@@ -43,6 +43,41 @@ pub(super) fn build_control_flow_graph<'tcx>(
43
43
/// We are interested in points where a variables is dropped or initialized, and the control flow
44
44
/// of the code. We identify locations in code by their post-order traversal index, so it is
45
45
/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`.
46
+ ///
47
+ /// We make several simplifying assumptions, with the goal of being more conservative than
48
+ /// necessary rather than less conservative (since being less conservative is unsound, but more
49
+ /// conservative is still safe). These assumptions are:
50
+ ///
51
+ /// 1. Moving a variable `a` counts as a move of the whole variable.
52
+ /// 2. Moving a partial path like `a.b.c` is ignored.
53
+ /// 3. Reinitializing through a field (e.g. `a.b.c = 5`) counds as a reinitialization of all of
54
+ /// `a`.
55
+ ///
56
+ /// Some examples:
57
+ ///
58
+ /// Rule 1:
59
+ /// ```rust
60
+ /// let mut a = (vec![0], vec![0]);
61
+ /// drop(a);
62
+ /// // `a` is not considered initialized.
63
+ /// ```
64
+ ///
65
+ /// Rule 2:
66
+ /// ```rust
67
+ /// let mut a = (vec![0], vec![0]);
68
+ /// drop(a.0);
69
+ /// drop(a.1);
70
+ /// // `a` is still considered initialized.
71
+ /// ```
72
+ ///
73
+ /// Rule 3:
74
+ /// ```rust
75
+ /// let mut a = (vec![0], vec![0]);
76
+ /// drop(a);
77
+ /// a.1 = vec![1];
78
+ /// // all of `a` is considered initialized
79
+ /// ```
80
+
46
81
struct DropRangeVisitor < ' a , ' tcx > {
47
82
hir : Map < ' tcx > ,
48
83
places : ConsumedAndBorrowedPlaces ,
@@ -89,23 +124,76 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
89
124
. get ( & expr. hir_id )
90
125
. map_or ( vec ! [ ] , |places| places. iter ( ) . cloned ( ) . collect ( ) ) ;
91
126
for place in places {
92
- for_each_consumable ( place, self . hir . find ( place. hir_id ( ) ) , |value| {
93
- self . record_drop ( value)
94
- } ) ;
127
+ for_each_consumable ( self . hir , place, |value| self . record_drop ( value) ) ;
95
128
}
96
129
}
97
130
131
+ /// Marks an expression as being reinitialized.
132
+ ///
133
+ /// Note that we always approximated on the side of things being more
134
+ /// initialized than they actually are, as opposed to less. In cases such
135
+ /// as `x.y = ...`, we would consider all of `x` as being initialized
136
+ /// instead of just the `y` field.
137
+ ///
138
+ /// This is because it is always safe to consider something initialized
139
+ /// even when it is not, but the other way around will cause problems.
140
+ ///
141
+ /// In the future, we will hopefully tighten up these rules to be more
142
+ /// precise.
98
143
fn reinit_expr ( & mut self , expr : & hir:: Expr < ' _ > ) {
99
- if let ExprKind :: Path ( hir:: QPath :: Resolved (
100
- _,
101
- hir:: Path { res : hir:: def:: Res :: Local ( hir_id) , .. } ,
102
- ) ) = expr. kind
103
- {
104
- let location = self . expr_index ;
105
- debug ! ( "reinitializing {:?} at {:?}" , hir_id, location) ;
106
- self . drop_ranges . reinit_at ( TrackedValue :: Variable ( * hir_id) , location) ;
107
- } else {
108
- debug ! ( "reinitializing {:?} is not supported" , expr) ;
144
+ // Walk the expression to find the base. For example, in an expression
145
+ // like `*a[i].x`, we want to find the `a` and mark that as
146
+ // reinitialized.
147
+ match expr. kind {
148
+ ExprKind :: Path ( hir:: QPath :: Resolved (
149
+ _,
150
+ hir:: Path { res : hir:: def:: Res :: Local ( hir_id) , .. } ,
151
+ ) ) => {
152
+ // This is the base case, where we have found an actual named variable.
153
+
154
+ let location = self . expr_index ;
155
+ debug ! ( "reinitializing {:?} at {:?}" , hir_id, location) ;
156
+ self . drop_ranges . reinit_at ( TrackedValue :: Variable ( * hir_id) , location) ;
157
+ }
158
+
159
+ ExprKind :: Field ( base, _) => self . reinit_expr ( base) ,
160
+
161
+ // Most expressions do not refer to something where we need to track
162
+ // reinitializations.
163
+ //
164
+ // Some of these may be interesting in the future
165
+ ExprKind :: Path ( ..)
166
+ | ExprKind :: Box ( _)
167
+ | ExprKind :: ConstBlock ( _)
168
+ | ExprKind :: Array ( _)
169
+ | ExprKind :: Call ( _, _)
170
+ | ExprKind :: MethodCall ( _, _, _, _)
171
+ | ExprKind :: Tup ( _)
172
+ | ExprKind :: Binary ( _, _, _)
173
+ | ExprKind :: Unary ( _, _)
174
+ | ExprKind :: Lit ( _)
175
+ | ExprKind :: Cast ( _, _)
176
+ | ExprKind :: Type ( _, _)
177
+ | ExprKind :: DropTemps ( _)
178
+ | ExprKind :: Let ( _, _, _)
179
+ | ExprKind :: If ( _, _, _)
180
+ | ExprKind :: Loop ( _, _, _, _)
181
+ | ExprKind :: Match ( _, _, _)
182
+ | ExprKind :: Closure ( _, _, _, _, _)
183
+ | ExprKind :: Block ( _, _)
184
+ | ExprKind :: Assign ( _, _, _)
185
+ | ExprKind :: AssignOp ( _, _, _)
186
+ | ExprKind :: Index ( _, _)
187
+ | ExprKind :: AddrOf ( _, _, _)
188
+ | ExprKind :: Break ( _, _)
189
+ | ExprKind :: Continue ( _)
190
+ | ExprKind :: Ret ( _)
191
+ | ExprKind :: InlineAsm ( _)
192
+ | ExprKind :: LlvmInlineAsm ( _)
193
+ | ExprKind :: Struct ( _, _, _)
194
+ | ExprKind :: Repeat ( _, _)
195
+ | ExprKind :: Yield ( _, _)
196
+ | ExprKind :: Err => ( ) ,
109
197
}
110
198
}
111
199
@@ -158,13 +246,47 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
158
246
self . drop_ranges . add_control_edge ( true_end, self . expr_index + 1 ) ;
159
247
}
160
248
ExprKind :: Match ( scrutinee, arms, ..) => {
249
+ // We walk through the match expression almost like a chain of if expressions.
250
+ // Here's a diagram to follow along with:
251
+ //
252
+ // ┌─┐
253
+ // match │A│ {
254
+ // ┌───┴─┘
255
+ // │
256
+ // ┌▼┌───►┌─┐ ┌─┐
257
+ // │B│ if │C│ =>│D│,
258
+ // └─┘ ├─┴──►└─┴──────┐
259
+ // ┌──┘ │
260
+ // ┌──┘ │
261
+ // │ │
262
+ // ┌▼┌───►┌─┐ ┌─┐ │
263
+ // │E│ if │F│ =>│G│, │
264
+ // └─┘ ├─┴──►└─┴┐ │
265
+ // │ │ │
266
+ // } ▼ ▼ │
267
+ // ┌─┐◄───────────────────┘
268
+ // │H│
269
+ // └─┘
270
+ //
271
+ // The order we want is that the scrutinee (A) flows into the first pattern (B),
272
+ // which flows into the guard (C). Then the guard either flows into the arm body
273
+ // (D) or into the start of the next arm (E). Finally, the body flows to the end
274
+ // of the match block (H).
275
+ //
276
+ // The subsequent arms follow the same ordering. First we go to the pattern, then
277
+ // the guard (if present, otherwise it flows straight into the body), then into
278
+ // the body and then to the end of the match expression.
279
+ //
280
+ // The comments below show which edge is being added.
161
281
self . visit_expr ( scrutinee) ;
162
282
163
283
let ( guard_exit, arm_end_ids) = arms. iter ( ) . fold (
164
284
( self . expr_index , vec ! [ ] ) ,
165
285
|( incoming_edge, mut arm_end_ids) , hir:: Arm { pat, body, guard, .. } | {
286
+ // A -> B, or C -> E
166
287
self . drop_ranges . add_control_edge ( incoming_edge, self . expr_index + 1 ) ;
167
288
self . visit_pat ( pat) ;
289
+ // B -> C and E -> F are added implicitly due to the traversal order.
168
290
match guard {
169
291
Some ( Guard :: If ( expr) ) => self . visit_expr ( expr) ,
170
292
Some ( Guard :: IfLet ( pat, expr) ) => {
@@ -173,17 +295,34 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
173
295
}
174
296
None => ( ) ,
175
297
}
298
+ // Likewise, C -> D and F -> G are added implicitly.
299
+
300
+ // Save C, F, so we can add the other outgoing edge.
176
301
let to_next_arm = self . expr_index ;
302
+
177
303
// The default edge does not get added since we also have an explicit edge,
178
304
// so we also need to add an edge to the next node as well.
305
+ //
306
+ // This adds C -> D, F -> G
179
307
self . drop_ranges . add_control_edge ( self . expr_index , self . expr_index + 1 ) ;
180
308
self . visit_expr ( body) ;
309
+
310
+ // Save the end of the body so we can add the exit edge once we know where
311
+ // the exit is.
181
312
arm_end_ids. push ( self . expr_index ) ;
313
+
314
+ // Pass C to the next iteration, as well as vec![D]
315
+ //
316
+ // On the last round through, we pass F and vec![D, G] so that we can
317
+ // add all the exit edges.
182
318
( to_next_arm, arm_end_ids)
183
319
} ,
184
320
) ;
321
+ // F -> H
185
322
self . drop_ranges . add_control_edge ( guard_exit, self . expr_index + 1 ) ;
323
+
186
324
arm_end_ids. into_iter ( ) . for_each ( |arm_end| {
325
+ // D -> H, G -> H
187
326
self . drop_ranges . add_control_edge ( arm_end, self . expr_index + 1 )
188
327
} ) ;
189
328
}
@@ -275,7 +414,7 @@ impl DropRangesBuilder {
275
414
let mut tracked_value_map = FxHashMap :: < _ , TrackedValueIndex > :: default ( ) ;
276
415
let mut next = <_ >:: from ( 0u32 ) ;
277
416
for value in tracked_values {
278
- for_each_consumable ( value , hir . find ( value. hir_id ( ) ) , |value| {
417
+ for_each_consumable ( hir , value, |value| {
279
418
if !tracked_value_map. contains_key ( & value) {
280
419
tracked_value_map. insert ( value, next) ;
281
420
next = next + 1 ;
0 commit comments