4
4
5
5
use either:: Either ;
6
6
use rustc_abi:: { FIRST_VARIANT , FieldIdx } ;
7
+ use rustc_data_structures:: fx:: FxHashSet ;
7
8
use rustc_index:: IndexSlice ;
8
9
use rustc_middle:: ty:: { self , Instance , Ty } ;
9
10
use rustc_middle:: { bug, mir, span_bug} ;
@@ -389,8 +390,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
389
390
390
391
/// Evaluate the arguments of a function call
391
392
fn eval_fn_call_argument (
392
- & self ,
393
+ & mut self ,
393
394
op : & mir:: Operand < ' tcx > ,
395
+ move_definitely_disjoint : bool ,
394
396
) -> InterpResult < ' tcx , FnArg < ' tcx , M :: Provenance > > {
395
397
interp_ok ( match op {
396
398
mir:: Operand :: Copy ( _) | mir:: Operand :: Constant ( _) => {
@@ -399,24 +401,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
399
401
FnArg :: Copy ( op)
400
402
}
401
403
mir:: Operand :: Move ( place) => {
402
- // If this place lives in memory, preserve its location.
403
- // We call `place_to_op` which will be an `MPlaceTy` whenever there exists
404
- // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
405
- // which can return a local even if that has an mplace.)
406
404
let place = self . eval_place ( * place) ?;
407
- let op = self . place_to_op ( & place) ?;
408
-
409
- match op. as_mplace_or_imm ( ) {
410
- Either :: Left ( mplace) => FnArg :: InPlace ( mplace) ,
411
- Either :: Right ( _imm) => {
412
- // This argument doesn't live in memory, so there's no place
413
- // to make inaccessible during the call.
414
- // We rely on there not being any stray `PlaceTy` that would let the
415
- // caller directly access this local!
416
- // This is also crucial for tail calls, where we want the `FnArg` to
417
- // stay valid when the old stack frame gets popped.
418
- FnArg :: Copy ( op)
405
+ if move_definitely_disjoint {
406
+ // We still have to ensure that no *other* pointers are used to access this place,
407
+ // so *if* it is in memory then we have to treat it as `InPlace`.
408
+ // Use `place_to_op` to guarantee that we notice it being in memory.
409
+ let op = self . place_to_op ( & place) ?;
410
+ match op. as_mplace_or_imm ( ) {
411
+ Either :: Left ( mplace) => FnArg :: InPlace ( mplace) ,
412
+ Either :: Right ( _imm) => FnArg :: Copy ( op) ,
419
413
}
414
+ } else {
415
+ // We have to force this into memory to detect aliasing among `Move` arguments.
416
+ FnArg :: InPlace ( self . force_allocation ( & place) ?)
420
417
}
421
418
}
422
419
} )
@@ -425,18 +422,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
425
422
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
426
423
/// necessary information about callee and arguments to make a call.
427
424
fn eval_callee_and_args (
428
- & self ,
425
+ & mut self ,
429
426
terminator : & mir:: Terminator < ' tcx > ,
430
427
func : & mir:: Operand < ' tcx > ,
431
428
args : & [ Spanned < mir:: Operand < ' tcx > > ] ,
432
429
) -> InterpResult < ' tcx , EvaluatedCalleeAndArgs < ' tcx , M > > {
433
430
let func = self . eval_operand ( func, None ) ?;
431
+
432
+ // Evaluating function call arguments. The tricky part here is dealing with `Move`
433
+ // arguments: we have to ensure no two such arguments alias. This would be most easily done
434
+ // by just forcing them all into memory and then doing the usual in-place argument
435
+ // protection, but then we'd force *a lot* of arguments into memory. So we do some syntactic
436
+ // pre-processing here where if all `move` arguments are syntactically distinct local
437
+ // variables (and none is indirect), we can skip the in-memory forcing.
438
+ let move_definitely_disjoint = ' move_definitely_disjoint: {
439
+ let mut previous_locals = FxHashSet :: < mir:: Local > :: default ( ) ;
440
+ for arg in args {
441
+ let mir:: Operand :: Move ( place) = arg. node else {
442
+ continue ; // we can skip non-`Move` arguments.
443
+ } ;
444
+ if place. is_indirect_first_projection ( ) {
445
+ // An indirect `Move` argument could alias with anything else...
446
+ break ' move_definitely_disjoint false ;
447
+ }
448
+ if !previous_locals. insert ( place. local ) {
449
+ // This local is the base for two arguments! They might overlap.
450
+ break ' move_definitely_disjoint false ;
451
+ }
452
+ }
453
+ // We found no violation so they are all definitely disjoint.
454
+ true
455
+ } ;
434
456
let args = args
435
457
. iter ( )
436
- . map ( |arg| self . eval_fn_call_argument ( & arg. node ) )
458
+ . map ( |arg| self . eval_fn_call_argument ( & arg. node , move_definitely_disjoint ) )
437
459
. collect :: < InterpResult < ' tcx , Vec < _ > > > ( ) ?;
438
460
439
- let fn_sig_binder = func. layout . ty . fn_sig ( * self . tcx ) ;
461
+ let fn_sig_binder = {
462
+ let _trace = enter_trace_span ! ( M , "fn_sig" , ty = ?func. layout. ty. kind( ) ) ;
463
+ func. layout . ty . fn_sig ( * self . tcx )
464
+ } ;
440
465
let fn_sig = self . tcx . normalize_erasing_late_bound_regions ( self . typing_env , fn_sig_binder) ;
441
466
let extra_args = & args[ fn_sig. inputs ( ) . len ( ) ..] ;
442
467
let extra_args =
0 commit comments