From b4f3f2aeacfc629e57eca6cabc27a00cf4cf2375 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 18 Nov 2023 08:24:02 +0100 Subject: [PATCH] guarantee that char and u32 are ABI-compatible --- compiler/rustc_const_eval/src/interpret/terminator.rs | 2 ++ library/core/src/primitive_docs.rs | 9 +++++---- src/tools/miri/tests/pass/function_calls/abi_compat.rs | 6 +++++- tests/ui/abi/compatibility.rs | 7 +++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index b54c668145638..e85ee437fadbf 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -384,10 +384,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Compatible integer types (in particular, usize vs ptr-sized-u32/u64). + // `char` counts as `u32.` let int_ty = |ty: Ty<'tcx>| { Some(match ty.kind() { 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), _ => return None, }) }; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index a7e20407cece4..c5091c1dcb296 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -330,7 +330,7 @@ mod prim_never {} /// the future ("reserved"); some will never be a character ("noncharacters"); and some may be given /// different meanings by different users ("private use"). /// -/// `char` is guaranteed to have the same size and alignment as `u32` on all +/// `char` is guaranteed to have the same size, alignment, and function call ABI as `u32` on all /// platforms. /// ``` /// use std::alloc::Layout; @@ -1557,6 +1557,7 @@ 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. +/// - `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 /// their signature. (This means you can pass `fn()` to a function expecting `fn(i32)`, and the @@ -1585,9 +1586,9 @@ mod prim_ref {} /// since it is not portable and not a stable guarantee. /// /// Noteworthy cases of types *not* being ABI-compatible in general are: -/// * `bool` vs `u8`, and `i32` vs `u32`: on some targets, the calling conventions for these types -/// differ in terms of what they guarantee for the remaining bits in the register that are not -/// used by the value. +/// * `bool` vs `u8`, `i32` vs `u32`, `char` vs `i32`: on some targets, the calling conventions for +/// these types differ in terms of what they guarantee for the remaining bits in the register that +/// are not used by the value. /// * `i32` vs `f32` are not compatible either, as has already been mentioned above. /// * `struct Foo(u32)` and `u32` are not compatible (without `repr(transparent)`) since structs are /// aggregate types and often passed in a different way than primitives like `i32`. 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 b24fe56cad695..14fd2d333d4fb 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,8 @@ fn main() { test_abi_compat(0isize, 0i64); } test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap()); + // - `char` and `u32`. + test_abi_compat(42u32, 'x'); // - Reference/pointer types with the same pointee. test_abi_compat(&0u32, &0u32 as *const u32); test_abi_compat(&mut 0u32 as *mut u32, Box::new(0u32)); @@ -81,7 +83,7 @@ fn main() { test_abi_compat(main as fn(), id:: as fn(i32) -> i32); // - 1-ZST test_abi_compat((), [0u8; 0]); - // - Guaranteed null-pointer-optimizations. + // - Guaranteed null-pointer-optimizations (RFC 3391). test_abi_compat(&0u32 as *const u32, Some(&0u32)); test_abi_compat(main as fn(), Some(main as fn())); test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap())); @@ -103,6 +105,8 @@ fn main() { test_abi_newtype::>(); // Extra test for assumptions made by arbitrary-self-dyn-receivers. + // 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. let rc = Rc::new(0); let rc_ptr: *mut i32 = unsafe { mem::transmute_copy(&rc) }; test_abi_compat(rc, rc_ptr); diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 53e1eff9d72b7..8066dca91cf4c 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -240,6 +240,13 @@ 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); +// Compatibility of integer types. +test_abi_compatible!(char_uint, char, u32); +#[cfg(target_pointer_width = "32")] +test_abi_compatible!(isize_int, isize, i32); +#[cfg(target_pointer_width = "64")] +test_abi_compatible!(isize_int, isize, i64); + // Compatibility of 1-ZST. test_abi_compatible!(zst_unit, Zst, ()); #[cfg(not(any(target_arch = "sparc64")))]