@@ -134,14 +134,25 @@ pub struct FrameInfo<'tcx> {
134
134
pub lint_root : Option < hir:: HirId > ,
135
135
}
136
136
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
138
149
pub enum StackPopCleanup {
139
150
/// Jump to the next block in the caller, or cause UB if None (that's a function
140
151
/// that may never return). Also store layout of return place so
141
152
/// we can validate it at that layout.
142
153
/// `ret` stores the block we jump to on a normal return, while `unwind`
143
154
/// 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 } ,
145
156
/// Just do nothing: Used by Main and for the `box_alloc` hook in miri.
146
157
/// `cleanup` says whether locals are deallocated. Static computation
147
158
/// 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> {
746
757
/// *Unwind* to the given `target` basic block.
747
758
/// Do *not* use for returning! Use `return_to_block` instead.
748
759
///
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 > {
752
766
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
+ }
755
772
} ;
773
+ Ok ( ( ) )
756
774
}
757
775
758
776
/// 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> {
801
819
}
802
820
}
803
821
822
+ let return_to_block = frame. return_to_block ;
823
+
804
824
// Now where do we jump next?
805
825
806
826
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
807
827
// In that case, we return early. We also avoid validation in that case,
808
828
// 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,
814
832
} ;
815
833
816
834
if !cleanup {
817
835
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!" ) ;
819
836
assert ! ( !unwinding, "tried to skip cleanup during unwinding" ) ;
820
837
// Leak the locals, skip validation, skip machine hook.
821
838
return Ok ( ( ) ) ;
@@ -834,16 +851,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
834
851
// Normal return, figure out where to jump.
835
852
if unwinding {
836
853
// 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)
839
861
} else {
840
862
// 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 ( ( ) ) ,
843
866
}
844
867
}
845
-
846
- Ok ( ( ) )
847
868
}
848
869
849
870
/// Mark a storage as live, killing the previous content.
0 commit comments