Auto merge of #91342 - RalfJung:fn-abi, r=eddyb,oli-obk

CTFE eval_fn_call: use FnAbi to determine argument skipping and compatibility

This makes use of the `FnAbi` type in CTFE/Miri, which `@eddyb` has been saying for years is what we should do.^^ `FnAbi` is used to
- determine which arguments to skip (rather than the previous heuristic of skipping ZST arguments with the Rust ABI)
- impose further restrictions on whether caller and callee are consistent in how a given argument is passed

I was hoping it would also simplify the code, but that is not the case -- the previous type compatibility checks are still required (AFAIK), only the ZST skipping is gone and that took barely any code. We also need some hacks because `FnAbi` assumes a certain way of implementing `caller_location` (by passing extra arguments), but Miri can just read the caller location from the call stack so it doesn't need those arguments. (The fact that every backend has to separately implement support for these arguments seems suboptimal -- looks like this might have been better implemented on the MIR level.) To avoid having to implement those unnecessary arguments in Miri, we just compute *whether* the argument is present on the caller/callee side, but don't actually pass that argument around.

I have no idea if this looks the way `@eddyb` thinks it should look... but it makes Miri's test suite pass. ;)
One of rustc's tests fails unfortunately (`ui/const-generics/issues/issue-67739.rs`), some const generic code that is evaluated too early -- I think that should raise `TooGeneric` but instead it ICEs. My assumption is this is some FnAbi code that has not been properly tested on polymorphic code, but it might also be me calling that FnAbi code the wrong way.

r? `@oli-obk` `@eddyb`
Fixes https://github.com/rust-lang/rust/issues/56166
Miri PR at https://github.com/rust-lang/miri/pull/1928
This commit is contained in:
bors 2021-12-24 04:59:05 +00:00
commit 59337cddd4
9 changed files with 241 additions and 150 deletions

View File

@ -260,7 +260,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
args: &[OpTy<'tcx>],
_ret: Option<(&PlaceTy<'tcx>, mir::BasicBlock)>,
_unwind: StackPopUnwind, // unwinding is not supported in consts
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
debug!("find_mir_or_eval_fn: {:?}", instance);
// Only check non-glue functions
@ -279,11 +279,21 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
if let Some(new_instance) = ecx.hook_special_const_fn(instance, args)? {
// We call another const fn instead.
return Self::find_mir_or_eval_fn(ecx, new_instance, _abi, args, _ret, _unwind);
// However, we return the *original* instance to make backtraces work out
// (and we hope this does not confuse the FnAbi checks too much).
return Ok(Self::find_mir_or_eval_fn(
ecx,
new_instance,
_abi,
args,
_ret,
_unwind,
)?
.map(|(body, _instance)| (body, instance)));
}
}
// This is a const fn. Call it.
Ok(Some(ecx.load_mir(instance.def, None)?))
Ok(Some((ecx.load_mir(instance.def, None)?, instance)))
}
fn call_intrinsic(

View File

@ -8,7 +8,10 @@ use rustc_index::vec::IndexVec;
use rustc_macros::HashStable;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo};
use rustc_middle::ty::layout::{self, LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
TyAndLayout,
};
use rustc_middle::ty::{
self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
};
@ -16,7 +19,7 @@ use rustc_mir_dataflow::storage::AlwaysLiveLocals;
use rustc_query_system::ich::StableHashingContext;
use rustc_session::Limit;
use rustc_span::{Pos, Span};
use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};
use super::{
AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace,
@ -333,6 +336,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> LayoutOfHelpers<'tcx> for InterpC
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'mir, 'tcx, M> {
type FnAbiOfResult = InterpResult<'tcx, &'tcx FnAbi<'tcx, Ty<'tcx>>>;
fn handle_fn_abi_err(
&self,
err: FnAbiError<'tcx>,
_span: Span,
_fn_abi_request: FnAbiRequest<'tcx>,
) -> InterpErrorInfo<'tcx> {
match err {
FnAbiError::Layout(err) => err_inval!(Layout(err)).into(),
FnAbiError::AdjustForForeignAbi(err) => {
err_inval!(FnAbiAdjustForForeignAbi(err)).into()
}
}
}
}
/// Test if it is valid for a MIR assignment to assign `src`-typed place to `dest`-typed value.
/// This test should be symmetric, as it is primarily about layout compatibility.
pub(super) fn mir_assign_valid_types<'tcx>(

View File

