diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 2c5147678e8cb..3a362dfce0877 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -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, }) }; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 09ebef89fb0c2..d975a3d783775 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -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 diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs index 0cfcd532ff4b2..ddb621776d34c 100644 --- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs +++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs @@ -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. diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index ca78604edd866..1ef39ffddc728 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -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::*; @@ -232,55 +232,75 @@ union ReprCUnion { something: ManuallyDrop, } -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); -test_abi_compatible!(ref_mut, &i32, &mut i32); -test_abi_compatible!(ref_ptr, &i32, *const i32); -test_abi_compatible!(box_ptr, Box, *const i32); -test_abi_compatible!(nonnull_ptr, NonNull, *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); +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, *const i32); +test_abi_compatible_nested!(nonnull_ptr, NonNull, *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); +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); // `#[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, ReprCEnum>); +test_abi_compatible_nested!(repr_c_enum_void, ReprCEnum, ReprCEnum>); // `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, *mut i32); -test_abi_compatible!(arc, Arc, *mut i32); +test_abi_compatible_nested!(rc, Rc, *mut i32); +test_abi_compatible_nested!(arc, Arc, *mut i32); // `repr(transparent)` compatibility. #[repr(transparent)] @@ -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>); } }; } @@ -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>); - assert_abi_compatible!(wrap2, $t, Wrapper2<$t>); - assert_abi_compatible!(wrap2_reprc, ReprC1<$t>, ReprC1>); + test_abi_compatible!(wrap1, $t, Wrapper1<$t>); + test_abi_compatible!(wrap1_reprc, ReprC1<$t>, ReprC1>); + test_abi_compatible!(wrap2, $t, Wrapper2<$t>); + test_abi_compatible!(wrap2_reprc, ReprC1<$t>, ReprC1>); } }; } @@ -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, $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, $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); } } }