@@ -159,6 +159,13 @@ pub enum StackPopCleanup {
159
159
Root { cleanup : bool } ,
160
160
}
161
161
162
+ /// Return type of [`InterpCx::pop_stack_frame`].
163
+ pub struct StackPop < ' tcx , Prov : Provenance > {
164
+ pub jump : StackPopJump ,
165
+ pub target : StackPopCleanup ,
166
+ pub destination : MPlaceTy < ' tcx , Prov > ,
167
+ }
168
+
162
169
/// State of a local variable including a memoized layout
163
170
#[ derive( Clone ) ]
164
171
pub struct LocalState < ' tcx , Prov : Provenance = CtfeProvenance > {
@@ -803,14 +810,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
803
810
return_to_block : StackPopCleanup ,
804
811
) -> InterpResult < ' tcx > {
805
812
trace ! ( "body: {:#?}" , body) ;
813
+
814
+ // First push a stack frame so we have access to the local args
815
+ self . push_new_stack_frame ( instance, body, return_to_block, return_place. clone ( ) ) ?;
816
+
817
+ self . after_stack_frame_push ( instance, body) ?;
818
+
819
+ Ok ( ( ) )
820
+ }
821
+
822
+ /// Creates a new stack frame, initializes it and pushes it unto the stack.
823
+ /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame).
824
+ fn push_new_stack_frame (
825
+ & mut self ,
826
+ instance : ty:: Instance < ' tcx > ,
827
+ body : & ' tcx mir:: Body < ' tcx > ,
828
+ return_to_block : StackPopCleanup ,
829
+ return_place : MPlaceTy < ' tcx , M :: Provenance > ,
830
+ ) -> InterpResult < ' tcx > {
806
831
let dead_local = LocalState { value : LocalValue :: Dead , layout : Cell :: new ( None ) } ;
807
832
let locals = IndexVec :: from_elem ( dead_local, & body. local_decls ) ;
808
- // First push a stack frame so we have access to the local args
809
833
let pre_frame = Frame {
810
834
body,
811
835
loc : Right ( body. span ) , // Span used for errors caused during preamble.
812
836
return_to_block,
813
- return_place : return_place . clone ( ) ,
837
+ return_place,
814
838
locals,
815
839
instance,
816
840
tracing_span : SpanGuard :: new ( ) ,
@@ -819,6 +843,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
819
843
let frame = M :: init_frame ( self , pre_frame) ?;
820
844
self . stack_mut ( ) . push ( frame) ;
821
845
846
+ Ok ( ( ) )
847
+ }
848
+
849
+ /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame).
850
+ fn after_stack_frame_push (
851
+ & mut self ,
852
+ instance : ty:: Instance < ' tcx > ,
853
+ body : & ' tcx mir:: Body < ' tcx > ,
854
+ ) -> InterpResult < ' tcx > {
822
855
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
823
856
for & const_ in & body. required_consts {
824
857
let c =
@@ -839,6 +872,59 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
839
872
Ok ( ( ) )
840
873
}
841
874
875
+ /// Pops a stack frame from the stack and returns some information about it.
876
+ ///
877
+ /// This also deallocates locals, if necessary.
878
+ ///
879
+ /// [`M::before_stack_pop`] should be called before calling this function.
880
+ /// [`M::after_stack_pop`] is called by this function automatically.
881
+ ///
882
+ /// [`M::before_stack_pop`]: Machine::before_stack_pop
883
+ /// [`M::after_stack_pop`]: Machine::after_stack_pop
884
+ pub fn pop_stack_frame (
885
+ & mut self ,
886
+ unwinding : bool ,
887
+ ) -> InterpResult < ' tcx , StackPop < ' tcx , M :: Provenance > > {
888
+ let cleanup = self . cleanup_current_frame_locals ( ) ?;
889
+
890
+ let frame =
891
+ self . stack_mut ( ) . pop ( ) . expect ( "tried to pop a stack frame, but there were none" ) ;
892
+
893
+ let target = frame. return_to_block ;
894
+ let destination = frame. return_place . clone ( ) ;
895
+
896
+ let jump = if cleanup {
897
+ M :: after_stack_pop ( self , frame, unwinding) ?
898
+ } else {
899
+ StackPopJump :: NoCleanup
900
+ } ;
901
+
902
+ Ok ( StackPop { jump, target, destination } )
903
+ }
904
+
905
+ /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame).
906
+ /// Returns whatever the cleanup was done.
907
+ fn cleanup_current_frame_locals ( & mut self ) -> InterpResult < ' tcx , bool > {
908
+ // Cleanup: deallocate locals.
909
+ // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
910
+ // We do this while the frame is still on the stack, so errors point to the callee.
911
+ let return_to_block = self . frame ( ) . return_to_block ;
912
+ let cleanup = match return_to_block {
913
+ StackPopCleanup :: Goto { .. } => true ,
914
+ StackPopCleanup :: Root { cleanup, .. } => cleanup,
915
+ } ;
916
+
917
+ if cleanup {
918
+ // We need to take the locals out, since we need to mutate while iterating.
919
+ let locals = mem:: take ( & mut self . frame_mut ( ) . locals ) ;
920
+ for local in & locals {
921
+ self . deallocate_local ( local. value ) ?;
922
+ }
923
+ }
924
+
925
+ Ok ( cleanup)
926
+ }
927
+
842
928
/// Jump to the given block.
843
929
#[ inline]
844
930
pub fn go_to_block ( & mut self , target : mir:: BasicBlock ) {
@@ -886,7 +972,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
886
972
}
887
973
888
974
/// Pops the current frame from the stack, deallocating the
889
- /// memory for allocated locals.
975
+ /// memory for allocated locals, and jumps to an appropriate place .
890
976
///
891
977
/// If `unwinding` is `false`, then we are performing a normal return
892
978
/// from a function. In this case, we jump back into the frame of the caller,
@@ -899,7 +985,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
899
985
/// The cleanup block ends with a special `Resume` terminator, which will
900
986
/// cause us to continue unwinding.
901
987
#[ instrument( skip( self ) , level = "debug" ) ]
902
- pub ( super ) fn pop_stack_frame ( & mut self , unwinding : bool ) -> InterpResult < ' tcx > {
988
+ pub ( super ) fn return_from_current_stack_frame (
989
+ & mut self ,
990
+ unwinding : bool ,
991
+ ) -> InterpResult < ' tcx > {
903
992
info ! (
904
993
"popping stack frame ({})" ,
905
994
if unwinding { "during unwinding" } else { "returning from function" }
@@ -947,45 +1036,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
947
1036
Ok ( ( ) )
948
1037
} ;
949
1038
950
- // Cleanup: deallocate locals.
951
- // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
952
- // We do this while the frame is still on the stack, so errors point to the callee.
953
- let return_to_block = self . frame ( ) . return_to_block ;
954
- let cleanup = match return_to_block {
955
- StackPopCleanup :: Goto { .. } => true ,
956
- StackPopCleanup :: Root { cleanup, .. } => cleanup,
957
- } ;
958
- if cleanup {
959
- // We need to take the locals out, since we need to mutate while iterating.
960
- let locals = mem:: take ( & mut self . frame_mut ( ) . locals ) ;
961
- for local in & locals {
962
- self . deallocate_local ( local. value ) ?;
963
- }
964
- }
965
-
966
1039
// All right, now it is time to actually pop the frame.
967
- // Note that its locals are gone already, but that's fine.
968
- let frame =
969
- self . stack_mut ( ) . pop ( ) . expect ( "tried to pop a stack frame, but there were none" ) ;
1040
+ let frame = self . pop_stack_frame ( unwinding) ?;
1041
+
970
1042
// Report error from return value copy, if any.
971
1043
copy_ret_result?;
972
1044
973
- // If we are not doing cleanup, also skip everything else.
974
- if !cleanup {
975
- assert ! ( self . stack( ) . is_empty( ) , "only the topmost frame should ever be leaked" ) ;
976
- assert ! ( !unwinding, "tried to skip cleanup during unwinding" ) ;
977
- // Skip machine hook.
978
- return Ok ( ( ) ) ;
979
- }
980
- if M :: after_stack_pop ( self , frame, unwinding) ? == StackPopJump :: NoJump {
981
- // The hook already did everything.
982
- return Ok ( ( ) ) ;
1045
+ match frame. jump {
1046
+ StackPopJump :: Normal => { }
1047
+ StackPopJump :: NoJump => {
1048
+ // The hook already did everything.
1049
+ return Ok ( ( ) ) ;
1050
+ }
1051
+ StackPopJump :: NoCleanup => {
1052
+ // If we are not doing cleanup, also skip everything else.
1053
+ assert ! ( self . stack( ) . is_empty( ) , "only the topmost frame should ever be leaked" ) ;
1054
+ assert ! ( !unwinding, "tried to skip cleanup during unwinding" ) ;
1055
+ // Skip machine hook.
1056
+ return Ok ( ( ) ) ;
1057
+ }
983
1058
}
984
1059
985
1060
// Normal return, figure out where to jump.
986
1061
if unwinding {
987
1062
// Follow the unwind edge.
988
- let unwind = match return_to_block {
1063
+ let unwind = match frame . target {
989
1064
StackPopCleanup :: Goto { unwind, .. } => unwind,
990
1065
StackPopCleanup :: Root { .. } => {
991
1066
panic ! ( "encountered StackPopCleanup::Root when unwinding!" )
@@ -995,7 +1070,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
995
1070
self . unwind_to_block ( unwind)
996
1071
} else {
997
1072
// Follow the normal return edge.
998
- match return_to_block {
1073
+ match frame . target {
999
1074
StackPopCleanup :: Goto { ret, .. } => self . return_to_block ( ret) ,
1000
1075
StackPopCleanup :: Root { .. } => {
1001
1076
assert ! (
0 commit comments