@@ -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