@@ -16,8 +16,8 @@ use tracing::{info, instrument, trace};
1616
1717use super :: {
1818 CtfeProvenance , FnVal , ImmTy , InterpCx , InterpResult , MPlaceTy , Machine , OpTy , PlaceTy ,
19- Projectable , Provenance , ReturnAction , ReturnContinuation , Scalar , StackPopInfo , interp_ok ,
20- throw_ub , throw_ub_custom, throw_unsup_format,
19+ Projectable , Provenance , ReturnAction , ReturnContinuation , Scalar , interp_ok , throw_ub ,
20+ throw_ub_custom, throw_unsup_format,
2121} ;
2222use crate :: interpret:: EnteredTraceSpan ;
2323use crate :: { enter_trace_span, fluent_generated as fluent} ;
@@ -40,25 +40,22 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> {
4040 FnArg :: InPlace ( mplace) => & mplace. layout ,
4141 }
4242 }
43- }
4443
45- impl < ' tcx , M : Machine < ' tcx > > InterpCx < ' tcx , M > {
4644 /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the
4745 /// original memory occurs.
48- pub fn copy_fn_arg ( & self , arg : & FnArg < ' tcx , M :: Provenance > ) -> OpTy < ' tcx , M :: Provenance > {
49- match arg {
46+ pub fn copy_fn_arg ( & self ) -> OpTy < ' tcx , Prov > {
47+ match self {
5048 FnArg :: Copy ( op) => op. clone ( ) ,
5149 FnArg :: InPlace ( mplace) => mplace. clone ( ) . into ( ) ,
5250 }
5351 }
52+ }
5453
54+ impl < ' tcx , M : Machine < ' tcx > > InterpCx < ' tcx , M > {
5555 /// Make a copy of the given fn_args. Any `InPlace` are degenerated to copies, no protection of the
5656 /// original memory occurs.
57- pub fn copy_fn_args (
58- & self ,
59- args : & [ FnArg < ' tcx , M :: Provenance > ] ,
60- ) -> Vec < OpTy < ' tcx , M :: Provenance > > {
61- args. iter ( ) . map ( |fn_arg| self . copy_fn_arg ( fn_arg) ) . collect ( )
57+ pub fn copy_fn_args ( args : & [ FnArg < ' tcx , M :: Provenance > ] ) -> Vec < OpTy < ' tcx , M :: Provenance > > {
58+ args. iter ( ) . map ( |fn_arg| fn_arg. copy_fn_arg ( ) ) . collect ( )
6259 }
6360
6461 /// Helper function for argument untupling.
@@ -310,7 +307,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
310307 // We work with a copy of the argument for now; if this is in-place argument passing, we
311308 // will later protect the source it comes from. This means the callee cannot observe if we
312309 // did in-place of by-copy argument passing, except for pointer equality tests.
313- let caller_arg_copy = self . copy_fn_arg ( caller_arg ) ;
310+ let caller_arg_copy = caller_arg . copy_fn_arg ( ) ;
314311 if !already_live {
315312 let local = callee_arg. as_local ( ) . unwrap ( ) ;
316313 let meta = caller_arg_copy. meta ( ) ;
@@ -559,7 +556,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
559556 if let Some ( fallback) = M :: call_intrinsic (
560557 self ,
561558 instance,
562- & self . copy_fn_args ( args) ,
559+ & Self :: copy_fn_args ( args) ,
563560 destination,
564561 target,
565562 unwind,
@@ -646,7 +643,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
646643 // An `InPlace` does nothing here, we keep the original receiver intact. We can't
647644 // really pass the argument in-place anyway, and we are constructing a new
648645 // `Immediate` receiver.
649- let mut receiver = self . copy_fn_arg ( & args[ 0 ] ) ;
646+ let mut receiver = args[ 0 ] . copy_fn_arg ( ) ;
650647 let receiver_place = loop {
651648 match receiver. layout . ty . kind ( ) {
652649 ty:: Ref ( ..) | ty:: RawPtr ( ..) => {
@@ -765,41 +762,50 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
765762 with_caller_location : bool ,
766763 ) -> InterpResult < ' tcx > {
767764 trace ! ( "init_fn_tail_call: {:#?}" , fn_val) ;
768-
769765 // This is the "canonical" implementation of tails calls,
770766 // a pop of the current stack frame, followed by a normal call
771767 // which pushes a new stack frame, with the return address from
772768 // the popped stack frame.
773769 //
774- // Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`,
775- // as the latter "executes" the goto to the return block, but we don't want to,
770+ // Note that we cannot use `return_from_current_stack_frame`,
771+ // as that "executes" the goto to the return block, but we don't want to,
776772 // only the tail called function should return to the current return block.
777- let StackPopInfo { return_action, return_cont, return_place } =
778- self . pop_stack_frame_raw ( false , |_this, _return_place| {
779- // This function's return value is just discarded, the tail-callee will fill in the return place instead.
780- interp_ok ( ( ) )
781- } ) ?;
782773
783- assert_eq ! ( return_action, ReturnAction :: Normal ) ;
784-
785- // Take the "stack pop cleanup" info, and use that to initiate the next call.
786- let ReturnContinuation :: Goto { ret, unwind } = return_cont else {
787- bug ! ( "can't tailcall as root" ) ;
774+ // The arguments need to all be copied since the current stack frame will be removed
775+ // before the callee even starts executing.
776+ // FIXME(explicit_tail_calls): does this match what codegen does?
777+ let args = args. iter ( ) . map ( |fn_arg| FnArg :: Copy ( fn_arg. copy_fn_arg ( ) ) ) . collect :: < Vec < _ > > ( ) ;
778+ // Remove the frame from the stack.
779+ let frame = self . pop_stack_frame_raw ( ) ?;
780+ // Remember where this frame would have returned to.
781+ let ReturnContinuation :: Goto { ret, unwind } = frame. return_cont ( ) else {
782+ bug ! ( "can't tailcall as root of the stack" ) ;
788783 } ;
789-
784+ // There's no return value to deal with! Instead, we forward the old return place
785+ // to the new function.
790786 // FIXME(explicit_tail_calls):
791787 // we should check if both caller&callee can/n't unwind,
792788 // see <https://github.com/rust-lang/rust/pull/113128#issuecomment-1614979803>
793789
790+ // Now push the new stack frame.
794791 self . init_fn_call (
795792 fn_val,
796793 ( caller_abi, caller_fn_abi) ,
797- args,
794+ & * args,
798795 with_caller_location,
799- & return_place,
796+ frame . return_place ( ) ,
800797 ret,
801798 unwind,
802- )
799+ ) ?;
800+
801+ // Finally, clear the local variables. Has to be done after pushing to support
802+ // non-scalar arguments.
803+ // FIXME(explicit_tail_calls): revisit this once codegen supports indirect arguments,
804+ // to ensure the semantics are compatible.
805+ let return_action = self . cleanup_stack_frame ( /* unwinding */ false , frame) ?;
806+ assert_eq ! ( return_action, ReturnAction :: Normal ) ;
807+
808+ interp_ok ( ( ) )
803809 }
804810
805811 pub ( super ) fn init_drop_in_place_call (
@@ -894,14 +900,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
894900 // local's value out.
895901 let return_op =
896902 self . local_to_op ( mir:: RETURN_PLACE , None ) . expect ( "return place should always be live" ) ;
897- // Do the actual pop + copy.
898- let stack_pop_info = self . pop_stack_frame_raw ( unwinding, |this, return_place| {
899- this. copy_op_allow_transmute ( & return_op, return_place) ?;
900- trace ! ( "return value: {:?}" , this. dump_place( return_place) ) ;
901- interp_ok ( ( ) )
902- } ) ?;
903-
904- match stack_pop_info. return_action {
903+ // Remove the frame from the stack.
904+ let frame = self . pop_stack_frame_raw ( ) ?;
905+ // Copy the return value and remember the return continuation.
906+ if !unwinding {
907+ self . copy_op_allow_transmute ( & return_op, frame. return_place ( ) ) ?;
908+ trace ! ( "return value: {:?}" , self . dump_place( frame. return_place( ) ) ) ;
909+ }
910+ let return_cont = frame. return_cont ( ) ;
911+ // Finish popping the stack frame.
912+ let return_action = self . cleanup_stack_frame ( unwinding, frame) ?;
913+ // Jump to the next block.
914+ match return_action {
905915 ReturnAction :: Normal => { }
906916 ReturnAction :: NoJump => {
907917 // The hook already did everything.
@@ -919,7 +929,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
919929 // Normal return, figure out where to jump.
920930 if unwinding {
921931 // Follow the unwind edge.
922- match stack_pop_info . return_cont {
932+ match return_cont {
923933 ReturnContinuation :: Goto { unwind, .. } => {
924934 // This must be the very last thing that happens, since it can in fact push a new stack frame.
925935 self . unwind_to_block ( unwind)
@@ -930,7 +940,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
930940 }
931941 } else {
932942 // Follow the normal return edge.
933- match stack_pop_info . return_cont {
943+ match return_cont {
934944 ReturnContinuation :: Goto { ret, .. } => self . return_to_block ( ret) ,
935945 ReturnContinuation :: Stop { .. } => {
936946 assert ! (
0 commit comments