Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repr(int) fieldless enums are ABI-compatible with int #128600

Closed
wants to merge 2 commits into from
Closed
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
14 changes: 14 additions & 0 deletions compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
ty::Int(ity) => (Integer::from_int_ty(&self.tcx, *ity), /* signed */ true),
ty::Uint(uty) => (Integer::from_uint_ty(&self.tcx, *uty), /* signed */ false),
ty::Char => (Integer::I32, /* signed */ false),
ty::Adt(def, _) => {
// Ensure it is an enum, with a suitable repr, and fieldless.
if !def.is_enum() {
return None;
}
let Some(int_ty) = def.repr().int else {
return None;
};
if !def.variants().iter().all(|variant| variant.fields.is_empty()) {
return None;
}
let int = Integer::from_attr(&self.tcx, int_ty);
(int, int_ty.is_signed())
}
_ => return None,
})
};
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,8 @@ mod prim_ref {}
/// Pointee>::Metadata`).
/// - `usize` is ABI-compatible with the `uN` integer type of the same size, and likewise `isize` is
/// ABI-compatible with the `iN` integer type of the same size.
/// - A fieldless enum with `repr(u*)` or `repr(i*)` is ABI-compatible with the integer type given
/// in the `repr`.
/// - `char` is ABI-compatible with `u32`.
/// - Any two `fn` (function pointer) types are ABI-compatible with each other if they have the same
/// ABI string or the ABI string only differs in a trailing `-unwind`, independent of the rest of
Expand Down
17 changes: 17 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 @@ -71,6 +71,23 @@ fn main() {
test_abi_compat(0isize, 0i64);
}
test_abi_compat(42u32, num::NonZero::new(1u32).unwrap());
// - `repr(int)` enums and the corresponding integer.
#[repr(i16)]
#[derive(Copy, Clone)]
enum I16 {
Var1 = 0,
Var2 = -5,
}
test_abi_compat(I16::Var1, 0i16);
test_abi_compat(I16::Var2, -5i16);
#[repr(u64)]
#[derive(Copy, Clone)]
enum U64 {
Var1 = 0,
Var2 = u64::MAX,
}
test_abi_compat(U64::Var1, 0u64);
test_abi_compat(U64::Var2, u64::MAX);
// - `char` and `u32`.
test_abi_compat(42u32, 'x');
// - Reference/pointer types with the same pointee.
Expand Down
104 changes: 62 additions & 42 deletions tests/ui/abi/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ mod prelude {
#[cfg(not(host))]
use prelude::*;

macro_rules! assert_abi_compatible {
macro_rules! test_abi_compatible {
($name:ident, $t1:ty, $t2:ty) => {
mod $name {
use super::*;
Expand Down Expand Up @@ -232,55 +232,75 @@ union ReprCUnion<T> {
something: ManuallyDrop<T>,
}

macro_rules! test_abi_compatible {
macro_rules! test_abi_compatible_nested {
($name:ident, $t1:ty, $t2:ty) => {
mod $name {
use super::*;
assert_abi_compatible!(plain, $t1, $t2);
test_abi_compatible!(plain, $t1, $t2);
// We also do some tests with differences in fields of `repr(C)` types.
assert_abi_compatible!(repr_c_1, ReprC1<$t1>, ReprC1<$t2>);
assert_abi_compatible!(repr_c_2_int, ReprC2Int<$t1>, ReprC2Int<$t2>);
assert_abi_compatible!(repr_c_2_float, ReprC2Float<$t1>, ReprC2Float<$t2>);
assert_abi_compatible!(repr_c_4, ReprC4<$t1>, ReprC4<$t2>);
assert_abi_compatible!(repr_c_4mixed, ReprC4Mixed<$t1>, ReprC4Mixed<$t2>);
assert_abi_compatible!(repr_c_enum, ReprCEnum<$t1>, ReprCEnum<$t2>);
assert_abi_compatible!(repr_c_union, ReprCUnion<$t1>, ReprCUnion<$t2>);
// This is not guaranteed, but it's still good to know when there are differences here.
test_abi_compatible!(repr_c_1, ReprC1<$t1>, ReprC1<$t2>);
test_abi_compatible!(repr_c_2_int, ReprC2Int<$t1>, ReprC2Int<$t2>);
test_abi_compatible!(repr_c_2_float, ReprC2Float<$t1>, ReprC2Float<$t2>);
test_abi_compatible!(repr_c_4, ReprC4<$t1>, ReprC4<$t2>);
test_abi_compatible!(repr_c_4mixed, ReprC4Mixed<$t1>, ReprC4Mixed<$t2>);
test_abi_compatible!(repr_c_enum, ReprCEnum<$t1>, ReprCEnum<$t2>);
test_abi_compatible!(repr_c_union, ReprCUnion<$t1>, ReprCUnion<$t2>);
}
};
}

// Compatibility of pointers.
test_abi_compatible!(ptr_mut, *const i32, *mut i32);
test_abi_compatible!(ptr_pointee, *const i32, *const Vec<i32>);
test_abi_compatible!(ref_mut, &i32, &mut i32);
test_abi_compatible!(ref_ptr, &i32, *const i32);
test_abi_compatible!(box_ptr, Box<i32>, *const i32);
test_abi_compatible!(nonnull_ptr, NonNull<i32>, *const i32);
test_abi_compatible!(fn_fn, fn(), fn(i32) -> i32);
test_abi_compatible_nested!(ptr_mut, *const i32, *mut i32);
test_abi_compatible_nested!(ptr_pointee, *const i32, *const Vec<i32>);
test_abi_compatible_nested!(ref_mut, &i32, &mut i32);
test_abi_compatible_nested!(ref_ptr, &i32, *const i32);
test_abi_compatible_nested!(box_ptr, Box<i32>, *const i32);
test_abi_compatible_nested!(nonnull_ptr, NonNull<i32>, *const i32);
test_abi_compatible_nested!(fn_fn, fn(), fn(i32) -> i32);

// Compatibility of integer types.
test_abi_compatible!(char_uint, char, u32);
test_abi_compatible_nested!(char_uint, char, u32);
#[cfg(target_pointer_width = "32")]
test_abi_compatible!(isize_int, isize, i32);
test_abi_compatible_nested!(isize_int, isize, i32);
#[cfg(target_pointer_width = "64")]
test_abi_compatible!(isize_int, isize, i64);
test_abi_compatible_nested!(isize_int, isize, i64);

// Compatibility of enums with `repr($int)`.
#[repr(i16)]
enum I16 {
Var1,
Var2,
}
test_abi_compatible_nested!(enum_i16, I16, i16);
#[repr(u64)]
enum U64 {
Var1,
Var2,
}
#[cfg(not(target_arch = "m68k"))]
test_abi_compatible_nested!(enum_u64, U64, u64);
// On m68k, the nested case does not work out. We don't guarantee that case
// so this is not a bug.
#[cfg(target_arch = "m68k")]
test_abi_compatible!(enum_u64, U64, u64);

// Compatibility of 1-ZST.
test_abi_compatible!(zst_unit, Zst, ());
#[cfg(not(any(target_arch = "sparc64")))]
test_abi_compatible!(zst_array, Zst, [u8; 0]);
test_abi_compatible!(nonzero_int, NonZero<i32>, i32);
test_abi_compatible_nested!(zst_unit, Zst, ());
#[cfg(not(target_arch = "sparc64"))]
test_abi_compatible_nested!(zst_array, Zst, [u8; 0]);
test_abi_compatible_nested!(nonzero_int, NonZero<i32>, i32);

// `#[repr(C)]` enums should not change ABI based on individual variant inhabitedness.
// (However, this is *not* a guarantee. We only guarantee same layout, not same ABI.)
enum Void {}
test_abi_compatible!(repr_c_enum_void, ReprCEnum<Void>, ReprCEnum<ReprCUnion<Void>>);
test_abi_compatible_nested!(repr_c_enum_void, ReprCEnum<Void>, ReprCEnum<ReprCUnion<Void>>);

// `DispatchFromDyn` relies on ABI compatibility.
// This is interesting since these types are not `repr(transparent)`. So this is not part of our
// public ABI guarantees, but is relied on by the compiler.
test_abi_compatible!(rc, Rc<i32>, *mut i32);
test_abi_compatible!(arc, Arc<i32>, *mut i32);
test_abi_compatible_nested!(rc, Rc<i32>, *mut i32);
test_abi_compatible_nested!(arc, Arc<i32>, *mut i32);

// `repr(transparent)` compatibility.
#[repr(transparent)]
Expand All @@ -299,10 +319,10 @@ macro_rules! test_transparent {
($name:ident, $t:ty) => {
mod $name {
use super::*;
test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
test_abi_compatible!(wrap3, $t, Wrapper3<$t>);
test_abi_compatible!(wrap4, $t, WrapperUnion<$t>);
test_abi_compatible_nested!(wrap1, $t, Wrapper1<$t>);
test_abi_compatible_nested!(wrap2, $t, Wrapper2<$t>);
test_abi_compatible_nested!(wrap3, $t, Wrapper3<$t>);
test_abi_compatible_nested!(wrap4, $t, WrapperUnion<$t>);
}
};
}
Expand Down Expand Up @@ -341,10 +361,10 @@ macro_rules! test_transparent_unsized {
($name:ident, $t:ty) => {
mod $name {
use super::*;
assert_abi_compatible!(wrap1, $t, Wrapper1<$t>);
assert_abi_compatible!(wrap1_reprc, ReprC1<$t>, ReprC1<Wrapper1<$t>>);
assert_abi_compatible!(wrap2, $t, Wrapper2<$t>);
assert_abi_compatible!(wrap2_reprc, ReprC1<$t>, ReprC1<Wrapper2<$t>>);
test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
test_abi_compatible!(wrap1_reprc, ReprC1<$t>, ReprC1<Wrapper1<$t>>);
test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
test_abi_compatible!(wrap2_reprc, ReprC1<$t>, ReprC1<Wrapper2<$t>>);
}
};
}
Expand All @@ -363,13 +383,13 @@ macro_rules! test_nonnull {
($name:ident, $t:ty) => {
mod $name {
use super::*;
test_abi_compatible!(option, Option<$t>, $t);
test_abi_compatible!(result_err_unit, Result<$t, ()>, $t);
test_abi_compatible!(result_ok_unit, Result<(), $t>, $t);
test_abi_compatible!(result_err_zst, Result<$t, Zst>, $t);
test_abi_compatible!(result_ok_zst, Result<Zst, $t>, $t);
test_abi_compatible!(result_err_arr, Result<$t, [i8; 0]>, $t);
test_abi_compatible!(result_ok_arr, Result<[i8; 0], $t>, $t);
test_abi_compatible_nested!(option, Option<$t>, $t);
test_abi_compatible_nested!(result_err_unit, Result<$t, ()>, $t);
test_abi_compatible_nested!(result_ok_unit, Result<(), $t>, $t);
test_abi_compatible_nested!(result_err_zst, Result<$t, Zst>, $t);
test_abi_compatible_nested!(result_ok_zst, Result<Zst, $t>, $t);
test_abi_compatible_nested!(result_err_arr, Result<$t, [i8; 0]>, $t);
test_abi_compatible_nested!(result_ok_arr, Result<[i8; 0], $t>, $t);
}
}
}
Expand Down
Loading