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:
commit
deb708af12
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user