@@ -48,6 +48,7 @@ struct LoopBlocks {
48
48
/// `None` for loops that are not terminating
49
49
end : Option < BasicBlockId > ,
50
50
place : Place ,
51
+ drop_scope_index : usize ,
51
52
}
52
53
53
54
#[ derive( Debug , Clone , Default ) ]
@@ -101,6 +102,35 @@ pub enum MirLowerError {
101
102
GenericArgNotProvided ( TypeOrConstParamId , Substitution ) ,
102
103
}
103
104
105
+ /// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
106
+ struct DropScopeToken ;
107
+ impl DropScopeToken {
108
+ fn pop_and_drop ( self , ctx : & mut MirLowerCtx < ' _ > , current : BasicBlockId ) -> BasicBlockId {
109
+ std:: mem:: forget ( self ) ;
110
+ ctx. pop_drop_scope_internal ( current)
111
+ }
112
+
113
+ /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
114
+ /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled
115
+ /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't
116
+ /// do anything)
117
+ fn pop_assume_dropped ( self , ctx : & mut MirLowerCtx < ' _ > ) {
118
+ std:: mem:: forget ( self ) ;
119
+ ctx. pop_drop_scope_assume_dropped_internal ( ) ;
120
+ }
121
+ }
122
+
123
+ // Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since
124
+ // in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be
125
+ // actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful
126
+ // stack trace.
127
+ //
128
+ // impl Drop for DropScopeToken {
129
+ // fn drop(&mut self) {
130
+ // never!("Drop scope doesn't popped");
131
+ // }
132
+ // }
133
+
104
134
impl MirLowerError {
105
135
pub fn pretty_print (
106
136
& self ,
@@ -506,7 +536,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
506
536
self . lower_loop ( current, place. clone ( ) , Some ( * label) , expr_id. into ( ) , |this, begin| {
507
537
if let Some ( current) = this. lower_block_to_place ( statements, begin, * tail, place, expr_id. into ( ) ) ? {
508
538
let end = this. current_loop_end ( ) ?;
509
- let current = this. pop_drop_scope ( current) ;
510
539
this. set_goto ( current, end, expr_id. into ( ) ) ;
511
540
}
512
541
Ok ( ( ) )
@@ -516,30 +545,39 @@ impl<'ctx> MirLowerCtx<'ctx> {
516
545
}
517
546
}
518
547
Expr :: Loop { body, label } => self . lower_loop ( current, place, * label, expr_id. into ( ) , |this, begin| {
519
- if let Some ( ( _, current) ) = this. lower_expr_as_place ( begin, * body, true ) ? {
520
- let current = this. pop_drop_scope ( current) ;
548
+ let scope = this. push_drop_scope ( ) ;
549
+ if let Some ( ( _, mut current) ) = this. lower_expr_as_place ( begin, * body, true ) ? {
550
+ current = scope. pop_and_drop ( this, current) ;
521
551
this. set_goto ( current, begin, expr_id. into ( ) ) ;
552
+ } else {
553
+ scope. pop_assume_dropped ( this) ;
522
554
}
523
555
Ok ( ( ) )
524
556
} ) ,
525
557
Expr :: While { condition, body, label } => {
526
558
self . lower_loop ( current, place, * label, expr_id. into ( ) , |this, begin| {
559
+ let scope = this. push_drop_scope ( ) ;
527
560
let Some ( ( discr, to_switch) ) = this. lower_expr_to_some_operand ( * condition, begin) ? else {
528
561
return Ok ( ( ) ) ;
529
562
} ;
530
- let end = this. current_loop_end ( ) ? ;
563
+ let fail_cond = this. new_basic_block ( ) ;
531
564
let after_cond = this. new_basic_block ( ) ;
532
565
this. set_terminator (
533
566
to_switch,
534
567
TerminatorKind :: SwitchInt {
535
568
discr,
536
- targets : SwitchTargets :: static_if ( 1 , after_cond, end ) ,
569
+ targets : SwitchTargets :: static_if ( 1 , after_cond, fail_cond ) ,
537
570
} ,
538
571
expr_id. into ( ) ,
539
572
) ;
573
+ let fail_cond = this. drop_until_scope ( this. drop_scopes . len ( ) - 1 , fail_cond) ;
574
+ let end = this. current_loop_end ( ) ?;
575
+ this. set_goto ( fail_cond, end, expr_id. into ( ) ) ;
540
576
if let Some ( ( _, block) ) = this. lower_expr_as_place ( after_cond, * body, true ) ? {
541
- let block = this . pop_drop_scope ( block) ;
577
+ let block = scope . pop_and_drop ( this , block) ;
542
578
this. set_goto ( block, begin, expr_id. into ( ) ) ;
579
+ } else {
580
+ scope. pop_assume_dropped ( this) ;
543
581
}
544
582
Ok ( ( ) )
545
583
} )
@@ -637,7 +675,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
637
675
Some ( l) => self . labeled_loop_blocks . get ( l) . ok_or ( MirLowerError :: UnresolvedLabel ) ?,
638
676
None => self . current_loop_blocks . as_ref ( ) . ok_or ( MirLowerError :: ContinueWithoutLoop ) ?,
639
677
} ;
640
- self . set_goto ( current, loop_data. begin , expr_id. into ( ) ) ;
678
+ let begin = loop_data. begin ;
679
+ current = self . drop_until_scope ( loop_data. drop_scope_index , current) ;
680
+ self . set_goto ( current, begin, expr_id. into ( ) ) ;
641
681
Ok ( None )
642
682
} ,
643
683
& Expr :: Break { expr, label } => {
@@ -651,10 +691,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
651
691
} ;
652
692
current = c;
653
693
}
654
- let end = match label {
655
- Some ( l) => self . labeled_loop_blocks . get ( & l) . ok_or ( MirLowerError :: UnresolvedLabel ) ?. end . expect ( "We always generate end for labeled loops" ) ,
656
- None => self . current_loop_end ( ) ?,
694
+ let ( end, drop_scope) = match label {
695
+ Some ( l) => {
696
+ let loop_blocks = self . labeled_loop_blocks . get ( & l) . ok_or ( MirLowerError :: UnresolvedLabel ) ?;
697
+ ( loop_blocks. end . expect ( "We always generate end for labeled loops" ) , loop_blocks. drop_scope_index )
698
+ } ,
699
+ None => {
700
+ ( self . current_loop_end ( ) ?, self . current_loop_blocks . as_ref ( ) . unwrap ( ) . drop_scope_index )
701
+ } ,
657
702
} ;
703
+ current = self . drop_until_scope ( drop_scope, current) ;
658
704
self . set_goto ( current, end, expr_id. into ( ) ) ;
659
705
Ok ( None )
660
706
}
@@ -1378,7 +1424,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
1378
1424
let begin = self . new_basic_block ( ) ;
1379
1425
let prev = mem:: replace (
1380
1426
& mut self . current_loop_blocks ,
1381
- Some ( LoopBlocks { begin, end : None , place } ) ,
1427
+ Some ( LoopBlocks { begin, end : None , place, drop_scope_index : self . drop_scopes . len ( ) } ) ,
1382
1428
) ;
1383
1429
let prev_label = if let Some ( label) = label {
1384
1430
// We should generate the end now, to make sure that it wouldn't change later. It is
@@ -1391,7 +1437,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
1391
1437
None
1392
1438
} ;
1393
1439
self . set_goto ( prev_block, begin, span) ;
1394
- self . push_drop_scope ( ) ;
1395
1440
f ( self , begin) ?;
1396
1441
let my = mem:: replace ( & mut self . current_loop_blocks , prev) . ok_or (
1397
1442
MirLowerError :: ImplementationError ( "current_loop_blocks is corrupt" . to_string ( ) ) ,
@@ -1489,6 +1534,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
1489
1534
place : Place ,
1490
1535
span : MirSpan ,
1491
1536
) -> Result < Option < Idx < BasicBlock > > > {
1537
+ let scope = self . push_drop_scope ( ) ;
1492
1538
for statement in statements. iter ( ) {
1493
1539
match statement {
1494
1540
hir_def:: hir:: Statement :: Let { pat, initializer, else_branch, type_ref : _ } => {
@@ -1497,6 +1543,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
1497
1543
let Some ( ( init_place, c) ) =
1498
1544
self . lower_expr_as_place ( current, * expr_id, true ) ?
1499
1545
else {
1546
+ scope. pop_assume_dropped ( self ) ;
1500
1547
return Ok ( None ) ;
1501
1548
} ;
1502
1549
current = c;
@@ -1528,18 +1575,25 @@ impl<'ctx> MirLowerCtx<'ctx> {
1528
1575
}
1529
1576
}
1530
1577
hir_def:: hir:: Statement :: Expr { expr, has_semi : _ } => {
1531
- self . push_drop_scope ( ) ;
1578
+ let scope2 = self . push_drop_scope ( ) ;
1532
1579
let Some ( ( _, c) ) = self . lower_expr_as_place ( current, * expr, true ) ? else {
1580
+ scope2. pop_assume_dropped ( self ) ;
1581
+ scope. pop_assume_dropped ( self ) ;
1533
1582
return Ok ( None ) ;
1534
1583
} ;
1535
- current = self . pop_drop_scope ( c) ;
1584
+ current = scope2 . pop_and_drop ( self , c) ;
1536
1585
}
1537
1586
}
1538
1587
}
1539
- match tail {
1540
- Some ( tail) => self . lower_expr_to_place ( tail, place, current) ,
1541
- None => Ok ( Some ( current) ) ,
1588
+ if let Some ( tail) = tail {
1589
+ let Some ( c) = self . lower_expr_to_place ( tail, place, current) ? else {
1590
+ scope. pop_assume_dropped ( self ) ;
1591
+ return Ok ( None ) ;
1592
+ } ;
1593
+ current = c;
1542
1594
}
1595
+ current = scope. pop_and_drop ( self , current) ;
1596
+ Ok ( Some ( current) )
1543
1597
}
1544
1598
1545
1599
fn lower_params_and_bindings (
@@ -1625,16 +1679,34 @@ impl<'ctx> MirLowerCtx<'ctx> {
1625
1679
current
1626
1680
}
1627
1681
1628
- fn push_drop_scope ( & mut self ) {
1682
+ fn push_drop_scope ( & mut self ) -> DropScopeToken {
1629
1683
self . drop_scopes . push ( DropScope :: default ( ) ) ;
1684
+ DropScopeToken
1685
+ }
1686
+
1687
+ /// Don't call directly
1688
+ fn pop_drop_scope_assume_dropped_internal ( & mut self ) {
1689
+ self . drop_scopes . pop ( ) ;
1630
1690
}
1631
1691
1632
- fn pop_drop_scope ( & mut self , mut current : BasicBlockId ) -> BasicBlockId {
1692
+ /// Don't call directly
1693
+ fn pop_drop_scope_internal ( & mut self , mut current : BasicBlockId ) -> BasicBlockId {
1633
1694
let scope = self . drop_scopes . pop ( ) . unwrap ( ) ;
1634
1695
self . emit_drop_and_storage_dead_for_scope ( & scope, & mut current) ;
1635
1696
current
1636
1697
}
1637
1698
1699
+ fn pop_drop_scope_assert_finished (
1700
+ & mut self ,
1701
+ mut current : BasicBlockId ,
1702
+ ) -> Result < BasicBlockId > {
1703
+ current = self . pop_drop_scope_internal ( current) ;
1704
+ if !self . drop_scopes . is_empty ( ) {
1705
+ implementation_error ! ( "Mismatched count between drop scope push and pops" ) ;
1706
+ }
1707
+ Ok ( current)
1708
+ }
1709
+
1638
1710
fn emit_drop_and_storage_dead_for_scope (
1639
1711
& mut self ,
1640
1712
scope : & DropScope ,
@@ -1728,7 +1800,7 @@ pub fn mir_body_for_closure_query(
1728
1800
|_| true ,
1729
1801
) ?;
1730
1802
if let Some ( current) = ctx. lower_expr_to_place ( * root, return_slot ( ) . into ( ) , current) ? {
1731
- let current = ctx. pop_drop_scope ( current) ;
1803
+ let current = ctx. pop_drop_scope_assert_finished ( current) ? ;
1732
1804
ctx. set_terminator ( current, TerminatorKind :: Return , ( * root) . into ( ) ) ;
1733
1805
}
1734
1806
let mut upvar_map: FxHashMap < LocalId , Vec < ( & CapturedItem , usize ) > > = FxHashMap :: default ( ) ;
@@ -1863,7 +1935,7 @@ pub fn lower_to_mir(
1863
1935
ctx. lower_params_and_bindings ( [ ] . into_iter ( ) , binding_picker) ?
1864
1936
} ;
1865
1937
if let Some ( current) = ctx. lower_expr_to_place ( root_expr, return_slot ( ) . into ( ) , current) ? {
1866
- let current = ctx. pop_drop_scope ( current) ;
1938
+ let current = ctx. pop_drop_scope_assert_finished ( current) ? ;
1867
1939
ctx. set_terminator ( current, TerminatorKind :: Return , root_expr. into ( ) ) ;
1868
1940
}
1869
1941
Ok ( ctx. result )
0 commit comments