re-do argument passing one more time to finally be sane
This commit is contained in:
parent
b8a40aad1b
commit
ec056d5188
5 changed files with 189 additions and 193 deletions
|
|
@ -512,6 +512,7 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
|
|||
mem::discriminant(&self).hash_stable(hcx, hasher);
|
||||
|
||||
match *self {
|
||||
FunctionArgCountMismatch |
|
||||
DanglingPointerDeref |
|
||||
DoubleFree |
|
||||
InvalidMemoryAccess |
|
||||
|
|
@ -558,7 +559,11 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
|
|||
},
|
||||
ReferencedConstant(ref err) => err.hash_stable(hcx, hasher),
|
||||
MachineError(ref err) => err.hash_stable(hcx, hasher),
|
||||
FunctionPointerTyMismatch(a, b) => {
|
||||
FunctionAbiMismatch(a, b) => {
|
||||
a.hash_stable(hcx, hasher);
|
||||
b.hash_stable(hcx, hasher)
|
||||
},
|
||||
FunctionArgMismatch(a, b) => {
|
||||
a.hash_stable(hcx, hasher);
|
||||
b.hash_stable(hcx, hasher)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@
|
|||
use std::{fmt, env};
|
||||
|
||||
use mir;
|
||||
use ty::{FnSig, Ty, layout};
|
||||
use ty::{Ty, layout};
|
||||
use ty::layout::{Size, Align};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::{
|
||||
Pointer, Lock, AccessKind
|
||||
|
|
@ -182,7 +183,10 @@ pub enum EvalErrorKind<'tcx, O> {
|
|||
/// This variant is used by machines to signal their own errors that do not
|
||||
/// match an existing variant
|
||||
MachineError(String),
|
||||
FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>),
|
||||
|
||||
FunctionAbiMismatch(Abi, Abi),
|
||||
FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
|
||||
FunctionArgCountMismatch,
|
||||
NoMirFor(String),
|
||||
UnterminatedCString(Pointer),
|
||||
DanglingPointerDeref,
|
||||
|
|
@ -290,8 +294,8 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
|
|||
use self::EvalErrorKind::*;
|
||||
match *self {
|
||||
MachineError(ref inner) => inner,
|
||||
FunctionPointerTyMismatch(..) =>
|
||||
"tried to call a function through a function pointer of a different type",
|
||||
FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionArgCountMismatch =>
|
||||
"tried to call a function through a function pointer of incompatible type",
|
||||
InvalidMemoryAccess =>
|
||||
"tried to access memory through an invalid pointer",
|
||||
DanglingPointerDeref =>
|
||||
|
|
@ -459,9 +463,15 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
|
|||
write!(f, "type validation failed: {}", err)
|
||||
}
|
||||
NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
|
||||
FunctionPointerTyMismatch(sig, got) =>
|
||||
write!(f, "tried to call a function with sig {} through a \
|
||||
function pointer of type {}", sig, got),
|
||||
FunctionAbiMismatch(caller_abi, callee_abi) =>
|
||||
write!(f, "tried to call a function with ABI {:?} using caller ABI {:?}",
|
||||
callee_abi, caller_abi),
|
||||
FunctionArgMismatch(caller_ty, callee_ty) =>
|
||||
write!(f, "tried to call a function with argument of type {:?} \
|
||||
passing data of type {:?}",
|
||||
callee_ty, caller_ty),
|
||||
FunctionArgCountMismatch =>
|
||||
write!(f, "tried to call a function with incorrect number of arguments"),
|
||||
BoundsCheck { ref len, ref index } =>
|
||||
write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index),
|
||||
ReallocatedWrongMemoryKind(ref old, ref new) =>
|
||||
|
|
|
|||
|
|
@ -487,10 +487,12 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> {
|
|||
use ::mir::interpret::EvalErrorKind::*;
|
||||
Some(match *self {
|
||||
MachineError(ref err) => MachineError(err.clone()),
|
||||
FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch(
|
||||
FunctionAbiMismatch(a, b) => FunctionAbiMismatch(a, b),
|
||||
FunctionArgMismatch(a, b) => FunctionArgMismatch(
|
||||
tcx.lift(&a)?,
|
||||
tcx.lift(&b)?,
|
||||
),
|
||||
FunctionArgCountMismatch => FunctionArgCountMismatch,
|
||||
NoMirFor(ref s) => NoMirFor(s.clone()),
|
||||
UnterminatedCString(ptr) => UnterminatedCString(ptr),
|
||||
DanglingPointerDeref => DanglingPointerDeref,
|
||||
|
|
|
|||
|
|
@ -10,13 +10,12 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use rustc::{mir, ty};
|
||||
use rustc::ty::layout::{self, TyLayout, LayoutOf};
|
||||
use syntax::source_map::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use rustc::mir::interpret::{EvalResult, Scalar, PointerArithmetic};
|
||||
use rustc::mir::interpret::{EvalResult, PointerArithmetic, EvalErrorKind, Scalar};
|
||||
use super::{
|
||||
EvalContext, Machine, Value, OpTy, Place, PlaceTy, ValTy, Operand, StackPopCleanup
|
||||
};
|
||||
|
|
@ -59,7 +58,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
let mut target_block = targets[targets.len() - 1];
|
||||
|
||||
for (index, &const_int) in values.iter().enumerate() {
|
||||
// Compare using binary_op
|
||||
// Compare using binary_op, to also support pointer values
|
||||
let const_int = Scalar::from_uint(const_int, discr.layout.size);
|
||||
let (res, _) = self.binary_op(mir::BinOp::Eq,
|
||||
discr,
|
||||
|
|
@ -86,37 +85,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
};
|
||||
|
||||
let func = self.eval_operand(func, None)?;
|
||||
let (fn_def, sig) = match func.layout.ty.sty {
|
||||
let (fn_def, abi) = match func.layout.ty.sty {
|
||||
ty::FnPtr(sig) => {
|
||||
let caller_abi = sig.abi();
|
||||
let fn_ptr = self.read_scalar(func)?.to_ptr()?;
|
||||
let instance = self.memory.get_fn(fn_ptr)?;
|
||||
let instance_ty = instance.ty(*self.tcx);
|
||||
match instance_ty.sty {
|
||||
ty::FnDef(..) => {
|
||||
let sig = self.tcx.normalize_erasing_late_bound_regions(
|
||||
self.param_env,
|
||||
&sig,
|
||||
);
|
||||
let real_sig = instance_ty.fn_sig(*self.tcx);
|
||||
let real_sig = self.tcx.normalize_erasing_late_bound_regions(
|
||||
self.param_env,
|
||||
&real_sig,
|
||||
);
|
||||
if !self.check_sig_compat(sig, real_sig)? {
|
||||
return err!(FunctionPointerTyMismatch(real_sig, sig));
|
||||
}
|
||||
(instance, sig)
|
||||
}
|
||||
_ => bug!("unexpected fn ptr to ty: {:?}", instance_ty),
|
||||
}
|
||||
(instance, caller_abi)
|
||||
}
|
||||
ty::FnDef(def_id, substs) => {
|
||||
let sig = func.layout.ty.fn_sig(*self.tcx);
|
||||
let sig = self.tcx.normalize_erasing_late_bound_regions(
|
||||
self.param_env,
|
||||
&sig,
|
||||
);
|
||||
(self.resolve(def_id, substs)?, sig)
|
||||
(self.resolve(def_id, substs)?, sig.abi())
|
||||
},
|
||||
_ => {
|
||||
let msg = format!("can't handle callee of type {:?}", func.layout.ty);
|
||||
|
|
@ -126,11 +104,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
let args = self.eval_operands(args)?;
|
||||
self.eval_fn_call(
|
||||
fn_def,
|
||||
terminator.source_info.span,
|
||||
abi,
|
||||
&args[..],
|
||||
dest,
|
||||
ret,
|
||||
terminator.source_info.span,
|
||||
Some(sig),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +143,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
if expected == cond_val {
|
||||
self.goto_block(Some(target))?;
|
||||
} else {
|
||||
// Compute error message
|
||||
use rustc::mir::interpret::EvalErrorKind::*;
|
||||
return match *msg {
|
||||
BoundsCheck { ref len, ref index } => {
|
||||
|
|
@ -187,11 +166,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
Yield { .. } => unimplemented!("{:#?}", terminator.kind),
|
||||
GeneratorDrop => unimplemented!(),
|
||||
DropAndReplace { .. } => unimplemented!(),
|
||||
Resume => unimplemented!(),
|
||||
Abort => unimplemented!(),
|
||||
Yield { .. } |
|
||||
GeneratorDrop |
|
||||
DropAndReplace { .. } |
|
||||
Resume |
|
||||
Abort => unimplemented!("{:#?}", terminator.kind),
|
||||
FalseEdges { .. } => bug!("should have been eliminated by\
|
||||
`simplify_branches` mir pass"),
|
||||
FalseUnwind { .. } => bug!("should have been eliminated by\
|
||||
|
|
@ -202,91 +181,67 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Decides whether it is okay to call the method with signature `real_sig`
|
||||
/// using signature `sig`.
|
||||
/// FIXME: This should take into account the platform-dependent ABI description.
|
||||
fn check_sig_compat(
|
||||
fn check_argument_compat(
|
||||
caller: TyLayout<'tcx>,
|
||||
callee: TyLayout<'tcx>,
|
||||
) -> bool {
|
||||
if caller.ty == callee.ty {
|
||||
// No question
|
||||
return true;
|
||||
}
|
||||
// Compare layout
|
||||
match (&caller.abi, &callee.abi) {
|
||||
(layout::Abi::Scalar(ref caller), layout::Abi::Scalar(ref callee)) =>
|
||||
// Different valid ranges are okay (once we enforce validity,
|
||||
// that will take care to make it UB to leave the range, just
|
||||
// like for transmute).
|
||||
caller.value == callee.value,
|
||||
// Be conservative
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass a single argument, checking the types for compatibility.
|
||||
fn pass_argument(
|
||||
&mut self,
|
||||
sig: ty::FnSig<'tcx>,
|
||||
real_sig: ty::FnSig<'tcx>,
|
||||
) -> EvalResult<'tcx, bool> {
|
||||
fn check_ty_compat<'tcx>(ty: Ty<'tcx>, real_ty: Ty<'tcx>) -> bool {
|
||||
if ty == real_ty {
|
||||
return true;
|
||||
} // This is actually a fast pointer comparison
|
||||
return match (&ty.sty, &real_ty.sty) {
|
||||
// Permit changing the pointer type of raw pointers and references as well as
|
||||
// mutability of raw pointers.
|
||||
// FIXME: Should not be allowed when fat pointers are involved.
|
||||
(&ty::RawPtr(_), &ty::RawPtr(_)) => true,
|
||||
(&ty::Ref(_, _, _), &ty::Ref(_, _, _)) => {
|
||||
ty.is_mutable_pointer() == real_ty.is_mutable_pointer()
|
||||
}
|
||||
// rule out everything else
|
||||
_ => false,
|
||||
};
|
||||
skip_zst: bool,
|
||||
caller_arg: &mut impl Iterator<Item=OpTy<'tcx>>,
|
||||
callee_arg: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
if skip_zst && callee_arg.layout.is_zst() {
|
||||
// Nothing to do.
|
||||
trace!("Skipping callee ZST");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if sig.abi == real_sig.abi && sig.variadic == real_sig.variadic &&
|
||||
sig.inputs_and_output.len() == real_sig.inputs_and_output.len() &&
|
||||
sig.inputs_and_output
|
||||
.iter()
|
||||
.zip(real_sig.inputs_and_output)
|
||||
.all(|(ty, real_ty)| check_ty_compat(ty, real_ty))
|
||||
{
|
||||
// Definitely good.
|
||||
return Ok(true);
|
||||
let caller_arg = caller_arg.next()
|
||||
.ok_or_else(|| EvalErrorKind::FunctionArgCountMismatch)?;
|
||||
if skip_zst {
|
||||
debug_assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out");
|
||||
}
|
||||
|
||||
if sig.variadic || real_sig.variadic {
|
||||
// We're not touching this
|
||||
return Ok(false);
|
||||
// Now, check
|
||||
if !Self::check_argument_compat(caller_arg.layout, callee_arg.layout) {
|
||||
return err!(FunctionArgMismatch(caller_arg.layout.ty, callee_arg.layout.ty));
|
||||
}
|
||||
|
||||
// We need to allow what comes up when a non-capturing closure is cast to a fn().
|
||||
match (sig.abi, real_sig.abi) {
|
||||
(Abi::Rust, Abi::RustCall) // check the ABIs. This makes the test here non-symmetric.
|
||||
if check_ty_compat(sig.output(), real_sig.output())
|
||||
&& real_sig.inputs_and_output.len() == 3 => {
|
||||
// First argument of real_sig must be a ZST
|
||||
let fst_ty = real_sig.inputs_and_output[0];
|
||||
if self.layout_of(fst_ty)?.is_zst() {
|
||||
// Second argument must be a tuple matching the argument list of sig
|
||||
let snd_ty = real_sig.inputs_and_output[1];
|
||||
match snd_ty.sty {
|
||||
ty::Tuple(tys) if sig.inputs().len() == tys.len() =>
|
||||
if sig.inputs()
|
||||
.iter()
|
||||
.zip(tys)
|
||||
.all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) {
|
||||
return Ok(true)
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
// Nope, this doesn't work.
|
||||
return Ok(false);
|
||||
self.copy_op(caller_arg, callee_arg)
|
||||
}
|
||||
|
||||
/// Call this function -- pushing the stack frame and initializing the arguments.
|
||||
/// `sig` is optional in case of FnPtr/FnDef -- but mandatory for closures!
|
||||
fn eval_fn_call(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
span: Span,
|
||||
caller_abi: Abi,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: Option<PlaceTy<'tcx>>,
|
||||
ret: Option<mir::BasicBlock>,
|
||||
span: Span,
|
||||
sig: Option<ty::FnSig<'tcx>>,
|
||||
) -> EvalResult<'tcx> {
|
||||
trace!("eval_fn_call: {:#?}", instance);
|
||||
|
||||
match instance.def {
|
||||
ty::InstanceDef::Intrinsic(..) => {
|
||||
if caller_abi != Abi::RustIntrinsic {
|
||||
return err!(FunctionAbiMismatch(caller_abi, Abi::RustIntrinsic));
|
||||
}
|
||||
// The intrinsic itself cannot diverge, so if we got here without a return
|
||||
// place... (can happen e.g. for transmute returning `!`)
|
||||
let dest = match dest {
|
||||
|
|
@ -305,6 +260,26 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
ty::InstanceDef::DropGlue(..) |
|
||||
ty::InstanceDef::CloneShim(..) |
|
||||
ty::InstanceDef::Item(_) => {
|
||||
// ABI check
|
||||
{
|
||||
let callee_abi = {
|
||||
let instance_ty = instance.ty(*self.tcx);
|
||||
match instance_ty.sty {
|
||||
ty::FnDef(..) =>
|
||||
instance_ty.fn_sig(*self.tcx).abi(),
|
||||
ty::Closure(..) => Abi::RustCall,
|
||||
ty::Generator(..) => Abi::Rust,
|
||||
_ => bug!("unexpected callee ty: {:?}", instance_ty),
|
||||
}
|
||||
};
|
||||
// Rust and RustCall are compatible
|
||||
let normalize_abi = |abi| if abi == Abi::RustCall { Abi::Rust } else { abi };
|
||||
if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
|
||||
return err!(FunctionAbiMismatch(caller_abi, callee_abi));
|
||||
}
|
||||
}
|
||||
|
||||
// We need MIR for this fn
|
||||
let mir = match M::find_fn(self, instance, args, dest, ret)? {
|
||||
Some(mir) => mir,
|
||||
None => return Ok(()),
|
||||
|
|
@ -322,89 +297,91 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
StackPopCleanup::Goto(ret),
|
||||
)?;
|
||||
|
||||
// If we didn't get a signture, ask `fn_sig`
|
||||
let sig = sig.unwrap_or_else(|| {
|
||||
let fn_sig = instance.ty(*self.tcx).fn_sig(*self.tcx);
|
||||
self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig)
|
||||
});
|
||||
assert_eq!(sig.inputs().len(), args.len());
|
||||
// We can't test the types, as it is fine if the types are ABI-compatible but
|
||||
// not equal.
|
||||
|
||||
// Figure out how to pass which arguments.
|
||||
// FIXME: Somehow this is horribly full of special cases here, and codegen has
|
||||
// none of that. What is going on?
|
||||
trace!(
|
||||
"ABI: {:?}, args: {:#?}",
|
||||
sig.abi,
|
||||
args.iter()
|
||||
.map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
trace!(
|
||||
"spread_arg: {:?}, locals: {:#?}",
|
||||
mir.spread_arg,
|
||||
mir.args_iter()
|
||||
.map(|local|
|
||||
(local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
|
||||
)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// We have two iterators: Where the arguments come from,
|
||||
// and where they go to.
|
||||
|
||||
// For where they come from: If the ABI is RustCall, we untuple the
|
||||
// last incoming argument. These do not have the same type,
|
||||
// so to keep the code paths uniform we accept an allocation
|
||||
// (for RustCall ABI only).
|
||||
let args_effective : Cow<[OpTy<'tcx>]> =
|
||||
if sig.abi == Abi::RustCall && !args.is_empty() {
|
||||
// Untuple
|
||||
let (&untuple_arg, args) = args.split_last().unwrap();
|
||||
trace!("eval_fn_call: Will pass last argument by untupling");
|
||||
Cow::from(args.iter().map(|&a| Ok(a))
|
||||
.chain((0..untuple_arg.layout.fields.count()).into_iter()
|
||||
.map(|i| self.operand_field(untuple_arg, i as u64))
|
||||
// We want to pop this frame again in case there was an error, to put
|
||||
// the blame in the right location. Until the 2018 edition is used in
|
||||
// the compiler, we have to do this with an immediately invoked function.
|
||||
let res = (||{
|
||||
trace!(
|
||||
"caller ABI: {:?}, args: {:#?}",
|
||||
caller_abi,
|
||||
args.iter()
|
||||
.map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
trace!(
|
||||
"spread_arg: {:?}, locals: {:#?}",
|
||||
mir.spread_arg,
|
||||
mir.args_iter()
|
||||
.map(|local|
|
||||
(local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
|
||||
)
|
||||
.collect::<EvalResult<Vec<OpTy<'tcx>>>>()?)
|
||||
} else {
|
||||
// Plain arg passing
|
||||
Cow::from(args)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// Figure out how to pass which arguments.
|
||||
// We have two iterators: Where the arguments come from,
|
||||
// and where they go to.
|
||||
let skip_zst = match caller_abi {
|
||||
Abi::Rust | Abi::RustCall => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
// Now we have to spread them out across the callee's locals,
|
||||
// taking into account the `spread_arg`.
|
||||
let mut args_iter = args_effective.iter();
|
||||
let mut local_iter = mir.args_iter();
|
||||
// HACK: ClosureOnceShim calls something that expects a ZST as
|
||||
// first argument, but the callers do not actually pass that ZST.
|
||||
// Codegen doesn't care because ZST arguments do not even exist there.
|
||||
match instance.def {
|
||||
ty::InstanceDef::ClosureOnceShim { .. } if sig.abi == Abi::Rust => {
|
||||
let local = local_iter.next().unwrap();
|
||||
// For where they come from: If the ABI is RustCall, we untuple the
|
||||
// last incoming argument. These two iterators do not have the same type,
|
||||
// so to keep the code paths uniform we accept an allocation
|
||||
// (for RustCall ABI only).
|
||||
let caller_args : Cow<[OpTy<'tcx>]> =
|
||||
if caller_abi == Abi::RustCall && !args.is_empty() {
|
||||
// Untuple
|
||||
let (&untuple_arg, args) = args.split_last().unwrap();
|
||||
trace!("eval_fn_call: Will pass last argument by untupling");
|
||||
Cow::from(args.iter().map(|&a| Ok(a))
|
||||
.chain((0..untuple_arg.layout.fields.count()).into_iter()
|
||||
.map(|i| self.operand_field(untuple_arg, i as u64))
|
||||
)
|
||||
.collect::<EvalResult<Vec<OpTy<'tcx>>>>()?)
|
||||
} else {
|
||||
// Plain arg passing
|
||||
Cow::from(args)
|
||||
};
|
||||
// Skip ZSTs
|
||||
let mut caller_iter = caller_args.iter()
|
||||
.filter(|op| !skip_zst || !op.layout.is_zst())
|
||||
.map(|op| *op);
|
||||
|
||||
// Now we have to spread them out across the callee's locals,
|
||||
// taking into account the `spread_arg`. If we could write
|
||||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ZSTs.
|
||||
let mut locals_iter = mir.args_iter();
|
||||
while let Some(local) = locals_iter.next() {
|
||||
let dest = self.eval_place(&mir::Place::Local(local))?;
|
||||
assert!(dest.layout.is_zst());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Now back to norml argument passing.
|
||||
while let Some(local) = local_iter.next() {
|
||||
let dest = self.eval_place(&mir::Place::Local(local))?;
|
||||
if Some(local) == mir.spread_arg {
|
||||
// Must be a tuple
|
||||
for i in 0..dest.layout.fields.count() {
|
||||
let dest = self.place_field(dest, i as u64)?;
|
||||
self.copy_op(*args_iter.next().unwrap(), dest)?;
|
||||
if Some(local) == mir.spread_arg {
|
||||
// Must be a tuple
|
||||
for i in 0..dest.layout.fields.count() {
|
||||
let dest = self.place_field(dest, i as u64)?;
|
||||
self.pass_argument(skip_zst, &mut caller_iter, dest)?;
|
||||
}
|
||||
} else {
|
||||
// Normal argument
|
||||
self.pass_argument(skip_zst, &mut caller_iter, dest)?;
|
||||
}
|
||||
} else {
|
||||
// Normal argument
|
||||
self.copy_op(*args_iter.next().unwrap(), dest)?;
|
||||
}
|
||||
// Now we should have no more caller args
|
||||
if caller_iter.next().is_some() {
|
||||
trace!("Caller has too many args over");
|
||||
return err!(FunctionArgCountMismatch);
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
match res {
|
||||
Err(err) => {
|
||||
self.stack.pop();
|
||||
Err(err)
|
||||
}
|
||||
Ok(v) => Ok(v)
|
||||
}
|
||||
// Now we should be done
|
||||
assert!(args_iter.next().is_none());
|
||||
Ok(())
|
||||
}
|
||||
// cannot use the shim here, because that will only result in infinite recursion
|
||||
ty::InstanceDef::Virtual(_, idx) => {
|
||||
|
|
@ -428,7 +405,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
args[0].op = Operand::Immediate(Value::Scalar(ptr.ptr.into())); // strip vtable
|
||||
trace!("Patched self operand to {:#?}", args[0]);
|
||||
// recurse with concrete function
|
||||
self.eval_fn_call(instance, &args, dest, ret, span, sig)
|
||||
self.eval_fn_call(instance, span, caller_abi, &args, dest, ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -464,11 +441,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
|
||||
self.eval_fn_call(
|
||||
instance,
|
||||
span,
|
||||
Abi::Rust,
|
||||
&[arg],
|
||||
Some(dest),
|
||||
Some(target),
|
||||
span,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,9 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
| MachineError(_)
|
||||
// at runtime these transformations might make sense
|
||||
// FIXME: figure out the rules and start linting
|
||||
| FunctionPointerTyMismatch(..)
|
||||
| FunctionAbiMismatch(..)
|
||||
| FunctionArgMismatch(..)
|
||||
| FunctionArgCountMismatch
|
||||
// fine at runtime, might be a register address or sth
|
||||
| ReadBytesAsPointer
|
||||
// fine at runtime
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue