Change:
This prints the values in comments: Note that contains strictly more opportunities for optimization, but it's optimized worse. That's because it uses a "niche-filling" layout (using in the field of for ), but the "tag" layout wouldn't be any larger, because it has enough space in bytes to fit the (byte-sized) tag, and . uses the tagged layout since it has no other choice, and ends up with free values in the byte-sized tag, but "jumps the gun" on using a pre-existing niche.

Discussion:
#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] <ins> #![feature(cmp_min_max_by)] </ins> #![feature(const_fn)] #![feature(const_panic)] #![feature(const_fn_transmute)]

.iter_enumerated() .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32())); <ins> let mut niche_filling_layout = None; </ins> // Niche-filling enum optimization. if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants { let mut dataful_variant = None;

let largest_niche = Niche::from_scalar(dl, offset, niche_scalar.clone()); <del> return Ok(tcx.intern_layout(Layout { </del> <ins> niche_filling_layout = Some(Layout { </ins> variants: Variants::Multiple { tag: niche_scalar, tag_encoding: TagEncoding::Niche {

largest_niche, size, align, <del> })); </del> <ins> }); </ins> } } }

let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone()); <del> tcx.intern_layout(Layout { </del> <ins> let tagged_layout = Layout { </ins> variants: Variants::Multiple { tag, tag_encoding: TagEncoding::Direct,

abi, align, size, <del> }) </del> <ins> }; let best_layout = match (tagged_layout, niche_filling_layout) { (tagged_layout, Some(niche_filling_layout)) => { // Pick the smaller layout; otherwise, // pick the layout with the larger niche; otherwise, // pick tagged as it has simpler codegen. cmp::min_by_key(tagged_layout, niche_filling_layout, |layout| { let niche_size = layout.largest_niche.as_ref().map_or(0, |n| n.available(dl)); (layout.size, cmp::Reverse(niche_size)) }) } (tagged_layout, None) => tagged_layout, }; tcx.intern_layout(best_layout) </ins> } // Types with no meaningful known layout.

print-type-size         field `.0`: 12 bytes print-type-size     variant `None`: 0 bytes print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes <ins> print-type-size     discriminant: 1 bytes </ins> print-type-size     variant `Record`: 7 bytes <del> print-type-size         field `.val`: 4 bytes print-type-size         field `.post`: 2 bytes </del> print-type-size         field `.pre`: 1 bytes <ins> print-type-size         field `.post`: 2 bytes print-type-size         field `.val`: 4 bytes </ins> print-type-size     variant `None`: 0 bytes <del> print-type-size     end padding: 1 bytes </del> print-type-size type: `MyOption<Union1<std::num::NonZeroU32>>`: 8 bytes, alignment: 4 bytes print-type-size     discriminant: 4 bytes print-type-size     variant `Some`: 4 bytes

#![feature(never_type)] use std::mem::size_of; <ins> use std::num::NonZeroU8; </ins> struct t {a: u8, b: i8} struct u {a: u8, b: i8, c: u8}

None } <ins> // 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<Option<CanBeNicheFilledButShouldnt>>` to fit into 8 bytes. pub enum CanBeNicheFilledButShouldnt { A(NonZeroU8, u32), B } pub enum AlwaysTaggedBecauseItHasNoNiche { A(u8, u32), B } </ins> pub fn main() { assert_eq!(size_of::<u8>(), 1 as usize); assert_eq!(size_of::<u32>(), 4 as usize);

assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>()); assert_eq!(size_of::<Option<Option2<bool, &()>>>(), size_of::<(bool, &())>()); assert_eq!(size_of::<Option<Option2<&(), bool>>>(), size_of::<(bool, &())>()); <ins> assert_eq!(size_of::<CanBeNicheFilledButShouldnt>(), 8); assert_eq!(size_of::<Option<CanBeNicheFilledButShouldnt>>(), 8); assert_eq!(size_of::<Option<Option<CanBeNicheFilledButShouldnt>>>(), 8); assert_eq!(size_of::<AlwaysTaggedBecauseItHasNoNiche>(), 8); assert_eq!(size_of::<Option<AlwaysTaggedBecauseItHasNoNiche>>(), 8); assert_eq!(size_of::<Option<Option<AlwaysTaggedBecauseItHasNoNiche>>>(), 8); </ins> }