about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2023-11-18 08:24:02 +0100
committerRalf Jung <post@ralfj.de>2023-11-18 08:24:02 +0100
commitb4f3f2aeacfc629e57eca6cabc27a00cf4cf2375 (patch)
treeb666799ef256be6a2d716afc8ff5e91ad243ef25
parente6dade96f4f16b2c0f621d657130b90b4bb519a2 (diff)
downloadrust-b4f3f2aeacfc629e57eca6cabc27a00cf4cf2375.tar.gz
rust-b4f3f2aeacfc629e57eca6cabc27a00cf4cf2375.zip
guarantee that char and u32 are ABI-compatible
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs2
-rw-r--r--library/core/src/primitive_docs.rs9
-rw-r--r--src/tools/miri/tests/pass/function_calls/abi_compat.rs6
-rw-r--r--tests/ui/abi/compatibility.rs7
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 b54c6681456..e85ee437fad 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 a7e20407cec..c5091c1dcb2 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 b24fe56cad6..14fd2d333d4 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::<i32> 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::<Option<num::NonZeroU32>>();
 
     // 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 53e1eff9d72..8066dca91cf 100644
--- a/tests/ui/abi/compatibility.rs
+++ b/tests/ui/abi/compatibility.rs
@@ -240,6 +240,13 @@ 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);
 
+// 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")))]