Skip to content

Commit 9452645

Browse files
committed
Do not hold space for niche in uninhabited variants when using niche-filling layout.
1 parent 0b658af commit 9452645

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
@@ -658,12 +658,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
658658
let mut align = dl.aggregate_align;
659659
let mut max_repr_align = repr.align;
660660
let mut unadjusted_abi_align = align;
661+
let mut inhabited_variants = 0;
661662

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

668672
align = align.max(st.align.abi);
669673
max_repr_align = max_repr_align.max(st.max_repr_align);
@@ -673,14 +677,29 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
673677
})
674678
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
675679

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

693+
if variant_layouts[largest_variant_index].uninhabited {
694+
// If the largest variant is uninhabited, then filling its niche would give
695+
// a worse layout than the tagged layout.
696+
return None;
697+
}
698+
681699
let all_indices = variants.indices();
682-
let needs_disc =
683-
|index: VariantIdx| index != largest_variant_index && !absent(&variants[index]);
700+
let needs_disc = |index: VariantIdx| {
701+
index != largest_variant_index && !variant_layouts[index].uninhabited
702+
};
684703
let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap()
685704
..=all_indices.rev().find(|v| needs_disc(*v)).unwrap();
686705

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

702725
layout.largest_niche = None;
@@ -741,14 +764,14 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
741764

742765
let largest_niche = Niche::from_scalar(dl, niche_offset, niche_scalar);
743766

744-
let others_zst = variant_layouts
745-
.iter_enumerated()
746-
.all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
767+
let others_zst_or_uninhabited = variant_layouts.iter_enumerated().all(|(i, layout)| {
768+
i == largest_variant_index || layout.size == Size::ZERO || layout.uninhabited
769+
});
747770
let same_size = size == variant_layouts[largest_variant_index].size;
748771
let same_align = align == variant_layouts[largest_variant_index].align.abi;
749772

750773
let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
751-
let abi = if same_size && same_align && others_zst {
774+
let abi = if same_size && same_align && others_zst_or_uninhabited {
752775
match variant_layouts[largest_variant_index].backend_repr {
753776
// When the total alignment and size match, we can use the
754777
// 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<()>>(), 2 * size_of::<&'static ()>());
281+
assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<!>>(), size_of::<&'static ()>());
282+
assert_eq!(
283+
size_of::<EnumWithMaybeUninhabitedVariant<(!, u8)>>(),
284+
2 * size_of::<&'static ()>(),
285+
);
286+
assert_eq!(
287+
size_of::<EnumWithMaybeUninhabitedVariant<(!, usize)>>(),
288+
2 * size_of::<&'static ()>(),
289+
);
282290
assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>());
283291

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

0 commit comments

Comments
 (0)