@@ -43,6 +43,41 @@ pub(super) fn build_control_flow_graph<'tcx>(
4343/// We are interested in points where a variables is dropped or initialized, and the control flow
4444/// of the code. We identify locations in code by their post-order traversal index, so it is
4545/// 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+
4681struct DropRangeVisitor < ' a , ' tcx > {
4782 hir : Map < ' tcx > ,
4883 places : ConsumedAndBorrowedPlaces ,
@@ -89,23 +124,76 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
89124 . get ( & expr. hir_id )
90125 . map_or ( vec ! [ ] , |places| places. iter ( ) . cloned ( ) . collect ( ) ) ;
91126 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) ) ;
95128 }
96129 }
97130
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.
98143 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 => ( ) ,
109197 }
110198 }
111199
@@ -158,13 +246,47 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
158246 self . drop_ranges . add_control_edge ( true_end, self . expr_index + 1 ) ;
159247 }
160248 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.
161281 self . visit_expr ( scrutinee) ;
162282
163283 let ( guard_exit, arm_end_ids) = arms. iter ( ) . fold (
164284 ( self . expr_index , vec ! [ ] ) ,
165285 |( incoming_edge, mut arm_end_ids) , hir:: Arm { pat, body, guard, .. } | {
286+ // A -> B, or C -> E
166287 self . drop_ranges . add_control_edge ( incoming_edge, self . expr_index + 1 ) ;
167288 self . visit_pat ( pat) ;
289+ // B -> C and E -> F are added implicitly due to the traversal order.
168290 match guard {
169291 Some ( Guard :: If ( expr) ) => self . visit_expr ( expr) ,
170292 Some ( Guard :: IfLet ( pat, expr) ) => {
@@ -173,17 +295,34 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
173295 }
174296 None => ( ) ,
175297 }
298+ // Likewise, C -> D and F -> G are added implicitly.
299+
300+ // Save C, F, so we can add the other outgoing edge.
176301 let to_next_arm = self . expr_index ;
302+
177303 // The default edge does not get added since we also have an explicit edge,
178304 // so we also need to add an edge to the next node as well.
305+ //
306+ // This adds C -> D, F -> G
179307 self . drop_ranges . add_control_edge ( self . expr_index , self . expr_index + 1 ) ;
180308 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.
181312 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.
182318 ( to_next_arm, arm_end_ids)
183319 } ,
184320 ) ;
321+ // F -> H
185322 self . drop_ranges . add_control_edge ( guard_exit, self . expr_index + 1 ) ;
323+
186324 arm_end_ids. into_iter ( ) . for_each ( |arm_end| {
325+ // D -> H, G -> H
187326 self . drop_ranges . add_control_edge ( arm_end, self . expr_index + 1 )
188327 } ) ;
189328 }
@@ -275,7 +414,7 @@ impl DropRangesBuilder {
275414 let mut tracked_value_map = FxHashMap :: < _ , TrackedValueIndex > :: default ( ) ;
276415 let mut next = <_ >:: from ( 0u32 ) ;
277416 for value in tracked_values {
278- for_each_consumable ( value , hir . find ( value. hir_id ( ) ) , |value| {
417+ for_each_consumable ( hir , value, |value| {
279418 if !tracked_value_map. contains_key ( & value) {
280419 tracked_value_map. insert ( value, next) ;
281420 next = next + 1 ;
0 commit comments