Auto merge of #115678 - RalfJung:abi-compat-test, r=petrochenkov

test ABI compatibility for some unsized types as well

and test for what `DispatchFromDyn` needs.

Also I ran this on a whole bunch of targets via Miri and added enough `cfg` to make it all work, as documentation for what does and doesn't currently work. (Most of those targets do not have their tests run on CI anyway.)

Here's the shell rune I used for that:
```
for TARGET in x86_64-unknown-linux-gnu x86_64-pc-windows-gnu aarch64-unknown-linux-gnu s390x-unknown-linux-gnu mips64-unknown-linux-gnuabi64 sparc64-unknown-linux-gnu  powerpc64-unknown-linux-gnu powerpc64le-unknown-linux-gnu riscv64gc-unknown-linux-gnu loongarch64-unknown-linux-gnu wasm32-unknown-unknown; do
   BOOTSTRAP_SKIP_TARGET_SANITY=1 ./x.py run miri --stage 0 --args tests/ui/abi/compatibility.rs --target $TARGET;
done
```
This commit is contained in:
bors 2023-09-12 07:11:48 +00:00
commit deb708af12

View File

@ -1,15 +1,24 @@
// check-pass // check-pass
#![feature(rustc_attrs, transparent_unions)] #![feature(rustc_attrs, unsized_fn_params, transparent_unions)]
#![allow(unused, improper_ctypes_definitions)] #![allow(unused, improper_ctypes_definitions, internal_features)]
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::num::NonZeroI32; use std::num::NonZeroI32;
use std::ptr::NonNull; use std::ptr::NonNull;
// FIXME: a bunch of 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
// riscv64: https://github.com/rust-lang/rust/issues/115481
// loongarch64: https://github.com/rust-lang/rust/issues/115509
macro_rules! assert_abi_compatible { macro_rules! assert_abi_compatible {
($name:ident, $t1:ty, $t2:ty) => { ($name:ident, $t1:ty, $t2:ty) => {
mod $name { mod $name {
use super::*; 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. // Test argument and return value, `Rust` and `C` ABIs.
#[rustc_abi(assert_eq)] #[rustc_abi(assert_eq)]
type TestRust = (fn($t1) -> $t1, fn($t2) -> $t2); type TestRust = (fn($t1) -> $t1, fn($t2) -> $t2);
@ -23,7 +32,7 @@ mod $name {
struct Zst; struct Zst;
#[repr(C)] #[repr(C)]
struct ReprC1<T>(T); struct ReprC1<T: ?Sized>(T);
#[repr(C)] #[repr(C)]
struct ReprC2Int<T>(i32, T); struct ReprC2Int<T>(i32, T);
#[repr(C)] #[repr(C)]
@ -72,14 +81,20 @@ mod $name {
// Some further guarantees we will likely (have to) make. // Some further guarantees we will likely (have to) make.
test_abi_compatible!(zst_unit, Zst, ()); test_abi_compatible!(zst_unit, Zst, ());
#[cfg(not(any(target_arch = "sparc64")))]
test_abi_compatible!(zst_array, Zst, [u8; 0]); test_abi_compatible!(zst_array, Zst, [u8; 0]);
test_abi_compatible!(nonzero_int, NonZeroI32, i32); 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, std::rc::Rc<i32>, *mut i32);
test_abi_compatible!(arc, std::sync::Arc<i32>, *mut i32);
// `repr(transparent)` compatibility. // `repr(transparent)` compatibility.
#[repr(transparent)] #[repr(transparent)]
struct Wrapper1<T>(T); struct Wrapper1<T: ?Sized>(T);
#[repr(transparent)] #[repr(transparent)]
struct Wrapper2<T>((), Zst, T); struct Wrapper2<T: ?Sized>((), Zst, T);
#[repr(transparent)] #[repr(transparent)]
struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>); struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>);
#[repr(transparent)] #[repr(transparent)]
@ -95,6 +110,7 @@ mod $name {
test_abi_compatible!(wrap1, $t, Wrapper1<$t>); test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
test_abi_compatible!(wrap2, $t, Wrapper2<$t>); test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
test_abi_compatible!(wrap3, $t, Wrapper3<$t>); test_abi_compatible!(wrap3, $t, Wrapper3<$t>);
#[cfg(not(any(target_arch = "riscv64", target_arch = "loongarch64")))]
test_abi_compatible!(wrap4, $t, WrapperUnion<$t>); test_abi_compatible!(wrap4, $t, WrapperUnion<$t>);
} }
}; };
@ -104,17 +120,51 @@ mod $name {
test_transparent!(reference, &'static i32); test_transparent!(reference, &'static i32);
test_transparent!(zst, Zst); test_transparent!(zst, Zst);
test_transparent!(unit, ()); test_transparent!(unit, ());
test_transparent!(pair, (i32, f32)); // mixing in some floats since they often get special treatment
test_transparent!(triple, (i8, i16, f32)); // chosen to fit into 64bit
test_transparent!(triple_f32, (f32, f32, f32)); // homogeneous case
test_transparent!(triple_f64, (f64, f64, f64));
test_transparent!(tuple, (i32, f32, i64, f64));
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]);
test_transparent!(enum_, Option<i32>); test_transparent!(enum_, Option<i32>);
test_transparent!(enum_niched, Option<&'static 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 std::any::Any);
}
// RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>. // RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>.
macro_rules! test_nonnull { macro_rules! test_nonnull {