982b49494e
This is to make the diff when stabilizing it easier to review.
53 lines
1.6 KiB
Rust
53 lines
1.6 KiB
Rust
// run-pass
|
|
|
|
#![allow(dead_code)]
|
|
|
|
// Tests that unions aren't subject to unsafe non-zero/niche-filling optimizations.
|
|
//
|
|
// For example, if a union `U` can contain both a `&T` and a `*const T`, there's definitely no
|
|
// bit-value that an `Option<U>` could reuse as `None`; this test makes sure that isn't done.
|
|
//
|
|
// Secondly, this tests the status quo (not a guarantee; subject to change!) to not apply such
|
|
// optimizations to types containing unions even if they're theoretically possible. (discussion:
|
|
// https://github.com/rust-lang/rust/issues/36394)
|
|
//
|
|
// Notably this nails down part of the behavior that `MaybeUninit` assumes: that an
|
|
// `Option<MaybeUninit<&u8>>` does not take advantage of non-zero optimization, and thus is a safe
|
|
// construct.
|
|
|
|
use std::mem::{size_of, transmute};
|
|
|
|
union U1<A: Copy> {
|
|
a: A,
|
|
}
|
|
|
|
union U2<A: Copy, B: Copy> {
|
|
a: A,
|
|
b: B,
|
|
}
|
|
|
|
// Option<E> uses a value other than 0 and 1 as None
|
|
#[derive(Clone,Copy)]
|
|
enum E {
|
|
A = 0,
|
|
B = 1,
|
|
}
|
|
|
|
fn main() {
|
|
// Unions do not participate in niche-filling/non-zero optimization...
|
|
assert!(size_of::<Option<U2<&u8, u8>>>() > size_of::<U2<&u8, u8>>());
|
|
assert!(size_of::<Option<U2<&u8, ()>>>() > size_of::<U2<&u8, ()>>());
|
|
assert!(size_of::<Option<U2<u8, E>>>() > size_of::<U2<u8, E>>());
|
|
|
|
// ...even when theoretically possible:
|
|
assert!(size_of::<Option<U1<&u8>>>() > size_of::<U1<&u8>>());
|
|
assert!(size_of::<Option<U2<&u8, &u8>>>() > size_of::<U2<&u8, &u8>>());
|
|
|
|
// The unused bits of the () variant can have any value.
|
|
let zeroed: U2<&u8, ()> = unsafe { transmute(std::ptr::null::<u8>()) };
|
|
|
|
if let None = Some(zeroed) {
|
|
panic!()
|
|
}
|
|
}
|