@@ -4,7 +4,7 @@ use super::{
44} ; 
55use  hir:: { 
66    intravisit:: { self ,  Visitor } , 
7-     Body ,  Expr ,  ExprKind ,  Guard ,  HirId , 
7+     Body ,  Expr ,  ExprKind ,  Guard ,  HirId ,   LoopIdError , 
88} ; 
99use  rustc_data_structures:: fx:: FxHashMap ; 
1010use  rustc_hir as  hir; 
@@ -85,6 +85,7 @@ struct DropRangeVisitor<'a, 'tcx> {
8585    expr_index :  PostOrderId , 
8686    tcx :  TyCtxt < ' tcx > , 
8787    typeck_results :  & ' a  TypeckResults < ' tcx > , 
88+     label_stack :  Vec < ( Option < rustc_ast:: Label > ,  PostOrderId ) > , 
8889} 
8990
9091impl < ' a ,  ' tcx >  DropRangeVisitor < ' a ,  ' tcx >  { 
@@ -101,7 +102,15 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
101102            hir, 
102103            num_exprs, 
103104        ) ; 
104-         Self  {  hir,  places,  drop_ranges,  expr_index :  PostOrderId :: from_u32 ( 0 ) ,  typeck_results,  tcx } 
105+         Self  { 
106+             hir, 
107+             places, 
108+             drop_ranges, 
109+             expr_index :  PostOrderId :: from_u32 ( 0 ) , 
110+             typeck_results, 
111+             tcx, 
112+             label_stack :  vec ! [ ] , 
113+         } 
105114    } 
106115
107116    fn  record_drop ( & mut  self ,  value :  TrackedValue )  { 
@@ -210,46 +219,61 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
210219        } 
211220    } 
212221
213-     /// Break and continue expression targets might be another expression or a block, 
214- /// but this analysis only looks at expressions. In case a break has a block as a 
215- /// target, this will find the last expression in the block and return its HirId 
216- /// instead. 
217- fn  find_target_expression ( & self ,  hir_id :  HirId )  -> HirId  { 
218-         let  node = self . hir . get ( hir_id) ; 
219-         match  node { 
220-             hir:: Node :: Expr ( _)  => hir_id, 
221-             hir:: Node :: Block ( b)  => b. expr . map_or_else ( 
222-                 // If there is no tail expression, there will be at least one statement in the 
223-                 // block because the block contains a break or continue statement. 
224-                 || b. stmts . last ( ) . unwrap ( ) . hir_id , 
225-                 |expr| expr. hir_id , 
226-             ) , 
227-             hir:: Node :: Param ( ..) 
228-             | hir:: Node :: Item ( ..) 
229-             | hir:: Node :: ForeignItem ( ..) 
230-             | hir:: Node :: TraitItem ( ..) 
231-             | hir:: Node :: ImplItem ( ..) 
232-             | hir:: Node :: Variant ( ..) 
233-             | hir:: Node :: Field ( ..) 
234-             | hir:: Node :: AnonConst ( ..) 
235-             | hir:: Node :: Stmt ( ..) 
236-             | hir:: Node :: PathSegment ( ..) 
237-             | hir:: Node :: Ty ( ..) 
238-             | hir:: Node :: TraitRef ( ..) 
239-             | hir:: Node :: Binding ( ..) 
240-             | hir:: Node :: Pat ( ..) 
241-             | hir:: Node :: Arm ( ..) 
242-             | hir:: Node :: Local ( ..) 
243-             | hir:: Node :: Ctor ( ..) 
244-             | hir:: Node :: Lifetime ( ..) 
245-             | hir:: Node :: GenericParam ( ..) 
246-             | hir:: Node :: Visibility ( ..) 
247-             | hir:: Node :: Crate ( ..) 
248-             | hir:: Node :: Infer ( ..)  => bug ! ( "Unsupported branch target: {:?}" ,  node) , 
249-         } 
222+     /// Map a Destination to an equivalent expression node 
223+ /// 
224+ /// The destination field of a Break or Continue expression can target either an 
225+ /// expression or a block. The drop range analysis, however, only deals in 
226+ /// expression nodes, so blocks that might be the destination of a Break or Continue 
227+ /// will not have a PostOrderId. 
228+ /// 
229+ /// If the destination is an expression, this function will simply return that expression's 
230+ /// hir_id. If the destination is a block, this function will return the hir_id of last 
231+ /// expression in the block. 
232+ fn  find_target_expression_from_destination ( 
233+         & self , 
234+         destination :  hir:: Destination , 
235+     )  -> Result < HirId ,  LoopIdError >  { 
236+         destination. target_id . map ( |target| { 
237+             let  node = self . hir . get ( target) ; 
238+             match  node { 
239+                 hir:: Node :: Expr ( _)  => target, 
240+                 hir:: Node :: Block ( b)  => find_last_block_expression ( b) , 
241+                 hir:: Node :: Param ( ..) 
242+                 | hir:: Node :: Item ( ..) 
243+                 | hir:: Node :: ForeignItem ( ..) 
244+                 | hir:: Node :: TraitItem ( ..) 
245+                 | hir:: Node :: ImplItem ( ..) 
246+                 | hir:: Node :: Variant ( ..) 
247+                 | hir:: Node :: Field ( ..) 
248+                 | hir:: Node :: AnonConst ( ..) 
249+                 | hir:: Node :: Stmt ( ..) 
250+                 | hir:: Node :: PathSegment ( ..) 
251+                 | hir:: Node :: Ty ( ..) 
252+                 | hir:: Node :: TraitRef ( ..) 
253+                 | hir:: Node :: Binding ( ..) 
254+                 | hir:: Node :: Pat ( ..) 
255+                 | hir:: Node :: Arm ( ..) 
256+                 | hir:: Node :: Local ( ..) 
257+                 | hir:: Node :: Ctor ( ..) 
258+                 | hir:: Node :: Lifetime ( ..) 
259+                 | hir:: Node :: GenericParam ( ..) 
260+                 | hir:: Node :: Visibility ( ..) 
261+                 | hir:: Node :: Crate ( ..) 
262+                 | hir:: Node :: Infer ( ..)  => bug ! ( "Unsupported branch target: {:?}" ,  node) , 
263+             } 
264+         } ) 
250265    } 
251266} 
252267
268+ fn  find_last_block_expression ( block :  & hir:: Block < ' _ > )  -> HirId  { 
269+     block. expr . map_or_else ( 
270+         // If there is no tail expression, there will be at least one statement in the 
271+         // block because the block contains a break or continue statement. 
272+         || block. stmts . last ( ) . unwrap ( ) . hir_id , 
273+         |expr| expr. hir_id , 
274+     ) 
275+ } 
276+ 
253277impl < ' a ,  ' tcx >  Visitor < ' tcx >  for  DropRangeVisitor < ' a ,  ' tcx >  { 
254278    fn  visit_expr ( & mut  self ,  expr :  & ' tcx  Expr < ' tcx > )  { 
255279        let  mut  reinit = None ; 
@@ -359,8 +383,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
359383                } ) ; 
360384            } 
361385
362-             ExprKind :: Loop ( body,  ..)  => { 
386+             ExprKind :: Loop ( body,  label ,   ..)  => { 
363387                let  loop_begin = self . expr_index  + 1 ; 
388+                 self . label_stack . push ( ( label,  loop_begin) ) ; 
364389                if  body. stmts . is_empty ( )  && body. expr . is_none ( )  { 
365390                    // For empty loops we won't have updated self.expr_index after visiting the 
366391                    // body, meaning we'd get an edge from expr_index to expr_index + 1, but 
@@ -370,11 +395,31 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
370395                    self . visit_block ( body) ; 
371396                    self . drop_ranges . add_control_edge ( self . expr_index ,  loop_begin) ; 
372397                } 
398+                 self . label_stack . pop ( ) ; 
373399            } 
374-             ExprKind :: Break ( hir:: Destination  {  target_id :  Ok ( target) ,  .. } ,  ..) 
375-             | ExprKind :: Continue ( hir:: Destination  {  target_id :  Ok ( target) ,  .. } ,  ..)  => { 
376-                 self . drop_ranges 
377-                     . add_control_edge_hir_id ( self . expr_index ,  self . find_target_expression ( target) ) ; 
400+             // Find the loop entry by searching through the label stack for either the last entry 
401+             // (if label is none), or the first entry where the label matches this one. The Loop 
402+             // case maintains this stack mapping labels to the PostOrderId for the loop entry. 
403+             ExprKind :: Continue ( hir:: Destination  {  label,  .. } ,  ..)  => self 
404+                 . label_stack 
405+                 . iter ( ) 
406+                 . rev ( ) 
407+                 . find ( |( loop_label,  _) | label. is_none ( )  || * loop_label == label) 
408+                 . map_or ( ( ) ,  |( _,  target) | { 
409+                     self . drop_ranges . add_control_edge ( self . expr_index ,  * target) 
410+                 } ) , 
411+ 
412+             ExprKind :: Break ( destination,  ..)  => { 
413+                 // destination either points to an expression or to a block. We use 
414+                 // find_target_expression_from_destination to use the last expression of the block 
415+                 // if destination points to a block. 
416+                 // 
417+                 // We add an edge to the hir_id of the expression/block we are breaking out of, and 
418+                 // then in process_deferred_edges we will map this hir_id to its PostOrderId, which 
419+                 // will refer to the end of the block due to the post order traversal. 
420+                 self . find_target_expression_from_destination ( destination) . map_or ( ( ) ,  |target| { 
421+                     self . drop_ranges . add_control_edge_hir_id ( self . expr_index ,  target) 
422+                 } ) 
378423            } 
379424
380425            ExprKind :: Call ( f,  args)  => { 
@@ -399,11 +444,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
399444            | ExprKind :: Binary ( ..) 
400445            | ExprKind :: Block ( ..) 
401446            | ExprKind :: Box ( ..) 
402-             | ExprKind :: Break ( ..) 
403447            | ExprKind :: Cast ( ..) 
404448            | ExprKind :: Closure ( ..) 
405449            | ExprKind :: ConstBlock ( ..) 
406-             | ExprKind :: Continue ( ..) 
407450            | ExprKind :: DropTemps ( ..) 
408451            | ExprKind :: Err 
409452            | ExprKind :: Field ( ..) 
0 commit comments