@@ -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 ) {
@@ -209,6 +218,60 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
209218 self . drop_ranges . add_control_edge ( self . expr_index + 1 , self . expr_index + 1 ) ;
210219 }
211220 }
221+
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+ } )
265+ }
266+ }
267+
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+ )
212275}
213276
214277impl < ' a , ' tcx > Visitor < ' tcx > for DropRangeVisitor < ' a , ' tcx > {
@@ -320,8 +383,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
320383 } ) ;
321384 }
322385
323- ExprKind :: Loop ( body, ..) => {
386+ ExprKind :: Loop ( body, label , ..) => {
324387 let loop_begin = self . expr_index + 1 ;
388+ self . label_stack . push ( ( label, loop_begin) ) ;
325389 if body. stmts . is_empty ( ) && body. expr . is_none ( ) {
326390 // For empty loops we won't have updated self.expr_index after visiting the
327391 // body, meaning we'd get an edge from expr_index to expr_index + 1, but
@@ -331,10 +395,31 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
331395 self . visit_block ( body) ;
332396 self . drop_ranges . add_control_edge ( self . expr_index , loop_begin) ;
333397 }
398+ self . label_stack . pop ( ) ;
334399 }
335- ExprKind :: Break ( hir:: Destination { target_id : Ok ( target) , .. } , ..)
336- | ExprKind :: Continue ( hir:: Destination { target_id : Ok ( target) , .. } , ..) => {
337- self . drop_ranges . add_control_edge_hir_id ( self . expr_index , 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+ } )
338423 }
339424
340425 ExprKind :: Call ( f, args) => {
@@ -359,11 +444,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
359444 | ExprKind :: Binary ( ..)
360445 | ExprKind :: Block ( ..)
361446 | ExprKind :: Box ( ..)
362- | ExprKind :: Break ( ..)
363447 | ExprKind :: Cast ( ..)
364448 | ExprKind :: Closure ( ..)
365449 | ExprKind :: ConstBlock ( ..)
366- | ExprKind :: Continue ( ..)
367450 | ExprKind :: DropTemps ( ..)
368451 | ExprKind :: Err
369452 | ExprKind :: Field ( ..)
@@ -462,11 +545,13 @@ impl DropRangesBuilder {
462545 /// Should be called after visiting the HIR but before solving the control flow, otherwise some
463546 /// edges will be missed.
464547 fn process_deferred_edges ( & mut self ) {
548+ trace ! ( "processing deferred edges. post_order_map={:#?}" , self . post_order_map) ;
465549 let mut edges = vec ! [ ] ;
466550 swap ( & mut edges, & mut self . deferred_edges ) ;
467551 edges. into_iter ( ) . for_each ( |( from, to) | {
468- let to = * self . post_order_map . get ( & to) . expect ( "Expression ID not found" ) ;
469552 trace ! ( "Adding deferred edge from {:?} to {:?}" , from, to) ;
553+ let to = * self . post_order_map . get ( & to) . expect ( "Expression ID not found" ) ;
554+ trace ! ( "target edge PostOrderId={:?}" , to) ;
470555 self . add_control_edge ( from, to)
471556 } ) ;
472557 }
0 commit comments