| //@ check-pass | |
| //@ add-minicore | |
| //@ revisions: host | |
| //@ revisions: i686 | |
| //@[i686] compile-flags: --target i686-unknown-linux-gnu | |
| //@[i686] needs-llvm-components: x86 | |
| //@ revisions: x86-64 | |
| //@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu | |
| //@[x86-64] needs-llvm-components: x86 | |
| //@ revisions: x86-64-win | |
| //@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc | |
| //@[x86-64-win] needs-llvm-components: x86 | |
| //@ revisions: arm | |
| //@[arm] compile-flags: --target arm-unknown-linux-gnueabi | |
| //@[arm] needs-llvm-components: arm | |
| //@ revisions: thumb | |
| //@[thumb] compile-flags: --target thumbv8m.main-none-eabi | |
| //@[thumb] 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: powerpc | |
| //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu | |
| //@[powerpc] needs-llvm-components: powerpc | |
| //@ revisions: powerpc64 | |
| //@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu | |
| //@[powerpc64] needs-llvm-components: powerpc | |
| //@ revisions: aix | |
| //@[aix] compile-flags: --target powerpc64-ibm-aix | |
| //@[aix] needs-llvm-components: powerpc | |
| //@ revisions: riscv | |
| //@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu | |
| //@[riscv] needs-llvm-components: riscv | |
| //@ revisions: loongarch32 | |
| //@[loongarch32] compile-flags: --target loongarch32-unknown-none | |
| //@[loongarch32] needs-llvm-components: loongarch | |
| //@ 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: wasip1 | |
| //@[wasip1] compile-flags: --target wasm32-wasip1 | |
| //@[wasip1] needs-llvm-components: webassembly | |
| //@ revisions: bpf | |
| //@[bpf] compile-flags: --target bpfeb-unknown-none | |
| //@[bpf] needs-llvm-components: bpf | |
| //@ revisions: m68k | |
| //@[m68k] compile-flags: --target m68k-unknown-linux-gnu | |
| //@[m68k] needs-llvm-components: m68k | |
| //@ revisions: csky | |
| //@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2 | |
| //@[csky] needs-llvm-components: csky | |
| //@ revisions: nvptx64 | |
| //@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda | |
| //@[nvptx64] needs-llvm-components: nvptx | |
| //@ ignore-backends: gcc | |
| #![feature(no_core, rustc_attrs, lang_items)] | |
| #![feature(unsized_fn_params, transparent_unions)] | |
| #![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. | |
| // mips64: https://github.com/rust-lang/rust/issues/115404 | |
| extern crate minicore; | |
| use minicore::*; | |
| /// To work cross-target this test must be no_core. This little prelude supplies what we need. | |
| /// | |
| /// Note that `minicore` provides a very minimal subset of `core` items (not yet complete). This | |
| /// prelude contains `alloc` and non-`core` (but in `std`) items that minicore does not stub out. | |
| mod prelude { | |
| use minicore::*; | |
| // Trait stub, no `type_id` method. | |
| pub trait Any: 'static {} | |
| #[lang = "clone"] | |
| pub trait Clone: Sized { | |
| fn clone(&self) -> Self; | |
| } | |
| // This just stands in for a non-trivial type. | |
| pub struct Vec<T> { | |
| ptr: NonNull<T>, | |
| cap: usize, | |
| len: usize, | |
| } | |
| #[lang = "global_alloc_ty"] | |
| pub struct Global; | |
| #[lang = "owned_box"] | |
| pub struct Box<T: ?Sized, A = Global>(Unique<T>, A); | |
| #[repr(C)] | |
| struct RcInner<T: ?Sized> { | |
| strong: UnsafeCell<usize>, | |
| weak: UnsafeCell<usize>, | |
| value: T, | |
| } | |
| pub struct Rc<T: ?Sized, A = Global> { | |
| ptr: NonNull<RcInner<T>>, | |
| phantom: PhantomData<RcInner<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, | |
| } | |
| } | |
| use prelude::*; | |
| macro_rules! test_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 | |
| } | |
| } | |
| enum Either<T, U> { | |
| Left(T), | |
| Right(U), | |
| } | |
| enum Either2<T, U> { | |
| Left(T), | |
| Right(U, ()), | |
| } | |
| #[repr(C)] | |
| enum ReprCEnum<T> { | |
| Variant1, | |
| Variant2(T), | |
| } | |
| #[repr(C)] | |
| union ReprCUnion<T> { | |
| nothing: (), | |
| something: ManuallyDrop<T>, | |
| } | |
| // Compatibility of pointers. | |
| 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); | |
| // 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, ()); | |
| test_abi_compatible!(zst_array, Zst, [u8; 0]); | |
| test_abi_compatible!(nonzero_int, NonZero<i32>, 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<Void>, ReprCEnum<ReprCUnion<Void>>); | |
| // `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<i32>, *mut i32); | |
| test_abi_compatible!(arc, Arc<i32>, *mut i32); | |
| // `repr(transparent)` compatibility. | |
| #[repr(transparent)] | |
| struct TransparentWrapper1<T: ?Sized>(T); | |
| #[repr(transparent)] | |
| struct TransparentWrapper2<T: ?Sized>((), Zst, T); | |
| #[repr(transparent)] | |
| struct TransparentWrapper3<T>(T, [u8; 0], PhantomData<u64>); | |
| #[repr(transparent)] | |
| union TransparentWrapperUnion<T> { | |
| nothing: (), | |
| something: ManuallyDrop<T>, | |
| } | |
| macro_rules! test_transparent { | |
| ($name:ident, $t:ty) => { | |
| mod $name { | |
| use super::*; | |
| test_abi_compatible!(wrap1, $t, TransparentWrapper1<$t>); | |
| test_abi_compatible!(wrap2, $t, TransparentWrapper2<$t>); | |
| test_abi_compatible!(wrap3, $t, TransparentWrapper3<$t>); | |
| test_abi_compatible!(wrap4, $t, TransparentWrapperUnion<$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")))] | |
| 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. | |
| 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::*; | |
| test_abi_compatible!(wrap1, $t, TransparentWrapper1<$t>); | |
| test_abi_compatible!(wrap2, $t, TransparentWrapper2<$t>); | |
| } | |
| }; | |
| } | |
| // NOTE: non-rustic ABIs do not support unsized types: they are skipped during ABI generation, and | |
| // will trigger an error if they make it to rustc_monomorphize/src/mono_checks/abi_check.rs | |
| mod unsized_ { | |
| use super::*; | |
| test_transparent_unsized!(str_, str); | |
| test_transparent_unsized!(slice, [u8]); | |
| test_transparent_unsized!(slice_with_prefix, (usize, [u8])); | |
| test_transparent_unsized!(dyn_trait, dyn Any); | |
| } | |
| // RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>, including the | |
| // extension ratified at <https://github.com/rust-lang/rust/pull/130628#issuecomment-2402761599>. | |
| 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_abi_compatible!(result_err_void, Result<$t, Void>, $t); | |
| test_abi_compatible!(result_ok_void, Result<Void, $t>, $t); | |
| test_abi_compatible!(either_err_zst, Either<$t, Zst>, $t); | |
| test_abi_compatible!(either_ok_zst, Either<Zst, $t>, $t); | |
| test_abi_compatible!(either2_err_zst, Either2<$t, Zst>, $t); | |
| test_abi_compatible!(either2_err_arr, Either2<$t, [i8; 0]>, $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, NonZero<i32>); | |
| fn main() {} |