| //@ run-pass | |
| //@ revisions: normal randomize-layout | |
| //@ [randomize-layout]compile-flags: -Zrandomize-layout -Zlayout-seed=2 | |
| #![feature(offset_of_enum)] | |
| use std::ptr; | |
| // these types only have their field offsets taken, they're never constructed | |
| #[allow(dead_code)] | |
| pub struct Foo<T>(u32, T, u8); | |
| #[allow(dead_code)] | |
| pub struct Wrapper<T>(T); | |
| #[repr(transparent)] | |
| #[allow(dead_code)] | |
| pub struct TransparentWrapper(u16); | |
| const _: () = { | |
| // Behavior of the current non-randomized implementation, not guaranteed | |
| #[cfg(not(randomize_layout))] | |
| assert!(std::mem::offset_of!(Foo::<u16>, 1) == std::mem::offset_of!(Foo::<Wrapper<u16>>, 1)); | |
| // under randomization Foo<T> != Foo<U> | |
| #[cfg(randomize_layout)] | |
| assert!(std::mem::offset_of!(Foo::<u16>, 1) != std::mem::offset_of!(Foo::<Wrapper<u16>>, 1)); | |
| // Even transparent wrapper inner types get a different layout since associated type | |
| // specialization could result in the outer type behaving differently depending on the exact | |
| // inner type. | |
| #[cfg(randomize_layout)] | |
| assert!( | |
| std::mem::offset_of!(Foo::<u16>, 1) != std::mem::offset_of!(Foo::<TransparentWrapper>, 1) | |
| ); | |
| // Currently all fn pointers are treated interchangably even with randomization. Not guaranteed. | |
| // Associated type specialization could also break this. | |
| assert!( | |
| std::mem::offset_of!(Foo::<fn(u32)>, 1) == std::mem::offset_of!(Foo::<fn() -> usize>, 1) | |
| ); | |
| // But subtype coercions must always result in the same layout. | |
| assert!( | |
| std::mem::offset_of!(Foo::<fn(&u32)>, 1) == std::mem::offset_of!(Foo::<fn(&'static u32)>, 1) | |
| ); | |
| // Randomization must uphold NPO guarantees | |
| assert!(std::mem::offset_of!(Option::<&usize>, Some.0) == 0); | |
| assert!(std::mem::offset_of!(Result::<&usize, ()>, Ok.0) == 0); | |
| }; | |
| // these types only have their size checked, they're never constructed. | |
| // these repr(Rust) types must remain zero-sized. | |
| #[allow(dead_code)] | |
| pub struct UnitStruct; | |
| #[allow(dead_code)] | |
| pub struct EmptyTupleStruct(); | |
| #[allow(dead_code)] | |
| pub struct EmptyStruct {} | |
| #[allow(dead_code)] | |
| pub struct ZstFieldsTupleStruct((), [u64; 0], [u8; 0], [(); 42]); | |
| #[allow(dead_code)] | |
| pub struct ZstFieldsStruct { | |
| a: (), | |
| b: [u64; 0], | |
| c: [u8; 0], | |
| d: [(); 42], | |
| } | |
| #[allow(dead_code)] | |
| pub enum EmptyEnum {} | |
| #[allow(dead_code)] | |
| pub enum SingleUnitVariantEnum { A } | |
| #[allow(dead_code)] | |
| pub enum SingleZstFieldTupleVariantEnum { A((), [u64; 0], [u8; 0], [(); 42]) } | |
| #[allow(dead_code)] | |
| pub enum SingleZstFieldVariantEnum { | |
| A { | |
| a: (), | |
| b: [u64; 0], | |
| c: [u8; 0], | |
| d: [(); 42], | |
| } | |
| } | |
| // all these types must remain zero-sized. | |
| const _: () = { | |
| assert!(size_of::<UnitStruct>() == 0); | |
| assert!(size_of::<EmptyTupleStruct>() == 0); | |
| assert!(size_of::<EmptyStruct>() == 0); | |
| assert!(size_of::<ZstFieldsTupleStruct>() == 0); | |
| assert!(size_of::<ZstFieldsStruct>() == 0); | |
| assert!(size_of::<EmptyEnum>() == 0); | |
| assert!(size_of::<SingleUnitVariantEnum>() == 0); | |
| assert!(size_of::<SingleZstFieldTupleVariantEnum>() == 0); | |
| assert!(size_of::<SingleZstFieldVariantEnum>() == 0); | |
| }; | |
| #[allow(dead_code)] | |
| struct Unsizable<T: ?Sized>(usize, T); | |
| fn main() { | |
| // offset_of doesn't let us probe the unsized field, check at runtime. | |
| let x = &Unsizable::<[u32; 4]>(0, [0; 4]); | |
| let y: &Unsizable::<[u32]> = x; | |
| // type coercion must not change the layout. | |
| assert_eq!(ptr::from_ref(&x.1).addr(), ptr::from_ref(&y.1).addr()); | |
| } |