Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2460,16 +2460,34 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
/// aggregates are passed via `PassMode::Indirect`, `PassMode::IndirectUnsized` or
/// `PassMode::Cast`; more strict checks would otherwise be required.
pub fn eq_abi(&self, other: &Self) -> bool {
let LayoutData {
size,
align,
backend_repr,
// We assume aggregates are passed via `Cast` or `Indirect*` so their details either
// do not matter or they are checked by `PassMode::eq_abi`.
fields: _,
variants: _,
// Inhabitedness and niches are not ABI-relevant.
largest_niche: _,
uninhabited: _,
// max_repr_align, unadjusted_abi_align, repr_c are not read directly by codegen
// backends. They are only used to compute `PassMode`. So we can ignore them here, any
// differences they may cause are picked up by the `PassMode` check.
max_repr_align: _,
unadjusted_abi_align: _,
repr_c: _,
// The seed does not affect the ABI.
randomization_seed: _,
} = self;
// The one thing that we are not capturing here is that for unsized types, the metadata must
// also have the same ABI, and moreover that the same metadata leads to the same size. The
// 2nd point is quite hard to check though.
self.size == other.size
&& self.is_sized() == other.is_sized()
&& self.backend_repr.eq_up_to_validity(&other.backend_repr)
&& self.backend_repr.is_bool() == other.backend_repr.is_bool()
&& self.align.abi == other.align.abi
&& self.max_repr_align == other.max_repr_align
&& self.unadjusted_abi_align == other.unadjusted_abi_align
*size == other.size
&& *align == other.align
&& backend_repr.eq_up_to_validity(&other.backend_repr)
// `is_bool` checks validity and is ABI-relevant, so we need to consider it.
&& backend_repr.is_bool() == other.backend_repr.is_bool()
}
}

Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if self.layout_compat(caller_abi.layout, callee_abi.layout)? {
// Ensure that our checks imply actual ABI compatibility for this concrete call.
// (This can fail e.g. if `#[rustc_nonnull_optimization_guaranteed]` is used incorrectly.)
assert!(caller_abi.eq_abi(callee_abi));
assert!(
caller_abi.eq_abi(callee_abi),
"these types were deemed ABI-compatible but do not actually have compatible `FnAbi`:\ncaller: {caller_abi:#?}\ncallee: {callee_abi:#?}"
);
interp_ok(true)
} else {
trace!(
Expand Down
18 changes: 3 additions & 15 deletions compiler/rustc_target/src/callconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,21 +613,9 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
where
Ty: PartialEq,
{
// Ideally we'd just compare the `mode`, but that is not enough -- for some modes LLVM will look
// at the type.
self.layout.eq_abi(&other.layout) && self.mode.eq_abi(&other.mode) && {
// `fn_arg_sanity_check` accepts `PassMode::Direct` for some aggregates.
// That elevates any type difference to an ABI difference since we just use the
// full Rust type as the LLVM argument/return type.
if matches!(self.mode, PassMode::Direct(..))
&& matches!(self.layout.backend_repr, BackendRepr::Memory { .. })
{
// For aggregates in `Direct` mode to be compatible, the types need to be equal.
self.layout.ty == other.layout.ty
} else {
true
}
}
// Ideally we'd just compare the `mode`, but that is not enough -- for some modes LLVM will
// look at the type.
self.layout.eq_abi(&other.layout) && self.mode.eq_abi(&other.mode)
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/tools/miri/tests/pass/function_calls/abi_compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ use std::{mem, num, ptr};
#[derive(Copy, Clone, Default)]
struct Zst;

#[derive(Copy, Clone, Default)]
#[repr(align(1))]
struct ReprAlignZst;

#[derive(Copy, Clone, Default)]
#[repr(C)]
struct ReprCZst;

#[repr(transparent)]
#[derive(Copy, Clone)]
struct Wrapper<T>(T);
Expand Down Expand Up @@ -124,6 +132,8 @@ fn main() {
test_abi_compat(main as fn(), id::<i32> as fn(i32) -> i32);
// - trivial-ABI types
test_abi_compat((), [(); 0]);
test_abi_compat((), Zst);
test_abi_compat((), ReprAlignZst);

// Guaranteed null-pointer-layout optimizations:
// - Guaranteed Option<X> null-pointer-optimizations (RFC 3391).
Expand Down Expand Up @@ -157,6 +167,8 @@ fn main() {
// with the wrapped field.
test_abi_newtype::<()>();
test_abi_newtype::<Zst>();
test_abi_newtype::<ReprAlignZst>();
test_abi_newtype::<ReprCZst>();
test_abi_newtype::<u32>();
test_abi_newtype::<f32>();
test_abi_newtype::<(u8, u16, f32)>();
Expand All @@ -179,5 +191,6 @@ fn main() {
let non_capturing_closure = || {};
test_abi_compat(non_capturing_closure.clone(), Wrapper(non_capturing_closure));

// Check that we take the CFI tag into account.
test_abi_cfi_encoding();
}
Loading