@@ -757,13 +757,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
757
757
/// *Unwind* to the given `target` basic block.
758
758
/// Do *not* use for returning! Use `return_to_block` instead.
759
759
///
760
- /// If `target` is `None`, that indicates the function does not need cleanup during
761
- /// unwinding, and we will just keep propagating that upwards.
762
- pub fn unwind_to_block ( & mut self , target : Option < mir:: BasicBlock > ) {
760
+ /// If `target` is `StackPopUnwind::Skip`, that indicates the function does not need cleanup
761
+ /// during unwinding, and we will just keep propagating that upwards.
762
+ ///
763
+ /// If `target` is `StackPopUnwind::NotAllowed`, that indicates the function does not allow
764
+ /// unwinding, and doing so is UB.
765
+ pub fn unwind_to_block ( & mut self , target : StackPopUnwind ) -> InterpResult < ' tcx > {
763
766
self . frame_mut ( ) . loc = match target {
764
- Some ( block) => Ok ( mir:: Location { block, statement_index : 0 } ) ,
765
- None => Err ( self . frame_mut ( ) . body . span ) ,
767
+ StackPopUnwind :: Cleanup ( block) => Ok ( mir:: Location { block, statement_index : 0 } ) ,
768
+ StackPopUnwind :: Skip => Err ( self . frame_mut ( ) . body . span ) ,
769
+ StackPopUnwind :: NotAllowed => {
770
+ throw_ub_format ! ( "unwinding past a frame that does not allow unwinding" )
771
+ }
766
772
} ;
773
+ Ok ( ( ) )
767
774
}
768
775
769
776
/// Pops the current frame from the stack, deallocating the
@@ -798,27 +805,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
798
805
throw_ub_format ! ( "unwinding past the topmost frame of the stack" ) ;
799
806
}
800
807
801
- // Where do we jump next?
802
-
803
- // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
804
- // In that case, we return early. We also avoid validation in that case,
805
- // because this is CTFE and the final value will be thoroughly validated anyway.
806
- let ( cleanup, next_block) = match ( self . frame ( ) . return_to_block , unwinding) {
807
- ( StackPopCleanup :: Goto { ret, .. } , false ) => ( true , Some ( ret) ) ,
808
- ( StackPopCleanup :: Goto { unwind, .. } , true ) => (
809
- true ,
810
- Some ( match unwind {
811
- StackPopUnwind :: Cleanup ( unwind) => Some ( unwind) ,
812
- StackPopUnwind :: Skip => None ,
813
- StackPopUnwind :: NotAllowed => {
814
- throw_ub_format ! ( "unwinding past a frame that does not allow unwinding" )
815
- }
816
- } ) ,
817
- ) ,
818
- ( StackPopCleanup :: None { cleanup, .. } , _) => ( cleanup, None ) ,
819
- } ;
820
-
821
- let frame = self . stack_mut ( ) . pop ( ) . unwrap ( ) ;
808
+ let frame =
809
+ self . stack_mut ( ) . pop ( ) . expect ( "tried to pop a stack frame, but there were none" ) ;
822
810
823
811
if !unwinding {
824
812
// Copy the return value to the caller's stack frame.
@@ -831,9 +819,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
831
819
}
832
820
}
833
821
822
+ let return_to_block = frame. return_to_block ;
823
+
824
+ // Now where do we jump next?
825
+
826
+ // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
827
+ // In that case, we return early. We also avoid validation in that case,
828
+ // because this is CTFE and the final value will be thoroughly validated anyway.
829
+ let cleanup = match return_to_block {
830
+ StackPopCleanup :: Goto { .. } => true ,
831
+ StackPopCleanup :: None { cleanup, .. } => cleanup,
832
+ } ;
833
+
834
834
if !cleanup {
835
835
assert ! ( self . stack( ) . is_empty( ) , "only the topmost frame should ever be leaked" ) ;
836
- assert ! ( next_block. is_none( ) , "tried to skip cleanup when we have a next block!" ) ;
837
836
assert ! ( !unwinding, "tried to skip cleanup during unwinding" ) ;
838
837
// Leak the locals, skip validation, skip machine hook.
839
838
return Ok ( ( ) ) ;
@@ -852,11 +851,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
852
851
// Normal return, figure out where to jump.
853
852
if unwinding {
854
853
// Follow the unwind edge.
855
- let unwind = next_block. expect ( "Encountered StackPopCleanup::None when unwinding!" ) ;
856
- self . unwind_to_block ( unwind) ;
854
+ let unwind = match return_to_block {
855
+ StackPopCleanup :: Goto { unwind, .. } => unwind,
856
+ StackPopCleanup :: None { .. } => {
857
+ panic ! ( "Encountered StackPopCleanup::None when unwinding!" )
858
+ }
859
+ } ;
860
+ self . unwind_to_block ( unwind) ?;
857
861
} else {
858
862
// Follow the normal return edge.
859
- if let Some ( ret) = next_block {
863
+ if let StackPopCleanup :: Goto { ret, .. } = return_to_block {
860
864
self . return_to_block ( ret) ?;
861
865
}
862
866
}
0 commit comments