| // check-pass |
| // revisions: host |
| // revisions: arm |
| //[arm] compile-flags: --target arm-unknown-linux-gnueabi |
| //[arm] needs-llvm-components: arm |
| // revisions: aarch64 |
| //[aarch64] compile-flags: --target aarch64-unknown-linux-gnu |
| //[aarch64] needs-llvm-components: aarch64 |
| // revisions: s390x |
| //[s390x] compile-flags: --target s390x-unknown-linux-gnu |
| //[s390x] needs-llvm-components: systemz |
| // revisions: mips |
| //[mips] compile-flags: --target mips-unknown-linux-gnu |
| //[mips] needs-llvm-components: mips |
| // revisions: mips64 |
| //[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64 |
| //[mips64] needs-llvm-components: mips |
| // revisions: sparc |
| //[sparc] compile-flags: --target sparc-unknown-linux-gnu |
| //[sparc] needs-llvm-components: sparc |
| // revisions: sparc64 |
| //[sparc64] compile-flags: --target sparc64-unknown-linux-gnu |
| //[sparc64] needs-llvm-components: sparc |
| // revisions: powerpc64 |
| //[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu |
| //[powerpc64] needs-llvm-components: powerpc |
| // revisions: riscv |
| //[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu |
| //[riscv] needs-llvm-components: riscv |
| // revisions: loongarch64 |
| //[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu |
| //[loongarch64] needs-llvm-components: loongarch |
| // revisions: wasm |
| //[wasm] compile-flags: --target wasm32-unknown-unknown |
| //[wasm] needs-llvm-components: webassembly |
| // revisions: wasi |
| //[wasi] compile-flags: --target wasm32-wasi |
| //[wasi] needs-llvm-components: webassembly |
| // revisions: nvptx64 |
| //[nvptx64] compile-flags: --target nvptx64-nvidia-cuda |
| //[nvptx64] needs-llvm-components: nvptx |
| #![feature(rustc_attrs, unsized_fn_params, transparent_unions)] |
| #![cfg_attr(not(host), feature(no_core, lang_items), no_std, no_core)] |
| #![allow(unused, improper_ctypes_definitions, internal_features)] |
| |
| // FIXME: some targets are broken in various ways. |
| // Hence there are `cfg` throughout this test to disable parts of it on those targets. |
| // sparc64: https://github.com/rust-lang/rust/issues/115336 |
| // mips64: https://github.com/rust-lang/rust/issues/115404 |
| |
| #[cfg(host)] |
| use std::{ |
| any::Any, marker::PhantomData, mem::ManuallyDrop, num::NonZeroI32, ptr::NonNull, rc::Rc, |
| sync::Arc, |
| }; |
| |
| /// To work cross-target this test must be no_core. |
| /// This little prelude supplies what we need. |
| #[cfg(not(host))] |
| mod prelude { |
| #[lang = "sized"] |
| pub trait Sized {} |
| |
| #[lang = "receiver"] |
| pub trait Receiver {} |
| impl<T: ?Sized> Receiver for &T {} |
| impl<T: ?Sized> Receiver for &mut T {} |
| |
| #[lang = "copy"] |
| pub trait Copy: Sized {} |
| impl Copy for i32 {} |
| impl Copy for f32 {} |
| impl<T: ?Sized> Copy for &T {} |
| impl<T: ?Sized> Copy for *const T {} |
| impl<T: ?Sized> Copy for *mut T {} |
| |
| #[lang = "clone"] |
| pub trait Clone: Sized { |
| fn clone(&self) -> Self; |
| } |
| |
| #[lang = "phantom_data"] |
| pub struct PhantomData<T: ?Sized>; |
| impl<T: ?Sized> Copy for PhantomData<T> {} |
| |
| #[lang = "unsafe_cell"] |
| #[repr(transparent)] |
| pub struct UnsafeCell<T: ?Sized> { |
| value: T, |
| } |
| |
| pub trait Any: 'static {} |
| |
| pub enum Option<T> { |
| None, |
| Some(T), |
| } |
| impl<T: Copy> Copy for Option<T> {} |
| |
| pub enum Result<T, E> { |
| Ok(T), |
| Err(E), |
| } |
| impl<T: Copy, E: Copy> Copy for Result<T, E> {} |
| |
| #[lang = "manually_drop"] |
| #[repr(transparent)] |
| pub struct ManuallyDrop<T: ?Sized> { |
| value: T, |
| } |
| impl<T: Copy + ?Sized> Copy for ManuallyDrop<T> {} |
| |
| #[repr(transparent)] |
| #[rustc_layout_scalar_valid_range_start(1)] |
| #[rustc_nonnull_optimization_guaranteed] |
| pub struct NonNull<T: ?Sized> { |
| pointer: *const T, |
| } |
| impl<T: ?Sized> Copy for NonNull<T> {} |
| |
| #[repr(transparent)] |
| #[rustc_layout_scalar_valid_range_start(1)] |
| #[rustc_nonnull_optimization_guaranteed] |
| pub struct NonZeroI32(i32); |
| |
| // This just stands in for a non-trivial type. |
| pub struct Vec<T> { |
| ptr: NonNull<T>, |
| cap: usize, |
| len: usize, |
| } |
| |
| pub struct Unique<T: ?Sized> { |
| pub pointer: NonNull<T>, |
| pub _marker: PhantomData<T>, |
| } |
| |
| pub struct Global; |
| |
| #[lang = "owned_box"] |
| pub struct Box<T: ?Sized, A = Global>(Unique<T>, A); |
| |
| #[repr(C)] |
| struct RcBox<T: ?Sized> { |
| strong: UnsafeCell<usize>, |
| weak: UnsafeCell<usize>, |
| value: T, |
| } |
| pub struct Rc<T: ?Sized, A = Global> { |
| ptr: NonNull<RcBox<T>>, |
| phantom: PhantomData<RcBox<T>>, |
| alloc: A, |
| } |
| |
| #[repr(C, align(8))] |
| struct AtomicUsize(usize); |
| #[repr(C)] |
| struct ArcInner<T: ?Sized> { |
| strong: AtomicUsize, |
| weak: AtomicUsize, |
| data: T, |
| } |
| pub struct Arc<T: ?Sized, A = Global> { |
| ptr: NonNull<ArcInner<T>>, |
| phantom: PhantomData<ArcInner<T>>, |
| alloc: A, |
| } |
| } |
| #[cfg(not(host))] |
| use prelude::*; |
| |
| macro_rules! assert_abi_compatible { |
| ($name:ident, $t1:ty, $t2:ty) => { |
| mod $name { |
| use super::*; |
| // Declaring a `type` doesn't even check well-formedness, so we also declare a function. |
| fn check_wf(_x: $t1, _y: $t2) {} |
| // Test argument and return value, `Rust` and `C` ABIs. |
| #[rustc_abi(assert_eq)] |
| type TestRust = (fn($t1) -> $t1, fn($t2) -> $t2); |
| #[rustc_abi(assert_eq)] |
| type TestC = (extern "C" fn($t1) -> $t1, extern "C" fn($t2) -> $t2); |
| } |
| }; |
| } |
| |
| struct Zst; |
| impl Copy for Zst {} |
| impl Clone for Zst { |
| fn clone(&self) -> Self { |
| Zst |
| } |
| } |
| |
| #[repr(C)] |
| struct ReprC1<T: ?Sized>(T); |
| #[repr(C)] |
| struct ReprC2Int<T>(i32, T); |
| #[repr(C)] |
| struct ReprC2Float<T>(f32, T); |
| #[repr(C)] |
| struct ReprC4<T>(T, Vec<i32>, Zst, T); |
| #[repr(C)] |
| struct ReprC4Mixed<T>(T, f32, i32, T); |
| #[repr(C)] |
| enum ReprCEnum<T> { |
| Variant1, |
| Variant2(T), |
| } |
| #[repr(C)] |
| union ReprCUnion<T> { |
| nothing: (), |
| something: ManuallyDrop<T>, |
| } |
| |
| macro_rules! test_abi_compatible { |
| ($name:ident, $t1:ty, $t2:ty) => { |
| mod $name { |
| use super::*; |
| assert_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>); |
| } |
| }; |
| } |
| |
| // Compatibility of pointers is probably de-facto guaranteed, |
| // but that does not seem to be documented. |
| 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); |
| |
| // Some further guarantees we will likely (have to) make. |
| 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, NonZeroI32, i32); |
| |
| // `DispatchFromDyn` relies on ABI compatibility. |
| // This is interesting since these types are not `repr(transparent)`. |
| test_abi_compatible!(rc, Rc<i32>, *mut i32); |
| test_abi_compatible!(arc, Arc<i32>, *mut i32); |
| |
| // `repr(transparent)` compatibility. |
| #[repr(transparent)] |
| struct Wrapper1<T: ?Sized>(T); |
| #[repr(transparent)] |
| struct Wrapper2<T: ?Sized>((), Zst, T); |
| #[repr(transparent)] |
| struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>); |
| #[repr(transparent)] |
| union WrapperUnion<T> { |
| nothing: (), |
| something: ManuallyDrop<T>, |
| } |
| |
| 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_transparent!(simple, i32); |
| test_transparent!(reference, &'static i32); |
| test_transparent!(zst, Zst); |
| test_transparent!(unit, ()); |
| test_transparent!(enum_, Option<i32>); |
| test_transparent!(enum_niched, Option<&'static i32>); |
| #[cfg(not(any(target_arch = "mips64", target_arch = "sparc64")))] |
| mod tuples { |
| use super::*; |
| // mixing in some floats since they often get special treatment |
| test_transparent!(pair, (i32, f32)); |
| // chosen to fit into 64bit |
| test_transparent!(triple, (i8, i16, f32)); |
| // Pure-float types that are not ScalarPair seem to be tricky. |
| test_transparent!(triple_f32, (f32, f32, f32)); |
| test_transparent!(triple_f64, (f64, f64, f64)); |
| // and also something that's larger than 2 pointers |
| test_transparent!(tuple, (i32, f32, i64, f64)); |
| } |
| // Some targets have special rules for arrays. |
| #[cfg(not(any(target_arch = "mips64", target_arch = "sparc64")))] |
| mod arrays { |
| use super::*; |
| test_transparent!(empty_array, [u32; 0]); |
| test_transparent!(empty_1zst_array, [u8; 0]); |
| test_transparent!(small_array, [i32; 2]); // chosen to fit into 64bit |
| test_transparent!(large_array, [i32; 16]); |
| } |
| |
| // Some tests with unsized types (not all wrappers are compatible with that). |
| 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>>); |
| } |
| }; |
| } |
| |
| #[cfg(not(any(target_arch = "mips64", target_arch = "sparc64")))] |
| mod unsized_ { |
| use super::*; |
| test_transparent_unsized!(str_, str); |
| test_transparent_unsized!(slice, [u8]); |
| test_transparent_unsized!(dyn_trait, dyn Any); |
| } |
| |
| // RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>. |
| 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_nonnull!(ref_, &i32); |
| test_nonnull!(mut_, &mut i32); |
| test_nonnull!(ref_unsized, &[i32]); |
| test_nonnull!(mut_unsized, &mut [i32]); |
| test_nonnull!(fn_, fn()); |
| test_nonnull!(nonnull, NonNull<i32>); |
| test_nonnull!(nonnull_unsized, NonNull<dyn Any>); |
| test_nonnull!(non_zero, NonZeroI32); |
| |
| fn main() {} |