@@ -134,14 +134,25 @@ pub struct FrameInfo<'tcx> {
134134 pub lint_root : Option < hir:: HirId > ,
135135}
136136
137- #[ derive( Clone , Eq , PartialEq , Debug , HashStable ) ] // Miri debug-prints these
137+ /// Unwind information.
138+ #[ derive( Clone , Copy , Eq , PartialEq , Debug , HashStable ) ]
139+ pub enum StackPopUnwind {
140+ /// The cleanup block.
141+ Cleanup ( mir:: BasicBlock ) ,
142+ /// No cleanup needs to be done.
143+ Skip ,
144+ /// Unwinding is not allowed (UB).
145+ NotAllowed ,
146+ }
147+
148+ #[ derive( Clone , Copy , Eq , PartialEq , Debug , HashStable ) ] // Miri debug-prints these
138149pub enum StackPopCleanup {
139150 /// Jump to the next block in the caller, or cause UB if None (that's a function
140151 /// that may never return). Also store layout of return place so
141152 /// we can validate it at that layout.
142153 /// `ret` stores the block we jump to on a normal return, while `unwind`
143154 /// stores the block used for cleanup during unwinding.
144- Goto { ret : Option < mir:: BasicBlock > , unwind : Option < mir :: BasicBlock > } ,
155+ Goto { ret : Option < mir:: BasicBlock > , unwind : StackPopUnwind } ,
145156 /// Just do nothing: Used by Main and for the `box_alloc` hook in miri.
146157 /// `cleanup` says whether locals are deallocated. Static computation
147158 /// wants them leaked to intern what they need (and just throw away
@@ -746,13 +757,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
746757 /// *Unwind* to the given `target` basic block.
747758 /// Do *not* use for returning! Use `return_to_block` instead.
748759 ///
749- /// If `target` is `None`, that indicates the function does not need cleanup during
750- /// unwinding, and we will just keep propagating that upwards.
751- 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 > {
752766 self . frame_mut ( ) . loc = match target {
753- Some ( block) => Ok ( mir:: Location { block, statement_index : 0 } ) ,
754- 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 stack frame that does not allow unwinding" )
771+ }
755772 } ;
773+ Ok ( ( ) )
756774 }
757775
758776 /// Pops the current frame from the stack, deallocating the
@@ -801,21 +819,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
801819 }
802820 }
803821
822+ let return_to_block = frame. return_to_block ;
823+
804824 // Now where do we jump next?
805825
806826 // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
807827 // In that case, we return early. We also avoid validation in that case,
808828 // because this is CTFE and the final value will be thoroughly validated anyway.
809- let ( cleanup, next_block) = match frame. return_to_block {
810- StackPopCleanup :: Goto { ret, unwind } => {
811- ( true , Some ( if unwinding { unwind } else { ret } ) )
812- }
813- StackPopCleanup :: None { cleanup, .. } => ( cleanup, None ) ,
829+ let cleanup = match return_to_block {
830+ StackPopCleanup :: Goto { .. } => true ,
831+ StackPopCleanup :: None { cleanup, .. } => cleanup,
814832 } ;
815833
816834 if !cleanup {
817835 assert ! ( self . stack( ) . is_empty( ) , "only the topmost frame should ever be leaked" ) ;
818- assert ! ( next_block. is_none( ) , "tried to skip cleanup when we have a next block!" ) ;
819836 assert ! ( !unwinding, "tried to skip cleanup during unwinding" ) ;
820837 // Leak the locals, skip validation, skip machine hook.
821838 return Ok ( ( ) ) ;
@@ -834,16 +851,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
834851 // Normal return, figure out where to jump.
835852 if unwinding {
836853 // Follow the unwind edge.
837- let unwind = next_block. expect ( "Encountered StackPopCleanup::None when unwinding!" ) ;
838- 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)
839861 } else {
840862 // Follow the normal return edge.
841- if let Some ( ret) = next_block {
842- self . return_to_block ( ret) ?;
863+ match return_to_block {
864+ StackPopCleanup :: Goto { ret, .. } => self . return_to_block ( ret) ,
865+ StackPopCleanup :: None { .. } => Ok ( ( ) ) ,
843866 }
844867 }
845-
846- Ok ( ( ) )
847868 }
848869
849870 /// Mark a storage as live, killing the previous content.
0 commit comments