blob: cd48bd2accb27d8acc3cd5aee8c156a968632d1f [file] [log] [blame]
#![feature(never_type)]
use std::rc::Rc;
use std::{mem, num, ptr};
#[derive(Copy, Clone, Default)]
struct Zst;
#[repr(transparent)]
#[derive(Copy, Clone)]
struct Wrapper<T>(T);
fn id<T>(x: T) -> T {
x
}
#[derive(Copy, Clone)]
enum Either<T, U> {
Left(T),
Right(U),
}
#[derive(Copy, Clone)]
enum Either2<T, U> {
Left(T),
#[allow(unused)]
Right(U, ()),
}
fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) {
fn id<T>(x: T) -> T {
x
}
extern "C" fn id_c<T>(x: T) -> T {
x
}
// This checks ABI compatibility both for arguments and return values,
// in both directions.
let f: fn(T) -> T = id;
let f: fn(U) -> U = unsafe { std::mem::transmute(f) };
let _val = f(u.clone());
let f: fn(U) -> U = id;
let f: fn(T) -> T = unsafe { std::mem::transmute(f) };
let _val = f(t.clone());
// And then we do the same for `extern "C"`.
let f: extern "C" fn(T) -> T = id_c;
let f: extern "C" fn(U) -> U = unsafe { std::mem::transmute(f) };
let _val = f(u);
let f: extern "C" fn(U) -> U = id_c;
let f: extern "C" fn(T) -> T = unsafe { std::mem::transmute(f) };
let _val = f(t);
}
/// Ensure that `T` is compatible with various repr(transparent) wrappers around `T`.
fn test_abi_newtype<T: Copy + Default>() {
#[repr(transparent)]
#[derive(Copy, Clone)]
struct Wrapper2<T>(T, ());
#[repr(transparent)]
#[derive(Copy, Clone)]
struct Wrapper2a<T>((), T);
#[repr(transparent)]
#[derive(Copy, Clone)]
struct Wrapper3<T>(Zst, T, [u8; 0]);
let t = T::default();
test_abi_compat(t, Wrapper(t));
test_abi_compat(t, Wrapper2(t, ()));
test_abi_compat(t, Wrapper2a((), t));
test_abi_compat(t, Wrapper3(Zst, t, []));
test_abi_compat(t, mem::MaybeUninit::new(t)); // MaybeUninit is `repr(transparent)`
}
fn main() {
// Here we check some of the guaranteed ABI compatibilities:
// - Different integer types of the same size and sign.
if cfg!(target_pointer_width = "32") {
test_abi_compat(0usize, 0u32);
test_abi_compat(0isize, 0i32);
} else {
test_abi_compat(0usize, 0u64);
test_abi_compat(0isize, 0i64);
}
test_abi_compat(42u32, num::NonZero::new(1u32).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));
test_abi_compat(&(), ptr::NonNull::<()>::dangling());
// - Reference/pointer types with different but sized pointees.
test_abi_compat(&0u32, &([true; 4], [0u32; 0]));
// - `fn` types
test_abi_compat(main as fn(), id::<i32> as fn(i32) -> i32);
// - 1-ZST
test_abi_compat((), [0u8; 0]);
// Guaranteed null-pointer-layout optimizations:
// - Guaranteed Option<X> 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::NonZero::new(1u32).unwrap()));
test_abi_compat(&0u32 as *const u32, Some(Wrapper(&0u32)));
test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1u32).unwrap())));
// - Guaranteed Result<X, ZST1> does the same as Option<X> (RFC 3391)
test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(&0u32));
test_abi_compat(&0u32 as *const u32, Result::<_, !>::Ok(&0u32));
test_abi_compat(main as fn(), Result::<_, ()>::Ok(main as fn()));
test_abi_compat(0u32, Result::<_, ()>::Ok(num::NonZeroU32::new(1).unwrap()));
test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(Wrapper(&0u32)));
test_abi_compat(0u32, Result::<_, ()>::Ok(Wrapper(num::NonZeroU32::new(1).unwrap())));
// - Guaranteed Result<ZST1, X> also does the same as Option<X> (RFC 3391)
test_abi_compat(&0u32 as *const u32, Result::<(), _>::Err(&0u32));
test_abi_compat(main as fn(), Result::<(), _>::Err(main as fn()));
test_abi_compat(0u32, Result::<(), _>::Err(num::NonZeroU32::new(1).unwrap()));
test_abi_compat(&0u32 as *const u32, Result::<(), _>::Err(Wrapper(&0u32)));
test_abi_compat(0u32, Result::<(), _>::Err(Wrapper(num::NonZeroU32::new(1).unwrap())));
// - Guaranteed null-pointer-optimizations for custom option-like types
test_abi_compat(&0u32 as *const u32, Either::<_, ()>::Left(&0u32));
test_abi_compat(&0u32 as *const u32, Either::<_, !>::Left(&0u32));
test_abi_compat(&0u32 as *const u32, Either::<(), _>::Right(&0u32));
test_abi_compat(&0u32 as *const u32, Either::<!, _>::Right(&0u32));
test_abi_compat(&0u32 as *const u32, Either2::<_, ()>::Left(&0u32));
test_abi_compat(&0u32 as *const u32, Either2::<_, [u8; 0]>::Left(&0u32));
// These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible
// with the wrapped field.
test_abi_newtype::<()>();
test_abi_newtype::<Zst>();
test_abi_newtype::<u32>();
test_abi_newtype::<f32>();
test_abi_newtype::<(u8, u16, f32)>();
test_abi_newtype::<[u8; 0]>();
test_abi_newtype::<[u32; 0]>();
test_abi_newtype::<[u32; 2]>();
test_abi_newtype::<[u32; 32]>();
test_abi_newtype::<Option<i32>>();
test_abi_newtype::<Option<num::NonZero<u32>>>();
// 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);
}