Skip to content

Commit bc74d06

Browse files
committed
[WIP] do not hold space for niche in uninhabited variants when using niche-filling layout.
1 parent 5345683 commit bc74d06

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -660,12 +660,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
660660
let mut align = dl.aggregate_align;
661661
let mut max_repr_align = repr.align;
662662
let mut unadjusted_abi_align = align.abi;
663+
let mut inhabited_variants = 0;
663664

664665
let mut variant_layouts = variants
665666
.iter_enumerated()
666667
.map(|(j, v)| {
667668
let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
668669
st.variants = Variants::Single { index: j, variants: None };
670+
if !st.uninhabited {
671+
inhabited_variants += 1;
672+
}
669673

670674
align = align.max(st.align);
671675
max_repr_align = max_repr_align.max(st.max_repr_align);
@@ -675,14 +679,29 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
675679
})
676680
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
677681

682+
if inhabited_variants < 2 {
683+
// If there's only one inhabited variant, the no-tag layout will be equivalent to
684+
// what the niched layout would be. Returning `None` here lets us assume there is
685+
// another inhabited variant which simplifies the rest of the layout computation.
686+
return None;
687+
}
688+
689+
// Choose the largest variant, picking an inhabited variant in a tie
678690
let largest_variant_index = variant_layouts
679691
.iter_enumerated()
680-
.max_by_key(|(_i, layout)| layout.size.bytes())
692+
.max_by_key(|(_i, layout)| (layout.size.bytes(), !layout.uninhabited))
681693
.map(|(i, _layout)| i)?;
682694

695+
if variant_layouts[largest_variant_index].uninhabited {
696+
// If the largest variant is uninhabited, then filling its niche would give
697+
// a worse layout than the tagged layout.
698+
return None;
699+
}
700+
683701
let all_indices = variants.indices();
684-
let needs_disc =
685-
|index: VariantIdx| index != largest_variant_index && !absent(&variants[index]);
702+
let needs_disc = |index: VariantIdx| {
703+
index != largest_variant_index && !variant_layouts[index].uninhabited
704+
};
686705
let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap()
687706
..=all_indices.rev().find(|v| needs_disc(*v)).unwrap();
688707

@@ -699,6 +718,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
699718
let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| {
700719
if i == largest_variant_index {
701720
return true;
721+
} else if layout.uninhabited {
722+
// This variant doesn't need to hold space for the niche,
723+
// it just needs to be at-most-as big and aligned as the enum.
724+
return layout.size <= size && layout.align.abi <= align.abi;
702725
}
703726

704727
layout.largest_niche = None;
@@ -743,14 +766,14 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
743766

744767
let largest_niche = Niche::from_scalar(dl, niche_offset, niche_scalar);
745768

746-
let others_zst = variant_layouts
747-
.iter_enumerated()
748-
.all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
769+
let others_zst_or_uninhabited = variant_layouts.iter_enumerated().all(|(i, layout)| {
770+
i == largest_variant_index || layout.size == Size::ZERO || layout.uninhabited
771+
});
749772
let same_size = size == variant_layouts[largest_variant_index].size;
750773
let same_align = align == variant_layouts[largest_variant_index].align;
751774

752775
let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
753-
let abi = if same_size && same_align && others_zst {
776+
let abi = if same_size && same_align && others_zst_or_uninhabited {
754777
match variant_layouts[largest_variant_index].backend_repr {
755778
// When the total alignment and size match, we can use the
756779
// same ABI as the scalar variant with the reserved niche.

tests/ui/structs-enums/type-sizes.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,16 @@ pub fn main() {
277277
assert_eq!(size_of::<EnumSingle4>(), 1);
278278
assert_eq!(size_of::<EnumSingle5>(), 1);
279279

280-
assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<!>>(),
281-
size_of::<EnumWithMaybeUninhabitedVariant<()>>());
280+
assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<!>>(), size_of::<&'static ()>());
281+
assert_eq!(
282+
size_of::<EnumWithMaybeUninhabitedVariant<(!, u8)>>(),
283+
2 * size_of::<&'static ()>(),
284+
);
285+
assert_eq!(
286+
size_of::<EnumWithMaybeUninhabitedVariant<(!, usize)>>(),
287+
2 * size_of::<&'static ()>(),
288+
);
289+
assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<()>>(), 2 * size_of::<&'static ()>());
282290
assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>());
283291

284292
assert_eq!(size_of::<NicheFieldOrder1>(), 24);

0 commit comments

Comments
 (0)