@@ -48,6 +48,7 @@ struct LoopBlocks {
4848 /// `None` for loops that are not terminating
4949 end : Option < BasicBlockId > ,
5050 place : Place ,
51+ drop_scope_index : usize ,
5152}
5253
5354#[ derive( Debug , Clone , Default ) ]
@@ -101,6 +102,35 @@ pub enum MirLowerError {
101102 GenericArgNotProvided ( TypeOrConstParamId , Substitution ) ,
102103}
103104
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+
104134impl MirLowerError {
105135 pub fn pretty_print (
106136 & self ,
@@ -506,7 +536,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
506536 self . lower_loop ( current, place. clone ( ) , Some ( * label) , expr_id. into ( ) , |this, begin| {
507537 if let Some ( current) = this. lower_block_to_place ( statements, begin, * tail, place, expr_id. into ( ) ) ? {
508538 let end = this. current_loop_end ( ) ?;
509- let current = this. pop_drop_scope ( current) ;
510539 this. set_goto ( current, end, expr_id. into ( ) ) ;
511540 }
512541 Ok ( ( ) )
@@ -516,30 +545,39 @@ impl<'ctx> MirLowerCtx<'ctx> {
516545 }
517546 }
518547 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) ;
521551 this. set_goto ( current, begin, expr_id. into ( ) ) ;
552+ } else {
553+ scope. pop_assume_dropped ( this) ;
522554 }
523555 Ok ( ( ) )
524556 } ) ,
525557 Expr :: While { condition, body, label } => {
526558 self . lower_loop ( current, place, * label, expr_id. into ( ) , |this, begin| {
559+ let scope = this. push_drop_scope ( ) ;
527560 let Some ( ( discr, to_switch) ) = this. lower_expr_to_some_operand ( * condition, begin) ? else {
528561 return Ok ( ( ) ) ;
529562 } ;
530- let end = this. current_loop_end ( ) ? ;
563+ let fail_cond = this. new_basic_block ( ) ;
531564 let after_cond = this. new_basic_block ( ) ;
532565 this. set_terminator (
533566 to_switch,
534567 TerminatorKind :: SwitchInt {
535568 discr,
536- targets : SwitchTargets :: static_if ( 1 , after_cond, end ) ,
569+ targets : SwitchTargets :: static_if ( 1 , after_cond, fail_cond ) ,
537570 } ,
538571 expr_id. into ( ) ,
539572 ) ;
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 ( ) ) ;
540576 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) ;
542578 this. set_goto ( block, begin, expr_id. into ( ) ) ;
579+ } else {
580+ scope. pop_assume_dropped ( this) ;
543581 }
544582 Ok ( ( ) )
545583 } )
@@ -637,7 +675,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
637675 Some ( l) => self . labeled_loop_blocks . get ( l) . ok_or ( MirLowerError :: UnresolvedLabel ) ?,
638676 None => self . current_loop_blocks . as_ref ( ) . ok_or ( MirLowerError :: ContinueWithoutLoop ) ?,
639677 } ;
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 ( ) ) ;
641681 Ok ( None )
642682 } ,
643683 & Expr :: Break { expr, label } => {
@@ -651,10 +691,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
651691 } ;
652692 current = c;
653693 }
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+ } ,
657702 } ;
703+ current = self . drop_until_scope ( drop_scope, current) ;
658704 self . set_goto ( current, end, expr_id. into ( ) ) ;
659705 Ok ( None )
660706 }
@@ -1378,7 +1424,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
13781424 let begin = self . new_basic_block ( ) ;
13791425 let prev = mem:: replace (
13801426 & 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 ( ) } ) ,
13821428 ) ;
13831429 let prev_label = if let Some ( label) = label {
13841430 // 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> {
13911437 None
13921438 } ;
13931439 self . set_goto ( prev_block, begin, span) ;
1394- self . push_drop_scope ( ) ;
13951440 f ( self , begin) ?;
13961441 let my = mem:: replace ( & mut self . current_loop_blocks , prev) . ok_or (
13971442 MirLowerError :: ImplementationError ( "current_loop_blocks is corrupt" . to_string ( ) ) ,
@@ -1489,6 +1534,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
14891534 place : Place ,
14901535 span : MirSpan ,
14911536 ) -> Result < Option < Idx < BasicBlock > > > {
1537+ let scope = self . push_drop_scope ( ) ;
14921538 for statement in statements. iter ( ) {
14931539 match statement {
14941540 hir_def:: hir:: Statement :: Let { pat, initializer, else_branch, type_ref : _ } => {
@@ -1497,6 +1543,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
14971543 let Some ( ( init_place, c) ) =
14981544 self . lower_expr_as_place ( current, * expr_id, true ) ?
14991545 else {
1546+ scope. pop_assume_dropped ( self ) ;
15001547 return Ok ( None ) ;
15011548 } ;
15021549 current = c;
@@ -1528,18 +1575,25 @@ impl<'ctx> MirLowerCtx<'ctx> {
15281575 }
15291576 }
15301577 hir_def:: hir:: Statement :: Expr { expr, has_semi : _ } => {
1531- self . push_drop_scope ( ) ;
1578+ let scope2 = self . push_drop_scope ( ) ;
15321579 let Some ( ( _, c) ) = self . lower_expr_as_place ( current, * expr, true ) ? else {
1580+ scope2. pop_assume_dropped ( self ) ;
1581+ scope. pop_assume_dropped ( self ) ;
15331582 return Ok ( None ) ;
15341583 } ;
1535- current = self . pop_drop_scope ( c) ;
1584+ current = scope2 . pop_and_drop ( self , c) ;
15361585 }
15371586 }
15381587 }
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;
15421594 }
1595+ current = scope. pop_and_drop ( self , current) ;
1596+ Ok ( Some ( current) )
15431597 }
15441598
15451599 fn lower_params_and_bindings (
@@ -1625,16 +1679,34 @@ impl<'ctx> MirLowerCtx<'ctx> {
16251679 current
16261680 }
16271681
1628- fn push_drop_scope ( & mut self ) {
1682+ fn push_drop_scope ( & mut self ) -> DropScopeToken {
16291683 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 ( ) ;
16301690 }
16311691
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 {
16331694 let scope = self . drop_scopes . pop ( ) . unwrap ( ) ;
16341695 self . emit_drop_and_storage_dead_for_scope ( & scope, & mut current) ;
16351696 current
16361697 }
16371698
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+
16381710 fn emit_drop_and_storage_dead_for_scope (
16391711 & mut self ,
16401712 scope : & DropScope ,
@@ -1728,7 +1800,7 @@ pub fn mir_body_for_closure_query(
17281800 |_| true ,
17291801 ) ?;
17301802 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) ? ;
17321804 ctx. set_terminator ( current, TerminatorKind :: Return , ( * root) . into ( ) ) ;
17331805 }
17341806 let mut upvar_map: FxHashMap < LocalId , Vec < ( & CapturedItem , usize ) > > = FxHashMap :: default ( ) ;
@@ -1863,7 +1935,7 @@ pub fn lower_to_mir(
18631935 ctx. lower_params_and_bindings ( [ ] . into_iter ( ) , binding_picker) ?
18641936 } ;
18651937 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) ? ;
18671939 ctx. set_terminator ( current, TerminatorKind :: Return , root_expr. into ( ) ) ;
18681940 }
18691941 Ok ( ctx. result )
0 commit comments