Skip to content

Commit ab60636

Browse files
committed
Don't encode enum tag for uninhabited repr(Rust) enum variants
1 parent e5929c3 commit ab60636

File tree

1 file changed

+57
-5
lines changed

1 file changed

+57
-5
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -844,11 +844,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
844844
let mut layout_variants = variants
845845
.iter_enumerated()
846846
.map(|(i, field_layouts)| {
847-
let mut st = self.univariant(
848-
field_layouts,
849-
repr,
850-
StructKind::Prefixed(min_ity.size(), prefix_align),
851-
)?;
847+
let uninhabited = field_layouts.iter().any(|f| f.is_uninhabited());
848+
// We don't need to encode the tag in uninhabited variants in repr(Rust) enums
849+
let struct_kind = if uninhabited && !repr.inhibit_enum_layout_opt() {
850+
StructKind::AlwaysSized
851+
} else {
852+
StructKind::Prefixed(min_ity.size(), prefix_align)
853+
};
854+
let mut st = self.univariant(field_layouts, repr, struct_kind)?;
855+
852856
st.variants = Variants::Single { index: i };
853857
// Find the first field we can't move later
854858
// to make room for a larger discriminant.
@@ -918,6 +922,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
918922
let old_ity_size = min_ity.size();
919923
let new_ity_size = ity.size();
920924
for variant in &mut layout_variants {
925+
// Don't change field offsets of uninhabited variants in repr(Rust) enums,
926+
// they don't encode the tag and their fields may overlap with the tag.
927+
if variant.is_uninhabited() && !repr.inhibit_enum_layout_opt() {
928+
continue;
929+
}
921930
match variant.fields {
922931
FieldsShape::Arbitrary { ref mut offsets, .. } => {
923932
for i in offsets {
@@ -962,6 +971,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
962971
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
963972
panic!("encountered a non-arbitrary layout during enum layout");
964973
};
974+
// Don't look in uninhabited variants for repr(Rust) enums, they will never be
975+
// passed over an ABI so they don't matter for the purpose of determining
976+
// BackendRepr.
977+
if layout_variant.is_uninhabited() && !repr.inhibit_enum_layout_opt() {
978+
continue;
979+
}
965980
// We skip *all* ZST here and later check if we are good in terms of alignment.
966981
// This lets us handle some cases involving aligned ZST.
967982
let mut fields = iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
@@ -1078,6 +1093,43 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
10781093
.map(|v| v.randomization_seed)
10791094
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
10801095

1096+
// If all variants are uninhabited, the repr does not inhibit layout optimizations,
1097+
// and all fields are ZSTs, then the tagged layout will not have room for the tag.
1098+
// So in this case, we return an uninhabited layout that is big enough and aligned
1099+
// enough for all variant fields, but do not say it has any fields itself.
1100+
// Doing this only when the layout is too small to fit the tag gives better error
1101+
// messages during const-eval in some cases, "constructing invalid value at .<enum-tag>:
1102+
// encountered an uninhabited enum variant" instead of "constructing invalid value:
1103+
// encountered a value of uninhabited type".
1104+
// Note the we only reach this case when there is at least one non-1-aligned ZST field,
1105+
// since the all-1-ZST case is handled by the "present_variants" check in
1106+
// `layout_of_struct_or_enum`.
1107+
if uninhabited && size < tag.size(&self.cx) {
1108+
// The only way for the size to be less than the tag's size is for it to be zero,
1109+
// which can only occur when the repr does not inhibit layout optimization.
1110+
debug_assert!(
1111+
size == Size::ZERO,
1112+
"size was non-zero but less than tag size: 0 < {size:?} < {:?}",
1113+
tag.size(&self.cx)
1114+
);
1115+
debug_assert!(
1116+
!repr.inhibit_enum_layout_opt(),
1117+
"enum size was zero with layout optimizations disabled"
1118+
);
1119+
return Ok(LayoutData {
1120+
fields: FieldsShape::Arbitrary { offsets: [].into(), memory_index: [].into() },
1121+
variants: Variants::Empty,
1122+
backend_repr: BackendRepr::Memory { sized: true },
1123+
largest_niche: None,
1124+
uninhabited: true,
1125+
align,
1126+
size,
1127+
max_repr_align,
1128+
unadjusted_abi_align,
1129+
randomization_seed: combined_seed,
1130+
});
1131+
}
1132+
10811133
let tagged_layout = LayoutData {
10821134
variants: Variants::Multiple {
10831135
tag,

0 commit comments

Comments
 (0)