11use std:: borrow:: Cow ;
22
3+ use either:: Either ;
4+
35use rustc_middle:: {
46 mir,
57 ty:: {
@@ -29,28 +31,25 @@ pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> {
2931 Copy ( OpTy < ' tcx , Prov > ) ,
3032 /// Allow for the argument to be passed in-place: destroy the value originally stored at that place and
3133 /// make the place inaccessible for the duration of the function call.
32- InPlace ( PlaceTy < ' tcx , Prov > ) ,
34+ InPlace ( MPlaceTy < ' tcx , Prov > ) ,
3335}
3436
3537impl < ' tcx , Prov : Provenance > FnArg < ' tcx , Prov > {
3638 pub fn layout ( & self ) -> & TyAndLayout < ' tcx > {
3739 match self {
3840 FnArg :: Copy ( op) => & op. layout ,
39- FnArg :: InPlace ( place ) => & place . layout ,
41+ FnArg :: InPlace ( mplace ) => & mplace . layout ,
4042 }
4143 }
4244}
4345
4446impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
4547 /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the
4648 /// original memory occurs.
47- pub fn copy_fn_arg (
48- & self ,
49- arg : & FnArg < ' tcx , M :: Provenance > ,
50- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: Provenance > > {
49+ pub fn copy_fn_arg ( & self , arg : & FnArg < ' tcx , M :: Provenance > ) -> OpTy < ' tcx , M :: Provenance > {
5150 match arg {
52- FnArg :: Copy ( op) => Ok ( op. clone ( ) ) ,
53- FnArg :: InPlace ( place ) => self . place_to_op ( place ) ,
51+ FnArg :: Copy ( op) => op. clone ( ) ,
52+ FnArg :: InPlace ( mplace ) => mplace . clone ( ) . into ( ) ,
5453 }
5554 }
5655
@@ -59,7 +58,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
5958 pub fn copy_fn_args (
6059 & self ,
6160 args : & [ FnArg < ' tcx , M :: Provenance > ] ,
62- ) -> InterpResult < ' tcx , Vec < OpTy < ' tcx , M :: Provenance > > > {
61+ ) -> Vec < OpTy < ' tcx , M :: Provenance > > {
6362 args. iter ( ) . map ( |fn_arg| self . copy_fn_arg ( fn_arg) ) . collect ( )
6463 }
6564
@@ -70,7 +69,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
7069 ) -> InterpResult < ' tcx , FnArg < ' tcx , M :: Provenance > > {
7170 Ok ( match arg {
7271 FnArg :: Copy ( op) => FnArg :: Copy ( self . project_field ( op, field) ?) ,
73- FnArg :: InPlace ( place ) => FnArg :: InPlace ( self . project_field ( place , field) ?) ,
72+ FnArg :: InPlace ( mplace ) => FnArg :: InPlace ( self . project_field ( mplace , field) ?) ,
7473 } )
7574 }
7675
@@ -238,10 +237,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
238237 ) -> InterpResult < ' tcx , Vec < FnArg < ' tcx , M :: Provenance > > > {
239238 ops. iter ( )
240239 . map ( |op| {
241- Ok ( match & op. node {
242- mir:: Operand :: Move ( place) => FnArg :: InPlace ( self . eval_place ( * place) ?) ,
243- _ => FnArg :: Copy ( self . eval_operand ( & op. node , None ) ?) ,
244- } )
240+ let arg = match & op. node {
241+ mir:: Operand :: Copy ( _) | mir:: Operand :: Constant ( _) => {
242+ // Make a regular copy.
243+ let op = self . eval_operand ( & op. node , None ) ?;
244+ FnArg :: Copy ( op)
245+ }
246+ mir:: Operand :: Move ( place) => {
247+ // If this place lives in memory, preserve its location.
248+ // We call `place_to_op` which will be an `MPlaceTy` whenever there exists
249+ // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
250+ // which can return a local even if that has an mplace.)
251+ let place = self . eval_place ( * place) ?;
252+ let op = self . place_to_op ( & place) ?;
253+
254+ match op. as_mplace_or_imm ( ) {
255+ Either :: Left ( mplace) => FnArg :: InPlace ( mplace) ,
256+ Either :: Right ( _imm) => {
257+ // This argument doesn't live in memory, so there's no place
258+ // to make inaccessible during the call.
259+ // We rely on there not being any stray `PlaceTy` that would let the
260+ // caller directly access this local!
261+ // This is also crucial for tail calls, where we want the `FnArg` to
262+ // stay valid when the old stack frame gets popped.
263+ FnArg :: Copy ( op)
264+ }
265+ }
266+ }
267+ } ;
268+
269+ Ok ( arg)
245270 } )
246271 . collect ( )
247272 }
@@ -451,7 +476,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
451476 // We work with a copy of the argument for now; if this is in-place argument passing, we
452477 // will later protect the source it comes from. This means the callee cannot observe if we
453478 // did in-place of by-copy argument passing, except for pointer equality tests.
454- let caller_arg_copy = self . copy_fn_arg ( caller_arg) ? ;
479+ let caller_arg_copy = self . copy_fn_arg ( caller_arg) ;
455480 if !already_live {
456481 let local = callee_arg. as_local ( ) . unwrap ( ) ;
457482 let meta = caller_arg_copy. meta ( ) ;
@@ -469,8 +494,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
469494 // specifically.)
470495 self . copy_op_allow_transmute ( & caller_arg_copy, & callee_arg) ?;
471496 // If this was an in-place pass, protect the place it comes from for the duration of the call.
472- if let FnArg :: InPlace ( place ) = caller_arg {
473- M :: protect_in_place_function_argument ( self , place ) ?;
497+ if let FnArg :: InPlace ( mplace ) = caller_arg {
498+ M :: protect_in_place_function_argument ( self , mplace ) ?;
474499 }
475500 Ok ( ( ) )
476501 }
@@ -517,7 +542,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
517542 M :: call_intrinsic (
518543 self ,
519544 instance,
520- & self . copy_fn_args ( args) ? ,
545+ & self . copy_fn_args ( args) ,
521546 destination,
522547 target,
523548 unwind,
@@ -594,8 +619,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
594619 . map( |arg| (
595620 arg. layout( ) . ty,
596621 match arg {
597- FnArg :: Copy ( op) => format!( "copy({:?})" , * op ) ,
598- FnArg :: InPlace ( place ) => format!( "in-place({:?})" , * place ) ,
622+ FnArg :: Copy ( op) => format!( "copy({op :?})" ) ,
623+ FnArg :: InPlace ( mplace ) => format!( "in-place({mplace :?})" ) ,
599624 }
600625 ) )
601626 . collect:: <Vec <_>>( )
@@ -717,8 +742,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
717742 callee_ty: callee_fn_abi. ret. layout. ty
718743 } ) ;
719744 }
745+
720746 // Protect return place for in-place return value passing.
721- M :: protect_in_place_function_argument ( self , & destination. clone ( ) . into ( ) ) ?;
747+ M :: protect_in_place_function_argument ( self , & destination) ?;
722748
723749 // Don't forget to mark "initially live" locals as live.
724750 self . storage_live_for_always_live_locals ( ) ?;
@@ -741,7 +767,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
741767 // An `InPlace` does nothing here, we keep the original receiver intact. We can't
742768 // really pass the argument in-place anyway, and we are constructing a new
743769 // `Immediate` receiver.
744- let mut receiver = self . copy_fn_arg ( & args[ 0 ] ) ? ;
770+ let mut receiver = self . copy_fn_arg ( & args[ 0 ] ) ;
745771 let receiver_place = loop {
746772 match receiver. layout . ty . kind ( ) {
747773 ty:: Ref ( ..) | ty:: RawPtr ( ..) => {
0 commit comments