@@ -4,7 +4,7 @@ use super::{
4
4
} ;
5
5
use hir:: {
6
6
intravisit:: { self , Visitor } ,
7
- Body , Expr , ExprKind , Guard , HirId ,
7
+ Body , Expr , ExprKind , Guard , HirId , LoopIdError ,
8
8
} ;
9
9
use rustc_data_structures:: fx:: FxHashMap ;
10
10
use rustc_hir as hir;
@@ -85,6 +85,7 @@ struct DropRangeVisitor<'a, 'tcx> {
85
85
expr_index : PostOrderId ,
86
86
tcx : TyCtxt < ' tcx > ,
87
87
typeck_results : & ' a TypeckResults < ' tcx > ,
88
+ label_stack : Vec < ( Option < rustc_ast:: Label > , PostOrderId ) > ,
88
89
}
89
90
90
91
impl < ' a , ' tcx > DropRangeVisitor < ' a , ' tcx > {
@@ -101,7 +102,15 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
101
102
hir,
102
103
num_exprs,
103
104
) ;
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
+ }
105
114
}
106
115
107
116
fn record_drop ( & mut self , value : TrackedValue ) {
@@ -210,46 +219,61 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
210
219
}
211
220
}
212
221
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
+ } )
250
265
}
251
266
}
252
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
+ )
275
+ }
276
+
253
277
impl < ' a , ' tcx > Visitor < ' tcx > for DropRangeVisitor < ' a , ' tcx > {
254
278
fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
255
279
let mut reinit = None ;
@@ -359,8 +383,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
359
383
} ) ;
360
384
}
361
385
362
- ExprKind :: Loop ( body, ..) => {
386
+ ExprKind :: Loop ( body, label , ..) => {
363
387
let loop_begin = self . expr_index + 1 ;
388
+ self . label_stack . push ( ( label, loop_begin) ) ;
364
389
if body. stmts . is_empty ( ) && body. expr . is_none ( ) {
365
390
// For empty loops we won't have updated self.expr_index after visiting the
366
391
// 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> {
370
395
self . visit_block ( body) ;
371
396
self . drop_ranges . add_control_edge ( self . expr_index , loop_begin) ;
372
397
}
398
+ self . label_stack . pop ( ) ;
373
399
}
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
+ } )
378
423
}
379
424
380
425
ExprKind :: Call ( f, args) => {
@@ -399,11 +444,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
399
444
| ExprKind :: Binary ( ..)
400
445
| ExprKind :: Block ( ..)
401
446
| ExprKind :: Box ( ..)
402
- | ExprKind :: Break ( ..)
403
447
| ExprKind :: Cast ( ..)
404
448
| ExprKind :: Closure ( ..)
405
449
| ExprKind :: ConstBlock ( ..)
406
- | ExprKind :: Continue ( ..)
407
450
| ExprKind :: DropTemps ( ..)
408
451
| ExprKind :: Err
409
452
| ExprKind :: Field ( ..)
0 commit comments