@@ -757,13 +757,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
757757 /// *Unwind* to the given `target` basic block.
758758 /// Do *not* use for returning! Use `return_to_block` instead.
759759 ///
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 > {
763766 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+ }
766772 } ;
773+ Ok ( ( ) )
767774 }
768775
769776 /// 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> {
798805 throw_ub_format ! ( "unwinding past the topmost frame of the stack" ) ;
799806 }
800807
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" ) ;
822810
823811 if !unwinding {
824812 // 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> {
831819 }
832820 }
833821
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+
834834 if !cleanup {
835835 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!" ) ;
837836 assert ! ( !unwinding, "tried to skip cleanup during unwinding" ) ;
838837 // Leak the locals, skip validation, skip machine hook.
839838 return Ok ( ( ) ) ;
@@ -852,11 +851,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
852851 // Normal return, figure out where to jump.
853852 if unwinding {
854853 // 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) ?;
857861 } else {
858862 // Follow the normal return edge.
859- if let Some ( ret) = next_block {
863+ if let StackPopCleanup :: Goto { ret, .. } = return_to_block {
860864 self . return_to_block ( ret) ?;
861865 }
862866 }
0 commit comments