Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1b86d85

Browse files
committedDec 1, 2024·
Variants::Single: do not use invalid VariantIdx for uninhabited enums
1 parent 3dc9b1b commit 1b86d85

File tree

24 files changed

+168
-152
lines changed

24 files changed

+168
-152
lines changed
 

‎compiler/rustc_abi/src/layout.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
120120
.max_by_key(|niche| niche.available(dl));
121121

122122
LayoutData {
123-
variants: Variants::Single { index: VariantIdx::new(0) },
123+
variants: Variants::Single { index: Some(VariantIdx::new(0)) },
124124
fields: FieldsShape::Arbitrary {
125125
offsets: [Size::ZERO, b_offset].into(),
126126
memory_index: [0, 1].into(),
@@ -214,7 +214,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
214214
) -> LayoutData<FieldIdx, VariantIdx> {
215215
let dl = self.cx.data_layout();
216216
LayoutData {
217-
variants: Variants::Single { index: VariantIdx::new(0) },
217+
variants: Variants::Single { index: None },
218218
fields: FieldsShape::Primitive,
219219
backend_repr: BackendRepr::Uninhabited,
220220
largest_niche: None,
@@ -385,7 +385,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
385385
};
386386

387387
Ok(LayoutData {
388-
variants: Variants::Single { index: only_variant_idx },
388+
variants: Variants::Single { index: Some(only_variant_idx) },
389389
fields: FieldsShape::Union(union_field_count),
390390
backend_repr: abi,
391391
largest_niche: None,
@@ -424,7 +424,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
424424
};
425425

426426
let mut st = self.univariant(&variants[v], repr, kind)?;
427-
st.variants = Variants::Single { index: v };
427+
st.variants = Variants::Single { index: Some(v) };
428428

429429
if is_unsafe_cell {
430430
let hide_niches = |scalar: &mut _| match scalar {
@@ -543,7 +543,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
543543
.iter_enumerated()
544544
.map(|(j, v)| {
545545
let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
546-
st.variants = Variants::Single { index: j };
546+
st.variants = Variants::Single { index: Some(j) };
547547

548548
align = align.max(st.align);
549549
max_repr_align = max_repr_align.max(st.max_repr_align);
@@ -736,7 +736,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
736736
repr,
737737
StructKind::Prefixed(min_ity.size(), prefix_align),
738738
)?;
739-
st.variants = Variants::Single { index: i };
739+
st.variants = Variants::Single { index: Some(i) };
740740
// Find the first field we can't move later
741741
// to make room for a larger discriminant.
742742
for field_idx in st.fields.index_by_increasing_offset() {
@@ -1344,7 +1344,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
13441344
};
13451345

13461346
Ok(LayoutData {
1347-
variants: Variants::Single { index: VariantIdx::new(0) },
1347+
variants: Variants::Single { index: Some(VariantIdx::new(0)) },
13481348
fields: FieldsShape::Arbitrary { offsets, memory_index },
13491349
backend_repr: abi,
13501350
largest_niche,

‎compiler/rustc_abi/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,9 +1506,9 @@ impl BackendRepr {
15061506
pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
15071507
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
15081508
Single {
1509-
/// Always 0 for non-enums/generators.
1510-
/// For enums without a variant, this is an invalid index!
1511-
index: VariantIdx,
1509+
/// Always `Some(0)` for types without variants (i.e., everything except for `!`, enums, and
1510+
/// generators). `None` indicates an uninhabited type; this is used for zero-variant enums.
1511+
index: Option<VariantIdx>,
15121512
},
15131513

15141514
/// Enum-likes with more than one variant: each variant comes with
@@ -1706,7 +1706,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
17061706
let size = scalar.size(cx);
17071707
let align = scalar.align(cx);
17081708
LayoutData {
1709-
variants: Variants::Single { index: VariantIdx::new(0) },
1709+
variants: Variants::Single { index: Some(VariantIdx::new(0)) },
17101710
fields: FieldsShape::Primitive,
17111711
backend_repr: BackendRepr::Scalar(scalar),
17121712
largest_niche,

‎compiler/rustc_codegen_cranelift/src/discriminant.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
1919
}
2020
match layout.variants {
2121
Variants::Single { index } => {
22-
assert_eq!(index, variant_index);
22+
assert_eq!(index.unwrap(), variant_index);
2323
}
2424
Variants::Multiple {
2525
tag: _,
@@ -86,9 +86,10 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
8686

8787
let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
8888
Variants::Single { index } => {
89+
let index = index.unwrap();
8990
let discr_val = layout
9091
.ty
91-
.discriminant_for_variant(fx.tcx, *index)
92+
.discriminant_for_variant(fx.tcx, index)
9293
.map_or(u128::from(index.as_u32()), |discr| discr.val);
9394

9495
let val = match dest_layout.ty.kind() {

‎compiler/rustc_codegen_gcc/src/type_of.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,14 @@ fn uncached_gcc_type<'gcc, 'tcx>(
9999
if !cx.sess().fewer_names() =>
100100
{
101101
let mut name = with_no_trimmed_paths!(layout.ty.to_string());
102-
if let (&ty::Adt(def, _), &Variants::Single { index }) =
102+
if let (&ty::Adt(def, _), &Variants::Single { index: Some(index) }) =
103103
(layout.ty.kind(), &layout.variants)
104104
{
105105
if def.is_enum() && !def.variants().is_empty() {
106106
write!(&mut name, "::{}", def.variant(index).name).unwrap();
107107
}
108108
}
109-
if let (&ty::Coroutine(_, _), &Variants::Single { index }) =
109+
if let (&ty::Coroutine(_, _), &Variants::Single { index: Some(index) }) =
110110
(layout.ty.kind(), &layout.variants)
111111
{
112112
write!(&mut name, "::{}", ty::CoroutineArgs::variant_name(index)).unwrap();
@@ -230,7 +230,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
230230

231231
// Check the cache.
232232
let variant_index = match self.variants {
233-
Variants::Single { index } => Some(index),
233+
Variants::Single { index } => index,
234234
_ => None,
235235
};
236236
let cached_type = cx.types.borrow().get(&(self.ty, variant_index)).cloned();

‎compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
206206
|cx, enum_type_di_node| {
207207
match enum_type_and_layout.variants {
208208
Variants::Single { index: variant_index } => {
209-
if enum_adt_def.variants().is_empty() {
209+
let Some(variant_index) = variant_index else {
210210
// Uninhabited enums have Variants::Single. We don't generate
211211
// any members for them.
212212
return smallvec![];
213-
}
213+
};
214214

215215
build_single_variant_union_fields(
216216
cx,

‎compiler/rustc_codegen_llvm/src/type_of.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ fn uncached_llvm_type<'a, 'tcx>(
3535
if !cx.sess().fewer_names() =>
3636
{
3737
let mut name = with_no_visible_paths!(with_no_trimmed_paths!(layout.ty.to_string()));
38-
if let (&ty::Adt(def, _), &Variants::Single { index }) =
38+
if let (&ty::Adt(def, _), &Variants::Single { index: Some(index) }) =
3939
(layout.ty.kind(), &layout.variants)
4040
{
41-
if def.is_enum() && !def.variants().is_empty() {
41+
if def.is_enum() {
4242
write!(&mut name, "::{}", def.variant(index).name).unwrap();
4343
}
4444
}
45-
if let (&ty::Coroutine(_, _), &Variants::Single { index }) =
45+
if let (&ty::Coroutine(_, _), &Variants::Single { index: Some(index) }) =
4646
(layout.ty.kind(), &layout.variants)
4747
{
4848
write!(&mut name, "::{}", ty::CoroutineArgs::variant_name(index)).unwrap();
@@ -216,7 +216,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
216216

217217
// Check the cache.
218218
let variant_index = match self.variants {
219-
Variants::Single { index } => Some(index),
219+
Variants::Single { index } => index,
220220
_ => None,
221221
};
222222
if let Some(llty) = cx.type_lowering.borrow().get(&(self.ty, variant_index)) {

‎compiler/rustc_codegen_ssa/src/mir/place.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
243243
}
244244
let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants {
245245
Variants::Single { index } => {
246+
let index = index.unwrap(); // we already checked `is_uninhabited`
246247
let discr_val = self
247248
.layout
248249
.ty
@@ -365,7 +366,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
365366
}
366367
match self.layout.variants {
367368
Variants::Single { index } => {
368-
assert_eq!(index, variant_index);
369+
assert_eq!(index.unwrap(), variant_index);
369370
}
370371
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
371372
let ptr = self.project_field(bx, tag_field);

‎compiler/rustc_const_eval/src/interpret/discriminant.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
4444
}
4545
}
4646

47-
/// Read discriminant, return the runtime value as well as the variant index.
47+
/// Read discriminant, return the variant index.
4848
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
4949
///
5050
/// Will never return an uninhabited variant.
@@ -66,20 +66,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
6666
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
6767
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants {
6868
Variants::Single { index } => {
69-
// Do some extra checks on enums.
70-
if ty.is_enum() {
71-
// Hilariously, `Single` is used even for 0-variant enums.
72-
// (See https://github.com/rust-lang/rust/issues/89765).
73-
if ty.ty_adt_def().unwrap().variants().is_empty() {
74-
throw_ub!(UninhabitedEnumVariantRead(index))
75-
}
69+
if op.layout().is_uninhabited() {
7670
// For consistency with `write_discriminant`, and to make sure that
7771
// `project_downcast` cannot fail due to strange layouts, we declare immediate UB
78-
// for uninhabited variants.
79-
if op.layout().for_variant(self, index).is_uninhabited() {
80-
throw_ub!(UninhabitedEnumVariantRead(index))
81-
}
72+
// for uninhabited enums.
73+
throw_ub!(UninhabitedEnumVariantRead(None));
8274
}
75+
// Sanity checks.
76+
let index = index.unwrap();
77+
assert!(!op.layout().for_variant(self, index).is_uninhabited());
8378
return interp_ok(index);
8479
}
8580
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
@@ -199,11 +194,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
199194
// `uninhabited_enum_branching` MIR pass. It also ensures consistency with
200195
// `write_discriminant`.
201196
if op.layout().for_variant(self, index).is_uninhabited() {
202-
throw_ub!(UninhabitedEnumVariantRead(index))
197+
throw_ub!(UninhabitedEnumVariantRead(Some(index)))
203198
}
204199
interp_ok(index)
205200
}
206201

202+
/// Read discriminant, return the user-visible discriminant.
203+
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
207204
pub fn discriminant_for_variant(
208205
&self,
209206
ty: Ty<'tcx>,

‎compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,9 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
342342
match layout.variants {
343343
Variants::Single { index } => {
344344
// Inside a variant
345-
PathElem::Field(def.variant(index).fields[FieldIdx::from_usize(field)].name)
345+
PathElem::Field(
346+
def.variant(index.unwrap()).fields[FieldIdx::from_usize(field)].name,
347+
)
346348
}
347349
Variants::Multiple { .. } => bug!("we handled variants above"),
348350
}

‎compiler/rustc_middle/src/mir/interpret/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
399399
/// A discriminant of an uninhabited enum variant is written.
400400
UninhabitedEnumVariantWritten(VariantIdx),
401401
/// An uninhabited enum variant is projected.
402-
UninhabitedEnumVariantRead(VariantIdx),
402+
UninhabitedEnumVariantRead(Option<VariantIdx>),
403403
/// Trying to set discriminant to the niched variant, but the value does not match.
404404
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
405405
/// ABI-incompatible argument types.

‎compiler/rustc_middle/src/ty/layout.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ where
732732
let layout = match this.variants {
733733
Variants::Single { index }
734734
// If all variants but one are uninhabited, the variant layout is the enum layout.
735-
if index == variant_index &&
735+
if index == Some(variant_index) &&
736736
// Don't confuse variants of uninhabited enums with the enum itself.
737737
// For more details see https://github.com/rust-lang/rust/issues/69763.
738738
this.fields != FieldsShape::Primitive =>
@@ -741,6 +741,8 @@ where
741741
}
742742

743743
Variants::Single { index } => {
744+
// `Single` variant enums *can* have other variants, but those are uninhabited.
745+
744746
let tcx = cx.tcx();
745747
let typing_env = cx.typing_env();
746748

@@ -756,7 +758,7 @@ where
756758
_ => bug!("`ty_and_layout_for_variant` on unexpected type {}", this.ty),
757759
};
758760
tcx.mk_layout(LayoutData {
759-
variants: Variants::Single { index: variant_index },
761+
variants: Variants::Single { index: Some(variant_index) },
760762
fields: match NonZero::new(fields) {
761763
Some(fields) => FieldsShape::Union(fields),
762764
None => FieldsShape::Arbitrary { offsets: IndexVec::new(), memory_index: IndexVec::new() },
@@ -773,7 +775,7 @@ where
773775
Variants::Multiple { ref variants, .. } => cx.tcx().mk_layout(variants[variant_index].clone()),
774776
};
775777

776-
assert_eq!(*layout.variants(), Variants::Single { index: variant_index });
778+
assert_eq!(*layout.variants(), Variants::Single { index: Some(variant_index) });
777779

778780
TyAndLayout { ty: this.ty, layout }
779781
}
@@ -903,7 +905,7 @@ where
903905
Variants::Single { index } => TyMaybeWithLayout::Ty(
904906
args.as_coroutine()
905907
.state_tys(def_id, tcx)
906-
.nth(index.as_usize())
908+
.nth(index.unwrap().as_usize())
907909
.unwrap()
908910
.nth(i)
909911
.unwrap(),
@@ -922,7 +924,8 @@ where
922924
ty::Adt(def, args) => {
923925
match this.variants {
924926
Variants::Single { index } => {
925-
let field = &def.variant(index).fields[FieldIdx::from_usize(i)];
927+
let field =
928+
&def.variant(index.unwrap()).fields[FieldIdx::from_usize(i)];
926929
TyMaybeWithLayout::Ty(field.ty(tcx, args))
927930
}
928931

‎compiler/rustc_mir_transform/src/jump_threading.rs

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
3636
//! cost by `MAX_COST`.
3737
38-
use rustc_abi::{TagEncoding, Variants};
3938
use rustc_arena::DroplessArena;
4039
use rustc_const_eval::const_eval::DummyMachine;
4140
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
@@ -565,31 +564,15 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
565564
StatementKind::SetDiscriminant { box place, variant_index } => {
566565
let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return };
567566
let enum_ty = place.ty(self.body, self.tcx).ty;
568-
// `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
569-
// of a niche encoding. If we cannot ensure that we write to the discriminant, do
570-
// nothing.
571-
let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else {
567+
// `SetDiscriminant` guarantees that the discriminant is now `variant_index`.
568+
// Even if the discriminant write does nothing due to niches, it is UB to set the
569+
// discriminant when the data does not encode the desired discriminant.
570+
let Some(discr) =
571+
self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()
572+
else {
572573
return;
573574
};
574-
let writes_discriminant = match enum_layout.variants {
575-
Variants::Single { index } => {
576-
assert_eq!(index, *variant_index);
577-
true
578-
}
579-
Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => true,
580-
Variants::Multiple {
581-
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
582-
..
583-
} => *variant_index != untagged_variant,
584-
};
585-
if writes_discriminant {
586-
let Some(discr) =
587-
self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()
588-
else {
589-
return;
590-
};
591-
self.process_immediate(bb, discr_target, discr, state);
592-
}
575+
self.process_immediate(bb, discr_target, discr, state);
593576
}
594577
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
595578
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(

‎compiler/rustc_mir_transform/src/unreachable_enum_branching.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ fn variant_discriminants<'tcx>(
5454
tcx: TyCtxt<'tcx>,
5555
) -> FxHashSet<u128> {
5656
match &layout.variants {
57-
Variants::Single { index } => {
57+
Variants::Single { index: None } => {
58+
// Uninhabited, no valid discriminant.
59+
FxHashSet::default()
60+
}
61+
Variants::Single { index: Some(index) } => {
5862
let mut res = FxHashSet::default();
5963
res.insert(
6064
ty.discriminant_for_variant(tcx, *index)

‎compiler/rustc_smir/src/rustc_smir/convert/abi.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Variants<rustc_abi::FieldIdx, rustc_abi::
164164

165165
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
166166
match self {
167-
rustc_abi::Variants::Single { index } => {
168-
VariantsShape::Single { index: index.stable(tables) }
169-
}
167+
rustc_abi::Variants::Single { index } => VariantsShape::Single {
168+
index: index.unwrap_or(rustc_abi::VariantIdx::from_u32(0)).stable(tables),
169+
},
170170
rustc_abi::Variants::Multiple { tag, tag_encoding, tag_field, variants } => {
171171
VariantsShape::Multiple {
172172
tag: tag.stable(tables),

‎compiler/rustc_transmute/src/layout/tree.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,14 +339,12 @@ pub(crate) mod rustc {
339339

340340
match layout.variants() {
341341
Variants::Single { index } => {
342-
// Hilariously, `Single` is used even for 0-variant enums;
343-
// `index` is just junk in that case.
344-
if ty.ty_adt_def().unwrap().variants().is_empty() {
345-
Ok(Self::uninhabited())
346-
} else {
342+
if let Some(index) = index {
347343
// `Variants::Single` on enums with variants denotes that
348344
// the enum delegates its layout to the variant at `index`.
349345
layout_of_variant(*index, None)
346+
} else {
347+
Ok(Self::uninhabited())
350348
}
351349
}
352350
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
@@ -504,7 +502,7 @@ pub(crate) mod rustc {
504502
ty::Adt(def, args) => {
505503
match layout.variants {
506504
Variants::Single { index } => {
507-
let field = &def.variant(index).fields[i];
505+
let field = &def.variant(index.unwrap()).fields[i];
508506
field.ty(cx.tcx(), args)
509507
}
510508
// Discriminant field for enums (where applicable).

‎compiler/rustc_ty_utils/src/layout.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ fn layout_of_uncached<'tcx>(
340340
let largest_niche = if count != 0 { element.largest_niche } else { None };
341341

342342
tcx.mk_layout(LayoutData {
343-
variants: Variants::Single { index: FIRST_VARIANT },
343+
variants: Variants::Single { index: Some(FIRST_VARIANT) },
344344
fields: FieldsShape::Array { stride: element.size, count },
345345
backend_repr: abi,
346346
largest_niche,
@@ -353,7 +353,7 @@ fn layout_of_uncached<'tcx>(
353353
ty::Slice(element) => {
354354
let element = cx.layout_of(element)?;
355355
tcx.mk_layout(LayoutData {
356-
variants: Variants::Single { index: FIRST_VARIANT },
356+
variants: Variants::Single { index: Some(FIRST_VARIANT) },
357357
fields: FieldsShape::Array { stride: element.size, count: 0 },
358358
backend_repr: BackendRepr::Memory { sized: false },
359359
largest_niche: None,
@@ -364,7 +364,7 @@ fn layout_of_uncached<'tcx>(
364364
})
365365
}
366366
ty::Str => tcx.mk_layout(LayoutData {
367-
variants: Variants::Single { index: FIRST_VARIANT },
367+
variants: Variants::Single { index: Some(FIRST_VARIANT) },
368368
fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 },
369369
backend_repr: BackendRepr::Memory { sized: false },
370370
largest_niche: None,
@@ -535,7 +535,7 @@ fn layout_of_uncached<'tcx>(
535535
};
536536

537537
tcx.mk_layout(LayoutData {
538-
variants: Variants::Single { index: FIRST_VARIANT },
538+
variants: Variants::Single { index: Some(FIRST_VARIANT) },
539539
fields,
540540
backend_repr: abi,
541541
largest_niche: e_ly.largest_niche,
@@ -927,7 +927,7 @@ fn coroutine_layout<'tcx>(
927927
&ReprOptions::default(),
928928
StructKind::Prefixed(prefix_size, prefix_align.abi),
929929
)?;
930-
variant.variants = Variants::Single { index };
930+
variant.variants = Variants::Single { index: Some(index) };
931931

932932
let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else {
933933
bug!();
@@ -1106,7 +1106,9 @@ fn variant_info_for_adt<'tcx>(
11061106

11071107
match layout.variants {
11081108
Variants::Single { index } => {
1109-
if !adt_def.variants().is_empty() && layout.fields != FieldsShape::Primitive {
1109+
if let Some(index) = index
1110+
&& layout.fields != FieldsShape::Primitive
1111+
{
11101112
debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name);
11111113
let variant_def = &adt_def.variant(index);
11121114
let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();

‎compiler/rustc_ty_utils/src/layout/invariant.rs

Lines changed: 70 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -241,63 +241,81 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
241241

242242
check_layout_abi(cx, layout);
243243

244-
if let Variants::Multiple { variants, tag, tag_encoding, .. } = &layout.variants {
245-
if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } = tag_encoding {
246-
let niche_size = tag.size(cx);
247-
assert!(*niche_start <= niche_size.unsigned_int_max());
248-
for (idx, variant) in variants.iter_enumerated() {
249-
// Ensure all inhabited variants are accounted for.
250-
if !variant.is_uninhabited() {
251-
assert!(idx == *untagged_variant || niche_variants.contains(&idx));
252-
}
253-
}
244+
match &layout.variants {
245+
Variants::Single { index: None } => {
246+
assert!(layout.is_uninhabited());
254247
}
255-
for variant in variants.iter() {
256-
// No nested "multiple".
257-
assert_matches!(variant.variants, Variants::Single { .. });
258-
// Variants should have the same or a smaller size as the full thing,
259-
// and same for alignment.
260-
if variant.size > layout.size {
261-
bug!(
262-
"Type with size {} bytes has variant with size {} bytes: {layout:#?}",
263-
layout.size.bytes(),
264-
variant.size.bytes(),
265-
)
248+
Variants::Single { index: Some(idx) } => {
249+
if let Some(variants) = layout.ty.variant_range(tcx) {
250+
assert!(variants.contains(idx));
251+
} else {
252+
// Types without variants use `0` as dummy variant index.
253+
assert!(idx.as_u32() == 0);
266254
}
267-
if variant.align.abi > layout.align.abi {
268-
bug!(
269-
"Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
270-
layout.align.abi.bytes(),
271-
variant.align.abi.bytes(),
272-
)
273-
}
274-
// Skip empty variants.
275-
if variant.size == Size::ZERO || variant.fields.count() == 0 || variant.is_uninhabited()
255+
}
256+
Variants::Multiple { variants, tag, tag_encoding, .. } => {
257+
if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } =
258+
tag_encoding
276259
{
277-
// These are never actually accessed anyway, so we can skip the coherence check
278-
// for them. They also fail that check, since they have
279-
// `Aggregate`/`Uninhabited` ABI even when the main type is
280-
// `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
281-
// 0, and sometimes, variants without fields have non-0 size.)
282-
continue;
260+
let niche_size = tag.size(cx);
261+
assert!(*niche_start <= niche_size.unsigned_int_max());
262+
for (idx, variant) in variants.iter_enumerated() {
263+
// Ensure all inhabited variants are accounted for.
264+
if !variant.is_uninhabited() {
265+
assert!(idx == *untagged_variant || niche_variants.contains(&idx));
266+
}
267+
}
283268
}
284-
// The top-level ABI and the ABI of the variants should be coherent.
285-
let scalar_coherent =
286-
|s1: Scalar, s2: Scalar| s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx);
287-
let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
288-
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
289-
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
290-
scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
269+
for variant in variants.iter() {
270+
// No nested "multiple".
271+
assert_matches!(variant.variants, Variants::Single { .. });
272+
// Variants should have the same or a smaller size as the full thing,
273+
// and same for alignment.
274+
if variant.size > layout.size {
275+
bug!(
276+
"Type with size {} bytes has variant with size {} bytes: {layout:#?}",
277+
layout.size.bytes(),
278+
variant.size.bytes(),
279+
)
280+
}
281+
if variant.align.abi > layout.align.abi {
282+
bug!(
283+
"Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
284+
layout.align.abi.bytes(),
285+
variant.align.abi.bytes(),
286+
)
287+
}
288+
// Skip empty variants.
289+
if variant.size == Size::ZERO
290+
|| variant.fields.count() == 0
291+
|| variant.is_uninhabited()
292+
{
293+
// These are never actually accessed anyway, so we can skip the coherence check
294+
// for them. They also fail that check, since they have
295+
// `Aggregate`/`Uninhabited` ABI even when the main type is
296+
// `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
297+
// 0, and sometimes, variants without fields have non-0 size.)
298+
continue;
299+
}
300+
// The top-level ABI and the ABI of the variants should be coherent.
301+
let scalar_coherent = |s1: Scalar, s2: Scalar| {
302+
s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx)
303+
};
304+
let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
305+
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
306+
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
307+
scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
308+
}
309+
(BackendRepr::Uninhabited, _) => true,
310+
(BackendRepr::Memory { .. }, _) => true,
311+
_ => false,
312+
};
313+
if !abi_coherent {
314+
bug!(
315+
"Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
316+
variant
317+
);
291318
}
292-
(BackendRepr::Uninhabited, _) => true,
293-
(BackendRepr::Memory { .. }, _) => true,
294-
_ => false,
295-
};
296-
if !abi_coherent {
297-
bug!(
298-
"Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
299-
variant
300-
);
301319
}
302320
}
303321
}

‎compiler/stable_mir/src/abi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ impl FieldsShape {
181181
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
182182
pub enum VariantsShape {
183183
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
184+
// FIXME: needs to become `Option` like in the internal type.
184185
Single { index: VariantIdx },
185186

186187
/// Enum-likes with more than one inhabited variant: each variant comes with

‎src/tools/rust-analyzer/crates/hir-ty/src/layout.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ fn layout_of_simd_ty(
189189
};
190190

191191
Ok(Arc::new(Layout {
192-
variants: Variants::Single { index: struct_variant_idx() },
192+
variants: Variants::Single { index: Some(struct_variant_idx()) },
193193
fields,
194194
backend_repr: BackendRepr::Vector { element: e_abi, count: e_len },
195195
largest_niche: e_ly.largest_niche,
@@ -305,7 +305,7 @@ pub fn layout_of_ty_query(
305305
let largest_niche = if count != 0 { element.largest_niche } else { None };
306306

307307
Layout {
308-
variants: Variants::Single { index: struct_variant_idx() },
308+
variants: Variants::Single { index: Some(struct_variant_idx()) },
309309
fields: FieldsShape::Array { stride: element.size, count },
310310
backend_repr,
311311
largest_niche,
@@ -318,7 +318,7 @@ pub fn layout_of_ty_query(
318318
TyKind::Slice(element) => {
319319
let element = db.layout_of_ty(element.clone(), trait_env)?;
320320
Layout {
321-
variants: Variants::Single { index: struct_variant_idx() },
321+
variants: Variants::Single { index: Some(struct_variant_idx()) },
322322
fields: FieldsShape::Array { stride: element.size, count: 0 },
323323
backend_repr: BackendRepr::Memory { sized: false },
324324
largest_niche: None,
@@ -329,7 +329,7 @@ pub fn layout_of_ty_query(
329329
}
330330
}
331331
TyKind::Str => Layout {
332-
variants: Variants::Single { index: struct_variant_idx() },
332+
variants: Variants::Single { index: Some(struct_variant_idx()) },
333333
fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 },
334334
backend_repr: BackendRepr::Memory { sized: false },
335335
largest_niche: None,

‎src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1639,7 +1639,8 @@ impl Evaluator<'_> {
16391639
};
16401640
match &layout.variants {
16411641
Variants::Single { index } => {
1642-
let r = self.const_eval_discriminant(self.db.enum_data(e).variants[index.0].0)?;
1642+
let r = self
1643+
.const_eval_discriminant(self.db.enum_data(e).variants[index.unwrap().0].0)?;
16431644
Ok(r)
16441645
}
16451646
Variants::Multiple { tag, tag_encoding, variants, .. } => {

‎src/tools/rust-analyzer/crates/hir-ty/src/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ pub(crate) fn detect_variant_from_bytes<'a>(
335335
) -> Option<(EnumVariantId, &'a Layout)> {
336336
let (var_id, var_layout) = match &layout.variants {
337337
hir_def::layout::Variants::Single { index } => {
338-
(db.enum_data(e).variants[index.0].0, layout)
338+
(db.enum_data(e).variants[index.unwrap().0].0, layout)
339339
}
340340
hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
341341
let size = tag.size(target_data_layout).bytes_usize();

‎tests/mir-opt/set_no_discriminant.f.JumpThreading.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
_2 = E::<char>::A;
1111
discriminant(_2) = 1;
1212
_1 = discriminant(_2);
13-
switchInt(copy _1) -> [0: bb1, otherwise: bb2];
13+
- switchInt(copy _1) -> [0: bb1, otherwise: bb2];
14+
+ goto -> bb2;
1415
}
1516

1617
bb1: {

‎tests/mir-opt/set_no_discriminant.generic.JumpThreading.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
_2 = E::<T>::A;
1111
discriminant(_2) = 1;
1212
_1 = discriminant(_2);
13-
switchInt(copy _1) -> [0: bb1, otherwise: bb2];
13+
- switchInt(copy _1) -> [0: bb1, otherwise: bb2];
14+
+ goto -> bb2;
1415
}
1516

1617
bb1: {

‎tests/mir-opt/set_no_discriminant.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant
2-
// of a niche encoding. Verify that we do not thread over this case.
2+
// of a niche encoding. However, it is UB to call `SetDiscriminant` with the untagged variant if the
3+
// value currently encodes a different variant. Verify that we do correctly thread in this case.
34
//@ test-mir-pass: JumpThreading
45

56
#![feature(custom_mir)]
@@ -16,20 +17,21 @@ enum E<T> {
1617
#[custom_mir(dialect = "runtime")]
1718
pub fn f() -> usize {
1819
// CHECK-LABEL: fn f(
19-
// CHECK-NOT: goto
20-
// CHECK: switchInt(
21-
// CHECK-NOT: goto
20+
// CHECK-NOT: switchInt
21+
// CHECK: goto
22+
// CHECK-NOT: switchInt
2223
mir! {
2324
let a: isize;
2425
let e: E<char>;
2526
{
2627
e = E::A;
27-
SetDiscriminant(e, 1);
28+
SetDiscriminant(e, 1); // UB!
2829
a = Discriminant(e);
2930
match a {
3031
0 => bb0,
3132
_ => bb1,
3233
}
34+
3335
}
3436
bb0 = {
3537
RET = 0;
@@ -46,15 +48,15 @@ pub fn f() -> usize {
4648
#[custom_mir(dialect = "runtime")]
4749
pub fn generic<T>() -> usize {
4850
// CHECK-LABEL: fn generic(
49-
// CHECK-NOT: goto
50-
// CHECK: switchInt(
51-
// CHECK-NOT: goto
51+
// CHECK-NOT: switchInt
52+
// CHECK: goto
53+
// CHECK-NOT: switchInt
5254
mir! {
5355
let a: isize;
5456
let e: E<T>;
5557
{
5658
e = E::A;
57-
SetDiscriminant(e, 1);
59+
SetDiscriminant(e, 1); // UB!
5860
a = Discriminant(e);
5961
match a {
6062
0 => bb0,
@@ -72,6 +74,7 @@ pub fn generic<T>() -> usize {
7274
}
7375
}
7476

77+
// CHECK-LABEL: fn main(
7578
fn main() {
7679
assert_eq!(f(), 0);
7780
assert_eq!(generic::<char>(), 0);

0 commit comments

Comments
 (0)
Please sign in to comment.