11use std:: borrow:: Cow ;
22
3+ use either:: Either ;
4+
35use rustc_ast:: ast:: InlineAsmOptions ;
46use rustc_middle:: {
57 mir,
@@ -30,28 +32,25 @@ pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> {
3032 Copy ( OpTy < ' tcx , Prov > ) ,
3133 /// Allow for the argument to be passed in-place: destroy the value originally stored at that place and
3234 /// make the place inaccessible for the duration of the function call.
33- InPlace ( PlaceTy < ' tcx , Prov > ) ,
35+ InPlace ( MPlaceTy < ' tcx , Prov > ) ,
3436}
3537
3638impl < ' tcx , Prov : Provenance > FnArg < ' tcx , Prov > {
3739 pub fn layout ( & self ) -> & TyAndLayout < ' tcx > {
3840 match self {
3941 FnArg :: Copy ( op) => & op. layout ,
40- FnArg :: InPlace ( place ) => & place . layout ,
42+ FnArg :: InPlace ( mplace ) => & mplace . layout ,
4143 }
4244 }
4345}
4446
4547impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
4648 /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the
4749 /// original memory occurs.
48- pub fn copy_fn_arg (
49- & self ,
50- arg : & FnArg < ' tcx , M :: Provenance > ,
51- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: Provenance > > {
50+ pub fn copy_fn_arg ( & self , arg : & FnArg < ' tcx , M :: Provenance > ) -> OpTy < ' tcx , M :: Provenance > {
5251 match arg {
53- FnArg :: Copy ( op) => Ok ( op. clone ( ) ) ,
54- FnArg :: InPlace ( place ) => self . place_to_op ( place ) ,
52+ FnArg :: Copy ( op) => op. clone ( ) ,
53+ FnArg :: InPlace ( mplace ) => mplace . clone ( ) . into ( ) ,
5554 }
5655 }
5756
@@ -60,7 +59,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6059 pub fn copy_fn_args (
6160 & self ,
6261 args : & [ FnArg < ' tcx , M :: Provenance > ] ,
63- ) -> InterpResult < ' tcx , Vec < OpTy < ' tcx , M :: Provenance > > > {
62+ ) -> Vec < OpTy < ' tcx , M :: Provenance > > {
6463 args. iter ( ) . map ( |fn_arg| self . copy_fn_arg ( fn_arg) ) . collect ( )
6564 }
6665
@@ -71,7 +70,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
7170 ) -> InterpResult < ' tcx , FnArg < ' tcx , M :: Provenance > > {
7271 Ok ( match arg {
7372 FnArg :: Copy ( op) => FnArg :: Copy ( self . project_field ( op, field) ?) ,
74- FnArg :: InPlace ( place ) => FnArg :: InPlace ( self . project_field ( place , field) ?) ,
73+ FnArg :: InPlace ( mplace ) => FnArg :: InPlace ( self . project_field ( mplace , field) ?) ,
7574 } )
7675 }
7776
@@ -246,10 +245,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
246245 ) -> InterpResult < ' tcx , Vec < FnArg < ' tcx , M :: Provenance > > > {
247246 ops. iter ( )
248247 . map ( |op| {
249- Ok ( match & op. node {
250- mir:: Operand :: Move ( place) => FnArg :: InPlace ( self . eval_place ( * place) ?) ,
251- _ => FnArg :: Copy ( self . eval_operand ( & op. node , None ) ?) ,
252- } )
248+ let arg = match & op. node {
249+ mir:: Operand :: Copy ( _) | mir:: Operand :: Constant ( _) => {
250+ // Make a regular copy.
251+ let op = self . eval_operand ( & op. node , None ) ?;
252+ FnArg :: Copy ( op)
253+ }
254+ mir:: Operand :: Move ( place) => {
255+ // If this place lives in memory, preserve its location.
256+ // We call `place_to_op` which will be an `MPlaceTy` whenever there exists
257+ // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
258+ // which can return a local even if that has an mplace.)
259+ let place = self . eval_place ( * place) ?;
260+ let op = self . place_to_op ( & place) ?;
261+
262+ match op. as_mplace_or_imm ( ) {
263+ Either :: Left ( mplace) => FnArg :: InPlace ( mplace) ,
264+ Either :: Right ( _imm) => {
265+ // This argument doesn't live in memory, so there's no place
266+ // to make inaccessible during the call.
267+ // We rely on there not being any stray `PlaceTy` that would let the
268+ // caller directly access this local!
269+ // This is also crucial for tail calls, where we want the `FnArg` to
270+ // stay valid when the old stack frame gets popped.
271+ FnArg :: Copy ( op)
272+ }
273+ }
274+ }
275+ } ;
276+
277+ Ok ( arg)
253278 } )
254279 . collect ( )
255280 }
@@ -459,7 +484,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
459484 // We work with a copy of the argument for now; if this is in-place argument passing, we
460485 // will later protect the source it comes from. This means the callee cannot observe if we
461486 // did in-place of by-copy argument passing, except for pointer equality tests.
462- let caller_arg_copy = self . copy_fn_arg ( caller_arg) ? ;
487+ let caller_arg_copy = self . copy_fn_arg ( caller_arg) ;
463488 if !already_live {
464489 let local = callee_arg. as_local ( ) . unwrap ( ) ;
465490 let meta = caller_arg_copy. meta ( ) ;
@@ -477,8 +502,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
477502 // specifically.)
478503 self . copy_op_allow_transmute ( & caller_arg_copy, & callee_arg) ?;
479504 // If this was an in-place pass, protect the place it comes from for the duration of the call.
480- if let FnArg :: InPlace ( place ) = caller_arg {
481- M :: protect_in_place_function_argument ( self , place ) ?;
505+ if let FnArg :: InPlace ( mplace ) = caller_arg {
506+ M :: protect_in_place_function_argument ( self , mplace ) ?;
482507 }
483508 Ok ( ( ) )
484509 }
@@ -525,7 +550,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
525550 M :: call_intrinsic (
526551 self ,
527552 instance,
528- & self . copy_fn_args ( args) ? ,
553+ & self . copy_fn_args ( args) ,
529554 destination,
530555 target,
531556 unwind,
@@ -602,8 +627,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
602627 . map( |arg| (
603628 arg. layout( ) . ty,
604629 match arg {
605- FnArg :: Copy ( op) => format!( "copy({:?})" , * op ) ,
606- FnArg :: InPlace ( place ) => format!( "in-place({:?})" , * place ) ,
630+ FnArg :: Copy ( op) => format!( "copy({op :?})" ) ,
631+ FnArg :: InPlace ( mplace ) => format!( "in-place({mplace :?})" ) ,
607632 }
608633 ) )
609634 . collect:: <Vec <_>>( )
@@ -725,8 +750,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
725750 callee_ty: callee_fn_abi. ret. layout. ty
726751 } ) ;
727752 }
728- // Protect return place for in-place return value passing.
729- M :: protect_in_place_function_argument ( self , destination) ?;
753+
754+ if let Either :: Left ( destination) =
755+ self . place_to_op ( & destination) ?. as_mplace_or_imm ( )
756+ {
757+ // Protect return place for in-place return value passing.
758+ M :: protect_in_place_function_argument ( self , & destination) ?;
759+ }
730760
731761 // Don't forget to mark "initially live" locals as live.
732762 self . storage_live_for_always_live_locals ( ) ?;
@@ -749,7 +779,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
749779 // An `InPlace` does nothing here, we keep the original receiver intact. We can't
750780 // really pass the argument in-place anyway, and we are constructing a new
751781 // `Immediate` receiver.
752- let mut receiver = self . copy_fn_arg ( & args[ 0 ] ) ? ;
782+ let mut receiver = self . copy_fn_arg ( & args[ 0 ] ) ;
753783 let receiver_place = loop {
754784 match receiver. layout . ty . kind ( ) {
755785 ty:: Ref ( ..) | ty:: RawPtr ( ..) => {
0 commit comments