@ -167,7 +167,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
args: &[OpTy<'tcx, Self::PointerTag>],
ret: Option<(&PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
unwind: StackPopUnwind,
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>;
/// Execute `fn_val`. It is the hook's responsibility to advance the instruction
/// pointer as appropriate.

View File

@ -1,14 +1,14 @@
use std::borrow::Cow;
use std::convert::TryFrom;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::layout::{self, LayoutOf as _, TyAndLayout};
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
use rustc_middle::ty::Instance;
use rustc_middle::{
mir,
ty::{self, Ty},
};
use rustc_target::abi;
use rustc_target::abi::call::{ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode};
use rustc_target::spec::abi::Abi;
use super::{
@ -17,10 +17,6 @@ use super::{
};
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
fn fn_can_unwind(&self, attrs: CodegenFnAttrFlags, abi: Abi) -> bool {
layout::fn_can_unwind(*self.tcx, attrs, abi)
}
pub(super) fn eval_terminator(
&mut self,
terminator: &mir::Terminator<'tcx>,
@ -64,25 +60,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let old_stack = self.frame_idx();
let old_loc = self.frame().loc;
let func = self.eval_operand(func, None)?;
let (fn_val, abi, caller_can_unwind) = match *func.layout.ty.kind() {
ty::FnPtr(sig) => {
let caller_abi = sig.abi();
let args = self.eval_operands(args)?;
let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
let fn_sig =
self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder);
let extra_args = &args[fn_sig.inputs().len()..];
let extra_args = self.tcx.mk_type_list(extra_args.iter().map(|arg| arg.layout.ty));
let (fn_val, fn_abi, with_caller_location) = match *func.layout.ty.kind() {
ty::FnPtr(_sig) => {
let fn_ptr = self.read_pointer(&func)?;
let fn_val = self.memory.get_fn(fn_ptr)?;
(
fn_val,
caller_abi,
self.fn_can_unwind(CodegenFnAttrFlags::empty(), caller_abi),
)
(fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false)
}
ty::FnDef(def_id, substs) => {
let sig = func.layout.ty.fn_sig(*self.tcx);
let instance =
self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?;
(
FnVal::Instance(
self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?,
),
sig.abi(),
self.fn_can_unwind(self.tcx.codegen_fn_attrs(def_id).flags, sig.abi()),
FnVal::Instance(instance),
self.fn_abi_of_instance(instance, extra_args)?,
instance.def.requires_caller_location(*self.tcx),
)
}
_ => span_bug!(
@ -91,7 +89,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
func.layout.ty
),
};
let args = self.eval_operands(args)?;
let dest_place;
let ret = match destination {
Some((dest, ret)) => {
@ -102,10 +100,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
};
self.eval_fn_call(
fn_val,
abi,
(fn_sig.abi, fn_abi),
&args,
with_caller_location,
ret,
match (cleanup, caller_can_unwind) {
match (cleanup, fn_abi.can_unwind) {
(Some(cleanup), true) => StackPopUnwind::Cleanup(*cleanup),
(None, true) => StackPopUnwind::Skip,
(_, false) => StackPopUnwind::NotAllowed,
@ -174,68 +173,128 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
fn check_argument_compat(
rust_abi: bool,
caller: TyAndLayout<'tcx>,
callee: TyAndLayout<'tcx>,
caller_abi: &ArgAbi<'tcx, Ty<'tcx>>,
callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
) -> bool {
if caller.ty == callee.ty {
// No question
// Heuristic for type comparison.
let layout_compat = || {
if caller_abi.layout.ty == callee_abi.layout.ty {
// No question
return true;
}
// Compare layout
match (caller_abi.layout.abi, callee_abi.layout.abi) {
// 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).
(abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {
caller.value == callee.value
}
(
abi::Abi::ScalarPair(caller1, caller2),
abi::Abi::ScalarPair(callee1, callee2),
) => caller1.value == callee1.value && caller2.value == callee2.value,
// Be conservative
_ => false,
}
};
// Padding must be fully equal.
let pad_compat = || caller_abi.pad == callee_abi.pad;
// When comparing the PassMode, we have to be smart about comparing the attributes.
let arg_attr_compat = |a1: ArgAttributes, a2: ArgAttributes| {
// There's only one regular attribute that matters for the call ABI: InReg.
// Everything else is things like noalias, dereferencable, nonnull, ...
// (This also applies to pointee_size, pointee_align.)
if a1.regular.contains(ArgAttribute::InReg) != a2.regular.contains(ArgAttribute::InReg)
{
return false;
}
// We also compare the sign extension mode -- this could let the callee make assumptions
// about bits that conceptually were not even passed.
if a1.arg_ext != a2.arg_ext {
return false;
}
return true;
};
let mode_compat = || match (caller_abi.mode, callee_abi.mode) {
(PassMode::Ignore, PassMode::Ignore) => true,
(PassMode::Direct(a1), PassMode::Direct(a2)) => arg_attr_compat(a1, a2),
(PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => {
arg_attr_compat(a1, a2) && arg_attr_compat(b1, b2)
}
(PassMode::Cast(c1), PassMode::Cast(c2)) => c1 == c2,
(
PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 },
PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
) => arg_attr_compat(a1, a2) && s1 == s2,
(
PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 },
PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 },
) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && s1 == s2,
_ => false,
};
if layout_compat() && pad_compat() && mode_compat() {
return true;
}
if !rust_abi {
// Don't risk anything
return false;
}
// Compare layout
match (caller.abi, callee.abi) {
// 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).
(abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => caller.value == callee.value,
(abi::Abi::ScalarPair(caller1, caller2), abi::Abi::ScalarPair(callee1, callee2)) => {
caller1.value == callee1.value && caller2.value == callee2.value
}
// Be conservative
_ => false,
}
trace!(
"check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}",
caller_abi,
callee_abi
);
return false;
}
/// Pass a single argument, checking the types for compatibility.
fn pass_argument(
/// Initialize a single callee argument, checking the types for compatibility.
fn pass_argument<'x, 'y>(
&mut self,
rust_abi: bool,
caller_arg: &mut impl Iterator<Item = OpTy<'tcx, M::PointerTag>>,
caller_args: &mut impl Iterator<
Item = (&'x OpTy<'tcx, M::PointerTag>, &'y ArgAbi<'tcx, Ty<'tcx>>),
>,
callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
callee_arg: &PlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
if rust_abi && callee_arg.layout.is_zst() {
// Nothing to do.
trace!("Skipping callee ZST");
) -> InterpResult<'tcx>
where
'tcx: 'x,
'tcx: 'y,
{
if matches!(callee_abi.mode, PassMode::Ignore) {
// This one is skipped.
return Ok(());
}
let caller_arg = caller_arg.next().ok_or_else(|| {
// Find next caller arg.
let (caller_arg, caller_abi) = caller_args.next().ok_or_else(|| {
err_ub_format!("calling a function with fewer arguments than it requires")
})?;
if rust_abi {
assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out");
}
// Now, check
if !Self::check_argument_compat(rust_abi, caller_arg.layout, callee_arg.layout) {
if !Self::check_argument_compat(caller_abi, callee_abi) {
throw_ub_format!(
"calling a function with argument of type {:?} passing data of type {:?}",
callee_arg.layout.ty,
caller_arg.layout.ty
)
}
// We allow some transmutes here
// We allow some transmutes here.
// FIXME: Depending on the PassMode, this should reset some padding to uninitialized. (This
// is true for all `copy_op`, but there are a lot of special cases for argument passing
// specifically.)
self.copy_op_transmute(&caller_arg, callee_arg)
}
/// Call this function -- pushing the stack frame and initializing the arguments.
///
/// `caller_fn_abi` is used to determine if all the arguments are passed the proper way.
/// However, we also need `caller_abi` to determine if we need to do untupling of arguments.
///
/// `with_caller_location` indicates whether the caller passed a caller location. Miri
/// implements caller locations without argument passing, but to match `FnAbi` we need to know
/// when those arguments are present.
pub(crate) fn eval_fn_call(
&mut self,
fn_val: FnVal<'tcx, M::ExtraFnVal>,
caller_abi: Abi,
(caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>),
args: &[OpTy<'tcx, M::PointerTag>],
with_caller_location: bool,
ret: Option<(&PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
mut unwind: StackPopUnwind,
) -> InterpResult<'tcx> {
@ -248,39 +307,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
};
let get_abi = |this: &Self, instance_ty: Ty<'tcx>| match instance_ty.kind() {
ty::FnDef(..) => instance_ty.fn_sig(*this.tcx).abi(),
ty::Closure(..) => Abi::RustCall,
ty::Generator(..) => Abi::Rust,
_ => span_bug!(this.cur_span(), "unexpected callee ty: {:?}", instance_ty),
};
// ABI check
let check_abi = |callee_abi: Abi| -> InterpResult<'tcx> {
let normalize_abi = |abi| match abi {
Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic =>
// These are all the same ABI, really.
{
Abi::Rust
}
abi => abi,
};
if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
throw_ub_format!(
"calling a function with ABI {} using caller ABI {}",
callee_abi.name(),
caller_abi.name()
)
}
Ok(())
};
match instance.def {
ty::InstanceDef::Intrinsic(..) => {
if M::enforce_abi(self) {
check_abi(get_abi(self, instance.ty(*self.tcx, self.param_env)))?;
}
assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic);
// caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic.
M::call_intrinsic(self, instance, args, ret, unwind)
}
ty::InstanceDef::VtableShim(..)
@ -291,26 +321,37 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
| ty::InstanceDef::CloneShim(..)
| ty::InstanceDef::Item(_) => {
// We need MIR for this fn
let body =
let (body, instance) =
match M::find_mir_or_eval_fn(self, instance, caller_abi, args, ret, unwind)? {
Some(body) => body,
None => return Ok(()),
};
// Check against the ABI of the MIR body we are calling (not the ABI of `instance`;
// these can differ when `find_mir_or_eval_fn` does something clever like resolve
// exported symbol names).
let callee_def_id = body.source.def_id();
let callee_abi = get_abi(self, self.tcx.type_of(callee_def_id));
// Compute callee information using the `instance` returned by
// `find_mir_or_eval_fn`.
// FIXME: for variadic support, do we have to somehow determine calle's extra_args?
let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
if M::enforce_abi(self) {
check_abi(callee_abi)?;
if callee_fn_abi.c_variadic != caller_fn_abi.c_variadic {
throw_ub_format!(
"calling a c-variadic function via a non-variadic call site, or vice versa"
);
}
if callee_fn_abi.c_variadic {
throw_unsup_format!("calling a c-variadic function is not supported");
}
if !matches!(unwind, StackPopUnwind::NotAllowed)
&& !self
.fn_can_unwind(self.tcx.codegen_fn_attrs(callee_def_id).flags, callee_abi)
{
if M::enforce_abi(self) {
if caller_fn_abi.conv != callee_fn_abi.conv {
throw_ub_format!(
"calling a function with calling convention {:?} using calling convention {:?}",
callee_fn_abi.conv,
caller_fn_abi.conv
)
}
}
if !matches!(unwind, StackPopUnwind::NotAllowed) && !callee_fn_abi.can_unwind {
// The callee cannot unwind.
unwind = StackPopUnwind::NotAllowed;
}
@ -343,12 +384,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.collect::<Vec<_>>()
);
// Figure out how to pass which arguments.
// The Rust ABI is special: ZST get skipped.
let rust_abi = matches!(caller_abi, Abi::Rust | Abi::RustCall);
// We have two iterators: Where the arguments come from,
// and where they go to.
// In principle, 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 two iterators do not have the same type,
@ -373,53 +410,59 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Plain arg passing
Cow::from(args)
};
// Skip ZSTs
let mut caller_iter =
caller_args.iter().filter(|op| !rust_abi || !op.layout.is_zst()).copied();
// If `with_caller_location` is set we pretend there is an extra argument (that
// we will not pass).
assert_eq!(
caller_args.len() + if with_caller_location { 1 } else { 0 },
caller_fn_abi.args.len(),
"mismatch between caller ABI and caller arguments",
);
let mut caller_args = caller_args
.iter()
.zip(caller_fn_abi.args.iter())
.filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore));
// 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 callee_args_abis = callee_fn_abi.args.iter();
for local in body.args_iter() {
let dest = self.eval_place(mir::Place::from(local))?;
if Some(local) == body.spread_arg {
// Must be a tuple
for i in 0..dest.layout.fields.count() {
let dest = self.place_field(&dest, i)?;
self.pass_argument(rust_abi, &mut caller_iter, &dest)?;
let callee_abi = callee_args_abis.next().unwrap();
self.pass_argument(&mut caller_args, callee_abi, &dest)?;
}
} else {
// Normal argument
self.pass_argument(rust_abi, &mut caller_iter, &dest)?;
let callee_abi = callee_args_abis.next().unwrap();
self.pass_argument(&mut caller_args, callee_abi, &dest)?;
}
}
// Now we should have no more caller args
if caller_iter.next().is_some() {
// If the callee needs a caller location, pretend we consume one more argument from the ABI.
if instance.def.requires_caller_location(*self.tcx) {
callee_args_abis.next().unwrap();
}
// Now we should have no more caller args or callee arg ABIs
assert!(
callee_args_abis.next().is_none(),
"mismatch between callee ABI and callee body arguments"
);
if caller_args.next().is_some() {
throw_ub_format!("calling a function with more arguments than it expected")
}
// Don't forget to check the return type!
if let Some((caller_ret, _)) = ret {
let callee_ret = self.eval_place(mir::Place::return_place())?;
if !Self::check_argument_compat(
rust_abi,
caller_ret.layout,
callee_ret.layout,
) {
throw_ub_format!(
"calling a function with return type {:?} passing \
return place of type {:?}",
callee_ret.layout.ty,
caller_ret.layout.ty
)
}
} else {
let local = mir::RETURN_PLACE;
let callee_layout = self.layout_of_local(self.frame(), local, None)?;
if !callee_layout.abi.is_uninhabited() {
throw_ub_format!("calling a returning function without a return place")
}
if !Self::check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret) {
throw_ub_format!(
"calling a function with return type {:?} passing \
return place of type {:?}",
callee_fn_abi.ret.layout.ty,
caller_fn_abi.ret.layout.ty,
)
}
};
match res {
@ -464,7 +507,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
));
trace!("Patched self operand to {:#?}", args[0]);
// recurse with concrete function
self.eval_fn_call(fn_val, caller_abi, &args, ret, unwind)
self.eval_fn_call(
fn_val,
(caller_abi, caller_fn_abi),
&args,
with_caller_location,
ret,
unwind,
)
}
}
}
@ -489,6 +539,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
_ => (instance, place),
};
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
let arg = ImmTy::from_immediate(
place.to_ref(self),
@ -500,8 +551,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.eval_fn_call(
FnVal::Instance(instance),
Abi::Rust,
(Abi::Rust, fn_abi),
&[arg.into()],
false,
Some((&dest.into(), target)),
match unwind {
Some(cleanup) => StackPopUnwind::Cleanup(cleanup),

View File

@ -8,7 +8,7 @@ use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported}
use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace;
use rustc_span::def_id::DefId;
use rustc_target::abi::{Align, Size};
use rustc_target::abi::{call, Align, Size};
use std::{any::Any, backtrace::Backtrace, fmt};
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
@ -141,6 +141,10 @@ pub enum InvalidProgramInfo<'tcx> {
AlreadyReported(ErrorReported),
/// An error occurred during layout computation.
Layout(layout::LayoutError<'tcx>),
/// An error occurred during FnAbi computation: the passed --target lacks FFI support
/// (which unfortunately typeck does not reject).
/// Not using `FnAbiError` as that contains a nested `LayoutError`.
FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
/// An invalid transmute happened.
TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
/// SizeOf of unsized type was requested.
@ -157,6 +161,7 @@ impl fmt::Display for InvalidProgramInfo<'_> {
write!(f, "encountered constants with type errors, stopping evaluation")
}
Layout(ref err) => write!(f, "{}", err),
FnAbiAdjustForForeignAbi(ref err) => write!(f, "{}", err),
TransmuteSizeDiff(from_ty, to_ty) => write!(
f,
"transmuting `{}` to `{}` is not possible, because these types do not have the same size",

View File

@ -2581,9 +2581,12 @@ impl<'tcx> ty::Instance<'tcx> {
// for `Instance` (e.g. typeck would use `Ty::fn_sig` instead),
// or should go through `FnAbi` instead, to avoid losing any
// adjustments `fn_abi_of_instance` might be performing.
fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
// FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function.
let ty = self.ty(tcx, ty::ParamEnv::reveal_all());
fn fn_sig_for_fn_abi(
&self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> ty::PolyFnSig<'tcx> {
let ty = self.ty(tcx, param_env);
match *ty.kind() {
ty::FnDef(..) => {
// HACK(davidtwco,eddyb): This is a workaround for polymorphization considering
@ -2965,7 +2968,7 @@ fn fn_abi_of_instance<'tcx>(
) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
let (param_env, (instance, extra_args)) = query.into_parts();
let sig = instance.fn_sig_for_fn_abi(tcx);
let sig = instance.fn_sig_for_fn_abi(tcx, param_env);
let caller_location = if instance.def.requires_caller_location(tcx) {
Some(tcx.caller_location_ty())

View File

@ -207,7 +207,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
_args: &[OpTy<'tcx>],
_ret: Option<(&PlaceTy<'tcx>, BasicBlock)>,
_unwind: StackPopUnwind,
) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> {
Ok(None)
}

View File

@ -9,7 +9,7 @@ const extern "C" fn c_fn() {}
const fn call_rust_fn(my_fn: extern "Rust" fn()) {
my_fn();
//~^ ERROR could not evaluate static initializer
//~| NOTE calling a function with ABI C using caller ABI Rust
//~| NOTE calling a function with calling convention C using calling convention Rust
//~| NOTE inside `call_rust_fn`
}

View File

@ -4,7 +4,7 @@ error[E0080]: could not evaluate static initializer
LL | my_fn();
| ^^^^^^^
| |
| calling a function with ABI C using caller ABI Rust
| calling a function with calling convention C using calling convention Rust
| inside `call_rust_fn` at $DIR/abi-mismatch.rs:10:5
...
LL | static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) });