@@ -159,6 +159,13 @@ pub enum StackPopCleanup {
159159 Root { cleanup : bool } ,
160160}
161161
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+
162169/// State of a local variable including a memoized layout
163170#[ derive( Clone ) ]
164171pub struct LocalState < ' tcx , Prov : Provenance = CtfeProvenance > {
@@ -803,14 +810,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
803810 return_to_block : StackPopCleanup ,
804811 ) -> InterpResult < ' tcx > {
805812 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 > {
806831 let dead_local = LocalState { value : LocalValue :: Dead , layout : Cell :: new ( None ) } ;
807832 let locals = IndexVec :: from_elem ( dead_local, & body. local_decls ) ;
808- // First push a stack frame so we have access to the local args
809833 let pre_frame = Frame {
810834 body,
811835 loc : Right ( body. span ) , // Span used for errors caused during preamble.
812836 return_to_block,
813- return_place : return_place . clone ( ) ,
837+ return_place,
814838 locals,
815839 instance,
816840 tracing_span : SpanGuard :: new ( ) ,
@@ -819,6 +843,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
819843 let frame = M :: init_frame ( self , pre_frame) ?;
820844 self . stack_mut ( ) . push ( frame) ;
821845
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 > {
822855 // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
823856 for & const_ in & body. required_consts {
824857 let c =
@@ -839,6 +872,59 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
839872 Ok ( ( ) )
840873 }
841874
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+
842928 /// Jump to the given block.
843929 #[ inline]
844930 pub fn go_to_block ( & mut self , target : mir:: BasicBlock ) {
@@ -886,7 +972,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
886972 }
887973
888974 /// 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 .
890976 ///
891977 /// If `unwinding` is `false`, then we are performing a normal return
892978 /// 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> {
899985 /// The cleanup block ends with a special `Resume` terminator, which will
900986 /// cause us to continue unwinding.
901987 #[ 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 > {
903992 info ! (
904993 "popping stack frame ({})" ,
905994 if unwinding { "during unwinding" } else { "returning from function" }
@@ -947,45 +1036,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
9471036 Ok ( ( ) )
9481037 } ;
9491038
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-
9661039 // 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+
9701042 // Report error from return value copy, if any.
9711043 copy_ret_result?;
9721044
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+ }
9831058 }
9841059
9851060 // Normal return, figure out where to jump.
9861061 if unwinding {
9871062 // Follow the unwind edge.
988- let unwind = match return_to_block {
1063+ let unwind = match frame . target {
9891064 StackPopCleanup :: Goto { unwind, .. } => unwind,
9901065 StackPopCleanup :: Root { .. } => {
9911066 panic ! ( "encountered StackPopCleanup::Root when unwinding!" )
@@ -995,7 +1070,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
9951070 self . unwind_to_block ( unwind)
9961071 } else {
9971072 // Follow the normal return edge.
998- match return_to_block {
1073+ match frame . target {
9991074 StackPopCleanup :: Goto { ret, .. } => self . return_to_block ( ret) ,
10001075 StackPopCleanup :: Root { .. } => {
10011076 assert ! (
0 commit comments