Skip to content

Commit

Permalink
Auto merge of rust-lang#118032 - RalfJung:char-u32, r=Mark-Simulacrum
Browse files Browse the repository at this point in the history
guarantee that char and u32 are ABI-compatible

In rust-lang#116894 we added a guarantee that `char` has the same alignment as `u32`, but there is still one axis where these types could differ: function call ABI. So let's nail that down as well: in a function signature, `char` and `u32` are completely equivalent.

This is a new stable guarantee, so it will need t-lang approval.
  • Loading branch information
bors committed Dec 11, 2023
2 parents 1cb200c + b4f3f2a commit e299752
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 5 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_const_eval/src/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
};
Expand Down
9 changes: 5 additions & 4 deletions library/core/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`.
Expand Down
6 changes: 5 additions & 1 deletion 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,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));
Expand All @@ -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()));
Expand All @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/abi/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,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")))]
Expand Down

0 comments on commit e299752

Please sign in to comment.