// run-pass #![allow(non_camel_case_types)] #![allow(dead_code)] #![feature(never_type)] #![feature(pointer_is_aligned)] #![feature(ptr_from_ref)] #![feature(strict_provenance)] use std::mem::size_of; use std::num::{NonZeroU8, NonZeroU16}; use std::ptr; use std::ptr::NonNull; use std::borrow::Cow; struct t {a: u8, b: i8} struct u {a: u8, b: i8, c: u8} struct v {a: u8, b: i8, c: v2, d: u32} struct v2 {u: char, v: u8} struct w {a: isize, b: ()} struct x {a: isize, b: (), c: ()} struct y {x: isize} enum e1 { a(u8, u32), b(u32), c } enum e2 { a(u32), b } #[repr(C, u8)] enum e3 { a([u16; 0], u8), b } struct ReorderedStruct { a: u8, b: u16, c: u8 } enum ReorderedEnum { A(u8, u16, u8), B(u8, u16, u8), } enum ReorderedEnum2 { A(u8, u32, u8), B(u16, u8, u16, u8), // 0x100 niche variants. _00, _01, _02, _03, _04, _05, _06, _07, _08, _09, _0A, _0B, _0C, _0D, _0E, _0F, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _1A, _1B, _1C, _1D, _1E, _1F, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _2A, _2B, _2C, _2D, _2E, _2F, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _3A, _3B, _3C, _3D, _3E, _3F, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _4A, _4B, _4C, _4D, _4E, _4F, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _5A, _5B, _5C, _5D, _5E, _5F, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _6A, _6B, _6C, _6D, _6E, _6F, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _7A, _7B, _7C, _7D, _7E, _7F, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _8A, _8B, _8C, _8D, _8E, _8F, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _9A, _9B, _9C, _9D, _9E, _9F, _A0, _A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8, _A9, _AA, _AB, _AC, _AD, _AE, _AF, _B0, _B1, _B2, _B3, _B4, _B5, _B6, _B7, _B8, _B9, _BA, _BB, _BC, _BD, _BE, _BF, _C0, _C1, _C2, _C3, _C4, _C5, _C6, _C7, _C8, _C9, _CA, _CB, _CC, _CD, _CE, _CF, _D0, _D1, _D2, _D3, _D4, _D5, _D6, _D7, _D8, _D9, _DA, _DB, _DC, _DD, _DE, _DF, _E0, _E1, _E2, _E3, _E4, _E5, _E6, _E7, _E8, _E9, _EA, _EB, _EC, _ED, _EE, _EF, _F0, _F1, _F2, _F3, _F4, _F5, _F6, _F7, _F8, _F9, _FA, _FB, _FC, _FD, _FE, _FF, } enum EnumEmpty {} enum EnumSingle1 { A, } enum EnumSingle2 { A = 42 as isize, } enum EnumSingle3 { A, B(!), } #[repr(u8)] enum EnumSingle4 { A, } #[repr(u8)] enum EnumSingle5 { A = 42 as u8, } enum EnumWithMaybeUninhabitedVariant { A(&'static ()), B(&'static (), T), C, } enum NicheFilledEnumWithAbsentVariant { A(&'static ()), B((), !), C, } enum Option2 { Some(A, B), None } // Two layouts are considered for `CanBeNicheFilledButShouldnt`: // Niche-filling: // { u32 (4 bytes), NonZeroU8 + tag in niche (1 byte), padding (3 bytes) } // Tagged: // { tag (1 byte), NonZeroU8 (1 byte), padding (2 bytes), u32 (4 bytes) } // Both are the same size (due to padding), // but the tagged layout is better as the tag creates a niche with 254 invalid values, // allowing types like `Option>` to fit into 8 bytes. pub enum CanBeNicheFilledButShouldnt { A(NonZeroU8, u32), B } pub enum AlwaysTaggedBecauseItHasNoNiche { A(u8, u32), B } pub enum NicheFilledMultipleFields { A(bool, u8), B(u8), C(u8), D(bool), E, F, G, } struct BoolInTheMiddle(std::num::NonZeroU16, bool, u8); enum NicheWithData { A, B([u16; 5]), Largest { a1: u32, a2: BoolInTheMiddle, a3: u32 }, C, D(u32, u32), } // A type with almost 2^16 invalid values. #[repr(u16)] pub enum NicheU16 { _0, } pub enum EnumManyVariant { Dataful(u8, X), // 0x100 niche variants. _00, _01, _02, _03, _04, _05, _06, _07, _08, _09, _0A, _0B, _0C, _0D, _0E, _0F, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _1A, _1B, _1C, _1D, _1E, _1F, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _2A, _2B, _2C, _2D, _2E, _2F, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _3A, _3B, _3C, _3D, _3E, _3F, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _4A, _4B, _4C, _4D, _4E, _4F, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _5A, _5B, _5C, _5D, _5E, _5F, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _6A, _6B, _6C, _6D, _6E, _6F, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _7A, _7B, _7C, _7D, _7E, _7F, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _8A, _8B, _8C, _8D, _8E, _8F, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _9A, _9B, _9C, _9D, _9E, _9F, _A0, _A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8, _A9, _AA, _AB, _AC, _AD, _AE, _AF, _B0, _B1, _B2, _B3, _B4, _B5, _B6, _B7, _B8, _B9, _BA, _BB, _BC, _BD, _BE, _BF, _C0, _C1, _C2, _C3, _C4, _C5, _C6, _C7, _C8, _C9, _CA, _CB, _CC, _CD, _CE, _CF, _D0, _D1, _D2, _D3, _D4, _D5, _D6, _D7, _D8, _D9, _DA, _DB, _DC, _DD, _DE, _DF, _E0, _E1, _E2, _E3, _E4, _E5, _E6, _E7, _E8, _E9, _EA, _EB, _EC, _ED, _EE, _EF, _F0, _F1, _F2, _F3, _F4, _F5, _F6, _F7, _F8, _F9, _FA, _FB, _FC, _FD, _FE, _FF, } struct Reorder4 { a: u32, b: u8, ary: [u8; 4], } struct Reorder2 { a: u16, b: u8, ary: [u8; 6], } // We want the niche in the front, which means we can't treat the array as quasi-aligned more than // 4 bytes even though we also want to place it at an 8-aligned offset where possible. // So the ideal layout would look like: (char, u32, [u8; 8], u8) // The current layout algorithm does (char, [u8; 8], u32, u8) #[repr(align(8))] struct ReorderWithNiche { a: u32, b: char, c: u8, ary: [u8; 8] } #[repr(C)] struct EndNiche8([u8; 7], bool); #[repr(C)] struct MiddleNiche4(u8, u8, bool, u8); struct ReorderEndNiche { a: EndNiche8, b: MiddleNiche4, } // standins for std types which we want to be laid out in a reasonable way struct RawVecDummy { ptr: NonNull, cap: usize, } struct VecDummy { r: RawVecDummy, len: usize, } pub fn main() { assert_eq!(size_of::(), 1 as usize); assert_eq!(size_of::(), 4 as usize); assert_eq!(size_of::(), 4 as usize); assert_eq!(size_of::(), 1 as usize); assert_eq!(size_of::(), 4 as usize); assert_eq!(size_of::(), 2 as usize); assert_eq!(size_of::(), 3 as usize); // Alignment causes padding before the char and the u32. assert_eq!(size_of::(), 16 as usize); assert_eq!(size_of::(), size_of::()); assert_eq!(size_of::(), size_of::()); assert_eq!(size_of::(), size_of::()); assert_eq!(size_of::(), size_of::()); // Make sure enum types are the appropriate size, mostly // around ensuring alignment is handled properly assert_eq!(size_of::(), 8 as usize); assert_eq!(size_of::(), 8 as usize); assert_eq!(size_of::(), 4 as usize); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 6); assert_eq!(size_of::(), 8); assert_eq!(size_of::(), 0); assert_eq!(size_of::(), 0); assert_eq!(size_of::(), 0); assert_eq!(size_of::(), 0); assert_eq!(size_of::(), 1); assert_eq!(size_of::(), 1); assert_eq!(size_of::>(), size_of::>()); assert_eq!(size_of::(), size_of::<&'static ()>()); assert_eq!(size_of::>>(), size_of::<(bool, &())>()); assert_eq!(size_of::>>(), size_of::<(bool, &())>()); assert_eq!(size_of::>>(), size_of::<(bool, &())>()); assert_eq!(size_of::>>(), size_of::<(bool, &())>()); assert_eq!(size_of::(), 8); assert_eq!(size_of::>(), 8); assert_eq!(size_of::>>(), 8); assert_eq!(size_of::(), 8); assert_eq!(size_of::>(), 8); assert_eq!(size_of::>>(), 8); assert_eq!(size_of::(), 2); assert_eq!(size_of::>(), 2); assert_eq!(size_of::>>(), 2); struct S1{ a: u16, b: std::num::NonZeroU16, c: u16, d: u8, e: u32, f: u64, g:[u8;2] } assert_eq!(size_of::(), 24); assert_eq!(size_of::>(), 24); assert_eq!(size_of::(), 12); assert_eq!(size_of::>(), 12); assert_eq!(size_of::>>(), 12); assert_eq!( size_of::>>>(), size_of::<(&(), NicheWithData)>() ); pub enum FillPadding { A(std::num::NonZeroU8, u32), B } assert_eq!(size_of::(), 8); assert_eq!(size_of::>(), 8); assert_eq!(size_of::>>(), 8); assert_eq!(size_of::>(), 4); assert_eq!(size_of::>>(), 4); assert_eq!(size_of::>(), 4); assert_eq!(size_of::>(), 6); assert_eq!(size_of::>(), 4); assert_eq!(size_of::>>(), 4); assert_eq!(size_of::>>(), 6); assert_eq!(size_of::>>(), 6); let v = Reorder4 {a: 0, b: 0, ary: [0; 4]}; assert_eq!(size_of::(), 12); assert!((&v.ary).as_ptr().is_aligned_to(4), "[u8; 4] should group with align-4 fields"); let v = Reorder2 {a: 0, b: 0, ary: [0; 6]}; assert_eq!(size_of::(), 10); assert!((&v.ary).as_ptr().is_aligned_to(2), "[u8; 6] should group with align-2 fields"); let v = VecDummy { r: RawVecDummy { ptr: NonNull::dangling(), cap: 0 }, len: 1 }; assert_eq!(ptr::from_ref(&v), ptr::from_ref(&v.r.ptr).cast(), "sort niches to the front where possible"); // Ideal layouts: (bool, u8, NonZeroU16) or (NonZeroU16, u8, bool) // Currently the layout algorithm will choose the latter because it doesn't attempt // to aggregate multiple smaller fields to move a niche before a higher-alignment one. let b = BoolInTheMiddle( NonZeroU16::new(1).unwrap(), true, 0); assert!(ptr::from_ref(&b.1).addr() > ptr::from_ref(&b.2).addr()); assert_eq!(size_of::>(), size_of::()); let v = ReorderWithNiche {a: 0, b: ' ', c: 0, ary: [0; 8]}; assert!((&v.ary).as_ptr().is_aligned_to(4), "here [u8; 8] should group with _at least_ align-4 fields"); assert_eq!(ptr::from_ref(&v), ptr::from_ref(&v.b).cast(), "sort niches to the front where possible"); // Neither field has a niche at the beginning so the layout algorithm should try move niches to // the end which means the 8-sized field shouldn't be alignment-promoted before the 4-sized one. let v = ReorderEndNiche { a: EndNiche8([0; 7], false), b: MiddleNiche4(0, 0, false, 0) }; assert!(ptr::from_ref(&v.a).addr() > ptr::from_ref(&v.b).addr()); }