@@ -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 ) {
@@ -209,6 +218,60 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
209
218
self . drop_ranges . add_control_edge ( self . expr_index + 1 , self . expr_index + 1 ) ;
210
219
}
211
220
}
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
+ )
212
275
}
213
276
214
277
impl < ' a , ' tcx > Visitor < ' tcx > for DropRangeVisitor < ' a , ' tcx > {
@@ -320,8 +383,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
320
383
} ) ;
321
384
}
322
385
323
- ExprKind :: Loop ( body, ..) => {
386
+ ExprKind :: Loop ( body, label , ..) => {
324
387
let loop_begin = self . expr_index + 1 ;
388
+ self . label_stack . push ( ( label, loop_begin) ) ;
325
389
if body. stmts . is_empty ( ) && body. expr . is_none ( ) {
326
390
// For empty loops we won't have updated self.expr_index after visiting the
327
391
// 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> {
331
395
self . visit_block ( body) ;
332
396
self . drop_ranges . add_control_edge ( self . expr_index , loop_begin) ;
333
397
}
398
+ self . label_stack . pop ( ) ;
334
399
}
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
+ } )
338
423
}
339
424
340
425
ExprKind :: Call ( f, args) => {
@@ -359,11 +444,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
359
444
| ExprKind :: Binary ( ..)
360
445
| ExprKind :: Block ( ..)
361
446
| ExprKind :: Box ( ..)
362
- | ExprKind :: Break ( ..)
363
447
| ExprKind :: Cast ( ..)
364
448
| ExprKind :: Closure ( ..)
365
449
| ExprKind :: ConstBlock ( ..)
366
- | ExprKind :: Continue ( ..)
367
450
| ExprKind :: DropTemps ( ..)
368
451
| ExprKind :: Err
369
452
| ExprKind :: Field ( ..)
@@ -462,11 +545,13 @@ impl DropRangesBuilder {
462
545
/// Should be called after visiting the HIR but before solving the control flow, otherwise some
463
546
/// edges will be missed.
464
547
fn process_deferred_edges ( & mut self ) {
548
+ trace ! ( "processing deferred edges. post_order_map={:#?}" , self . post_order_map) ;
465
549
let mut edges = vec ! [ ] ;
466
550
swap ( & mut edges, & mut self . deferred_edges ) ;
467
551
edges. into_iter ( ) . for_each ( |( from, to) | {
468
- let to = * self . post_order_map . get ( & to) . expect ( "Expression ID not found" ) ;
469
552
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) ;
470
555
self . add_control_edge ( from, to)
471
556
} ) ;
472
557
}
0 commit comments