@@ -7,6 +7,9 @@ use rustc_middle::mir::*;
7
7
use rustc_middle:: ty:: { self , subst:: SubstsRef , AdtDef , Ty } ;
8
8
use rustc_span:: DUMMY_SP ;
9
9
use rustc_trait_selection:: traits;
10
+ use rustc_index:: vec:: IndexVec ;
11
+ use rustc_index:: bit_set:: BitSet ;
12
+ use crate :: dataflow:: JoinSemiLattice ;
10
13
11
14
use super :: ConstCx ;
12
15
@@ -16,9 +19,9 @@ pub fn in_any_value_of_ty(
16
19
error_occured : Option < ErrorReported > ,
17
20
) -> ConstQualifs {
18
21
ConstQualifs {
19
- has_mut_interior : HasMutInterior :: in_any_value_of_ty ( cx, ty) ,
20
- needs_drop : NeedsDrop :: in_any_value_of_ty ( cx, ty) ,
21
- custom_eq : CustomEq :: in_any_value_of_ty ( cx, ty) ,
22
+ has_mut_interior : HasMutInterior :: in_any_value_of_ty ( cx, ty) . is_some ( ) ,
23
+ needs_drop : NeedsDrop :: in_any_value_of_ty ( cx, ty) . is_some ( ) ,
24
+ custom_eq : CustomEq :: in_any_value_of_ty ( cx, ty) . is_some ( ) ,
22
25
error_occured,
23
26
}
24
27
}
@@ -34,15 +37,21 @@ pub fn in_any_value_of_ty(
34
37
/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a
35
38
/// type-based one). Qualifications propagate structurally across variables: If a local (or a
36
39
/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed.
37
- pub trait Qualif {
40
+ pub ( crate ) trait Qualif {
38
41
/// The name of the file used to debug the dataflow analysis that computes this qualif.
39
42
const ANALYSIS_NAME : & ' static str ;
40
43
44
+ /// The dataflow result type. If it's just qualified/not qualified, then
45
+ /// you can just use a `()` (most qualifs do that). But if you need more state, use a
46
+ /// custom enum.
47
+ type Result : SetChoice = ( ) ;
48
+ type Set : QualifsPerLocal < Self :: Result > = <Self :: Result as SetChoice >:: Set ;
49
+
41
50
/// Whether this `Qualif` is cleared when a local is moved from.
42
51
const IS_CLEARED_ON_MOVE : bool = false ;
43
52
44
53
/// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`.
45
- fn in_qualifs ( qualifs : & ConstQualifs ) -> bool ;
54
+ fn in_qualifs ( qualifs : & ConstQualifs ) -> Option < Self :: Result > ;
46
55
47
56
/// Returns `true` if *any* value of the given type could possibly have this `Qualif`.
48
57
///
@@ -51,7 +60,7 @@ pub trait Qualif {
51
60
/// from a call to another function.
52
61
///
53
62
/// It also determines the `Qualif`s for primitive types.
54
- fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> bool ;
63
+ fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> Option < Self :: Result > ;
55
64
56
65
/// Returns `true` if this `Qualif` is inherent to the given struct or enum.
57
66
///
@@ -65,7 +74,61 @@ pub trait Qualif {
65
74
cx : & ConstCx < ' _ , ' tcx > ,
66
75
adt : & ' tcx AdtDef ,
67
76
substs : SubstsRef < ' tcx > ,
68
- ) -> bool ;
77
+ ) -> Option < Self :: Result > ;
78
+ }
79
+
80
+ pub ( crate ) trait SetChoice : Sized + Clone + JoinSemiLattice {
81
+ type Set : QualifsPerLocal < Self > = IndexVec < Local , Option < Self > > ;
82
+ }
83
+
84
+ impl SetChoice for ( ) {
85
+ type Set = BitSet < Local > ;
86
+ }
87
+
88
+ pub ( crate ) trait QualifsPerLocal < Value > : Sized + Clone + JoinSemiLattice {
89
+ fn new_empty ( n : usize ) -> Self ;
90
+ fn insert ( & mut self , local : Local , val : Value ) ;
91
+ fn remove ( & mut self , local : Local ) ;
92
+ fn clear ( & mut self ) ;
93
+ fn get ( & self , local : Local ) -> Option < Value > ;
94
+ }
95
+
96
+ impl QualifsPerLocal < ( ) > for BitSet < Local > {
97
+ fn new_empty ( n : usize ) -> Self {
98
+ BitSet :: new_empty ( n)
99
+ }
100
+ fn insert ( & mut self , local : Local , _: ( ) ) {
101
+ BitSet :: insert ( self , local) ;
102
+ }
103
+ fn remove ( & mut self , local : Local ) {
104
+ BitSet :: remove ( self , local) ;
105
+ }
106
+ fn clear ( & mut self ) {
107
+ BitSet :: clear ( self )
108
+ }
109
+ fn get ( & self , local : Local ) -> Option < ( ) > {
110
+ self . contains ( local) . then_some ( ( ) )
111
+ }
112
+ }
113
+
114
+ impl < T : Clone + Eq + JoinSemiLattice > QualifsPerLocal < T > for IndexVec < Local , Option < T > > {
115
+ fn new_empty ( n : usize ) -> Self {
116
+ IndexVec :: from_elem_n ( None , n)
117
+ }
118
+ fn insert ( & mut self , local : Local , val : T ) {
119
+ self [ local] = Some ( val) ;
120
+ }
121
+ fn remove ( & mut self , local : Local ) {
122
+ self [ local] = None ;
123
+ }
124
+ fn clear ( & mut self ) {
125
+ for elem in self . iter_mut ( ) {
126
+ * elem = None ;
127
+ }
128
+ }
129
+ fn get ( & self , local : Local ) -> Option < T > {
130
+ self [ local] . clone ( )
131
+ }
69
132
}
70
133
71
134
/// Constant containing interior mutability (`UnsafeCell<T>`).
@@ -78,18 +141,18 @@ pub struct HasMutInterior;
78
141
impl Qualif for HasMutInterior {
79
142
const ANALYSIS_NAME : & ' static str = "flow_has_mut_interior" ;
80
143
81
- fn in_qualifs ( qualifs : & ConstQualifs ) -> bool {
82
- qualifs. has_mut_interior
144
+ fn in_qualifs ( qualifs : & ConstQualifs ) -> Option < ( ) > {
145
+ qualifs. has_mut_interior . then_some ( ( ) )
83
146
}
84
147
85
- fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> bool {
86
- !ty. is_freeze ( cx. tcx . at ( DUMMY_SP ) , cx. param_env )
148
+ fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> Option < ( ) > {
149
+ ( !ty. is_freeze ( cx. tcx . at ( DUMMY_SP ) , cx. param_env ) ) . then_some ( ( ) )
87
150
}
88
151
89
- fn in_adt_inherently ( cx : & ConstCx < ' _ , ' tcx > , adt : & ' tcx AdtDef , _: SubstsRef < ' tcx > ) -> bool {
152
+ fn in_adt_inherently ( cx : & ConstCx < ' _ , ' tcx > , adt : & ' tcx AdtDef , _: SubstsRef < ' tcx > ) -> Option < ( ) > {
90
153
// Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
91
154
// It arises structurally for all other types.
92
- Some ( adt. did ) == cx. tcx . lang_items ( ) . unsafe_cell_type ( )
155
+ ( Some ( adt. did ) == cx. tcx . lang_items ( ) . unsafe_cell_type ( ) ) . then_some ( ( ) )
93
156
}
94
157
}
95
158
@@ -103,16 +166,16 @@ impl Qualif for NeedsDrop {
103
166
const ANALYSIS_NAME : & ' static str = "flow_needs_drop" ;
104
167
const IS_CLEARED_ON_MOVE : bool = true ;
105
168
106
- fn in_qualifs ( qualifs : & ConstQualifs ) -> bool {
107
- qualifs. needs_drop
169
+ fn in_qualifs ( qualifs : & ConstQualifs ) -> Option < ( ) > {
170
+ qualifs. needs_drop . then_some ( ( ) )
108
171
}
109
172
110
- fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> bool {
111
- ty. needs_drop ( cx. tcx , cx. param_env )
173
+ fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> Option < ( ) > {
174
+ ty. needs_drop ( cx. tcx , cx. param_env ) . then_some ( ( ) )
112
175
}
113
176
114
- fn in_adt_inherently ( cx : & ConstCx < ' _ , ' tcx > , adt : & ' tcx AdtDef , _: SubstsRef < ' tcx > ) -> bool {
115
- adt. has_dtor ( cx. tcx )
177
+ fn in_adt_inherently ( cx : & ConstCx < ' _ , ' tcx > , adt : & ' tcx AdtDef , _: SubstsRef < ' tcx > ) -> Option < ( ) > {
178
+ adt. has_dtor ( cx. tcx ) . then_some ( ( ) )
116
179
}
117
180
}
118
181
@@ -122,37 +185,37 @@ pub struct CustomEq;
122
185
impl Qualif for CustomEq {
123
186
const ANALYSIS_NAME : & ' static str = "flow_custom_eq" ;
124
187
125
- fn in_qualifs ( qualifs : & ConstQualifs ) -> bool {
126
- qualifs. custom_eq
188
+ fn in_qualifs ( qualifs : & ConstQualifs ) -> Option < ( ) > {
189
+ qualifs. custom_eq . then_some ( ( ) )
127
190
}
128
191
129
- fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> bool {
192
+ fn in_any_value_of_ty ( cx : & ConstCx < ' _ , ' tcx > , ty : Ty < ' tcx > ) -> Option < ( ) > {
130
193
// If *any* component of a composite data type does not implement `Structural{Partial,}Eq`,
131
194
// we know that at least some values of that type are not structural-match. I say "some"
132
195
// because that component may be part of an enum variant (e.g.,
133
196
// `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
134
197
// structural-match (`Option::None`).
135
198
let id = cx. tcx . hir ( ) . local_def_id_to_hir_id ( cx. def_id ( ) ) ;
136
- traits:: search_for_structural_match_violation ( id, cx. body . span , cx. tcx , ty) . is_some ( )
199
+ traits:: search_for_structural_match_violation ( id, cx. body . span , cx. tcx , ty) . map ( drop )
137
200
}
138
201
139
202
fn in_adt_inherently (
140
203
cx : & ConstCx < ' _ , ' tcx > ,
141
204
adt : & ' tcx AdtDef ,
142
205
substs : SubstsRef < ' tcx > ,
143
- ) -> bool {
206
+ ) -> Option < ( ) > {
144
207
let ty = cx. tcx . mk_ty ( ty:: Adt ( adt, substs) ) ;
145
- !ty. is_structural_eq_shallow ( cx. tcx )
208
+ ( !ty. is_structural_eq_shallow ( cx. tcx ) ) . then_some ( ( ) )
146
209
}
147
210
}
148
211
149
212
// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return.
150
213
151
214
/// Returns `true` if this `Rvalue` contains qualif `Q`.
152
- pub fn in_rvalue < Q , F > ( cx : & ConstCx < ' _ , ' tcx > , in_local : & mut F , rvalue : & Rvalue < ' tcx > ) -> bool
215
+ pub ( crate ) fn in_rvalue < Q , F > ( cx : & ConstCx < ' _ , ' tcx > , in_local : & mut F , rvalue : & Rvalue < ' tcx > ) -> Option < Q :: Result >
153
216
where
154
217
Q : Qualif ,
155
- F : FnMut ( Local ) -> bool ,
218
+ F : FnMut ( Local ) -> Option < Q :: Result > ,
156
219
{
157
220
match rvalue {
158
221
Rvalue :: ThreadLocalRef ( _) | Rvalue :: NullaryOp ( ..) => {
@@ -169,7 +232,9 @@ where
169
232
| Rvalue :: Cast ( _, operand, _) => in_operand :: < Q , _ > ( cx, in_local, operand) ,
170
233
171
234
Rvalue :: BinaryOp ( _, lhs, rhs) | Rvalue :: CheckedBinaryOp ( _, lhs, rhs) => {
172
- in_operand :: < Q , _ > ( cx, in_local, lhs) || in_operand :: < Q , _ > ( cx, in_local, rhs)
235
+ let mut res = in_operand :: < Q , _ > ( cx, in_local, lhs) ;
236
+ res. join ( & in_operand :: < Q , _ > ( cx, in_local, rhs) ) ;
237
+ res
173
238
}
174
239
175
240
Rvalue :: Ref ( _, _, place) | Rvalue :: AddressOf ( _, place) => {
@@ -189,57 +254,61 @@ where
189
254
}
190
255
191
256
Rvalue :: Aggregate ( kind, operands) => {
192
- // Return early if we know that the struct or enum being constructed is always
193
- // qualified.
257
+ // Check if we know that the struct or enum being constructed is always qualified.
258
+ let mut result = None ;
194
259
if let AggregateKind :: Adt ( def, _, substs, ..) = * * kind {
195
- if Q :: in_adt_inherently ( cx, def, substs) {
196
- return true ;
197
- }
260
+ result. join ( & Q :: in_adt_inherently ( cx, def, substs) ) ;
198
261
}
199
262
200
263
// Otherwise, proceed structurally...
201
- operands. iter ( ) . any ( |o| in_operand :: < Q , _ > ( cx, in_local, o) )
264
+ for o in operands {
265
+ result. join ( & in_operand :: < Q , _ > ( cx, in_local, o) ) ;
266
+ }
267
+ result
202
268
}
203
269
}
204
270
}
205
271
206
272
/// Returns `true` if this `Place` contains qualif `Q`.
207
- pub fn in_place < Q , F > ( cx : & ConstCx < ' _ , ' tcx > , in_local : & mut F , place : PlaceRef < ' tcx > ) -> bool
273
+ pub ( crate ) fn in_place < Q , F > ( cx : & ConstCx < ' _ , ' tcx > , in_local : & mut F , place : PlaceRef < ' tcx > ) -> Option < Q :: Result >
208
274
where
209
275
Q : Qualif ,
210
- F : FnMut ( Local ) -> bool ,
276
+ F : FnMut ( Local ) -> Option < Q :: Result > ,
211
277
{
212
278
let mut projection = place. projection ;
279
+ let mut result = None ;
213
280
while let & [ ref proj_base @ .., proj_elem] = projection {
214
281
match proj_elem {
215
- ProjectionElem :: Index ( index) if in_local ( index) => return true ,
282
+ ProjectionElem :: Index ( index) => {
283
+ result. join ( & in_local ( index) ) ;
284
+ } ,
216
285
217
286
ProjectionElem :: Deref
218
287
| ProjectionElem :: Field ( _, _)
219
288
| ProjectionElem :: ConstantIndex { .. }
220
289
| ProjectionElem :: Subslice { .. }
221
- | ProjectionElem :: Downcast ( _, _)
222
- | ProjectionElem :: Index ( _) => { }
290
+ | ProjectionElem :: Downcast ( _, _) => { }
223
291
}
224
292
225
293
let base_ty = Place :: ty_from ( place. local , proj_base, cx. body , cx. tcx ) ;
226
294
let proj_ty = base_ty. projection_ty ( cx. tcx , proj_elem) . ty ;
227
- if ! Q :: in_any_value_of_ty ( cx, proj_ty) {
228
- return false ;
295
+ if Q :: in_any_value_of_ty ( cx, proj_ty) . is_none ( ) {
296
+ return result ;
229
297
}
230
298
231
299
projection = proj_base;
232
300
}
233
301
234
302
assert ! ( projection. is_empty( ) ) ;
235
- in_local ( place. local )
303
+ result. join ( & in_local ( place. local ) ) ;
304
+ result
236
305
}
237
306
238
307
/// Returns `true` if this `Operand` contains qualif `Q`.
239
- pub fn in_operand < Q , F > ( cx : & ConstCx < ' _ , ' tcx > , in_local : & mut F , operand : & Operand < ' tcx > ) -> bool
308
+ pub ( crate ) fn in_operand < Q , F > ( cx : & ConstCx < ' _ , ' tcx > , in_local : & mut F , operand : & Operand < ' tcx > ) -> Option < Q :: Result >
240
309
where
241
310
Q : Qualif ,
242
- F : FnMut ( Local ) -> bool ,
311
+ F : FnMut ( Local ) -> Option < Q :: Result > ,
243
312
{
244
313
let constant = match operand {
245
314
Operand :: Copy ( place) | Operand :: Move ( place) => {
@@ -260,9 +329,10 @@ where
260
329
cx. tcx . at ( constant. span ) . mir_const_qualif ( def. did )
261
330
} ;
262
331
263
- if !Q :: in_qualifs ( & qualifs) {
264
- return false ;
265
- }
332
+ // Since this comes from a constant's qualifs, there can only
333
+ // be `Option<()>` style qualifs, so we are allowed to early
334
+ // return here and not try to join the results.
335
+ Q :: in_qualifs ( & qualifs) ?;
266
336
267
337
// Just in case the type is more specific than
268
338
// the definition, e.g., impl associated const
0 commit comments