Skip to content

Commit db7203d

Browse files
authored
Rollup merge of #73442 - davidtwco:issue-72181-pretty-print-const-val-enum-no-variants, r=oli-obk
pretty/mir: const value enums with no variants Fixes #72181. This PR modifies the pretty printer and const eval in the MIR so that `destructure_const` (used in `pretty_print_const_value`) can handle enums with no variants (or types containing enums with no variants). I'm not convinced that this is the correct approach, folks more familiar with `destructure_const` would be able to say - happy to adjust the PR. Looking through `destructure_const` and the functions that it invokes, it didn't seem like it was written to handle zero-variant-enums - I assume that case is handled earlier in some way so `destructure_const` doesn't need to under normal circumstances. It didn't seem like it would be straightforward to make `destructure_const` handle this case in a first-class-feeling way (e.g. adding a `Variants::None` variant), so this PR makes some minimal changes to avoid ICEs.
2 parents d69d4c3 + 6fa7dc6 commit db7203d

21 files changed

+654
-13
lines changed

src/librustc_middle/mir/query.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,6 @@ pub enum ClosureOutlivesSubject<'tcx> {
244244
/// The constituent parts of an ADT or array.
245245
#[derive(Copy, Clone, Debug, HashStable)]
246246
pub struct DestructuredConst<'tcx> {
247-
pub variant: VariantIdx,
247+
pub variant: Option<VariantIdx>,
248248
pub fields: &'tcx [&'tcx ty::Const<'tcx>],
249249
}

src/librustc_middle/ty/layout.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,8 @@ where
20012001
}
20022002

20032003
let fields = match this.ty.kind {
2004+
ty::Adt(def, _) if def.variants.is_empty() =>
2005+
bug!("for_variant called on zero-variant enum"),
20042006
ty::Adt(def, _) => def.variants[variant_index].fields.len(),
20052007
_ => bug!(),
20062008
};

src/librustc_middle/ty/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,6 +2352,7 @@ impl<'tcx> AdtDef {
23522352
/// Alternatively, if there is no explicit discriminant, returns the
23532353
/// inferred discriminant directly.
23542354
pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option<DefId>, u32) {
2355+
assert!(!self.variants.is_empty());
23552356
let mut explicit_index = variant_index.as_u32();
23562357
let expr_did;
23572358
loop {

src/librustc_middle/ty/print/pretty.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1177,8 +1177,13 @@ pub trait PrettyPrinter<'tcx>:
11771177
}
11781178
p!(write(")"));
11791179
}
1180+
ty::Adt(def, substs) if def.variants.is_empty() => {
1181+
p!(print_value_path(def.did, substs));
1182+
}
11801183
ty::Adt(def, substs) => {
1181-
let variant_def = &def.variants[contents.variant];
1184+
let variant_id =
1185+
contents.variant.expect("destructed const of adt without variant id");
1186+
let variant_def = &def.variants[variant_id];
11821187
p!(print_value_path(variant_def.def_id, substs));
11831188

11841189
match variant_def.ctor_kind {

src/librustc_middle/ty/sty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,9 @@ impl<'tcx> TyS<'tcx> {
20992099
variant_index: VariantIdx,
21002100
) -> Option<Discr<'tcx>> {
21012101
match self.kind {
2102+
TyKind::Adt(adt, _) if adt.variants.is_empty() => {
2103+
bug!("discriminant_for_variant called on zero variant enum");
2104+
}
21022105
TyKind::Adt(adt, _) if adt.is_enum() => {
21032106
Some(adt.discriminant_for_variant(tcx, variant_index))
21042107
}

src/librustc_mir/const_eval/mod.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ pub(crate) fn const_caller_location(
3030
ConstValue::Scalar(loc_place.ptr)
3131
}
3232

33-
// this function uses `unwrap` copiously, because an already validated constant
34-
// must have valid fields and can thus never fail outside of compiler bugs
33+
/// This function uses `unwrap` copiously, because an already validated constant
34+
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
35+
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
36+
/// `read_discriminant` needs to be able to handle that.
3537
pub(crate) fn destructure_const<'tcx>(
3638
tcx: TyCtxt<'tcx>,
3739
param_env: ty::ParamEnv<'tcx>,
@@ -41,17 +43,21 @@ pub(crate) fn destructure_const<'tcx>(
4143
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
4244
let op = ecx.eval_const_to_op(val, None).unwrap();
4345

44-
let variant = ecx.read_discriminant(op).unwrap().1;
45-
4646
// We go to `usize` as we cannot allocate anything bigger anyway.
47-
let field_count = match val.ty.kind {
48-
ty::Array(_, len) => usize::try_from(len.eval_usize(tcx, param_env)).unwrap(),
49-
ty::Adt(def, _) => def.variants[variant].fields.len(),
50-
ty::Tuple(substs) => substs.len(),
47+
let (field_count, variant, down) = match val.ty.kind {
48+
ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
49+
ty::Adt(def, _) if def.variants.is_empty() => {
50+
return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) };
51+
}
52+
ty::Adt(def, _) => {
53+
let variant = ecx.read_discriminant(op).unwrap().1;
54+
let down = ecx.operand_downcast(op, variant).unwrap();
55+
(def.variants[variant].fields.len(), Some(variant), down)
56+
}
57+
ty::Tuple(substs) => (substs.len(), None, op),
5158
_ => bug!("cannot destructure constant {:?}", val),
5259
};
5360

54-
let down = ecx.operand_downcast(op, variant).unwrap();
5561
let fields_iter = (0..field_count).map(|i| {
5662
let field_op = ecx.operand_field(down, i).unwrap();
5763
let val = op_to_const(&ecx, field_op);

src/librustc_mir_build/hair/pattern/_match.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,11 @@ impl<'tcx> Constructor<'tcx> {
800800
assert!(!adt.is_enum());
801801
VariantIdx::new(0)
802802
}
803-
ConstantValue(c) => cx.tcx.destructure_const(cx.param_env.and(c)).variant,
803+
ConstantValue(c) => cx
804+
.tcx
805+
.destructure_const(cx.param_env.and(c))
806+
.variant
807+
.expect("destructed const of adt without variant id"),
804808
_ => bug!("bad constructor {:?} for adt {:?}", self, adt),
805809
}
806810
}

src/librustc_mir_build/hair/pattern/const_to_pat.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
275275
PatKind::Variant {
276276
adt_def,
277277
substs,
278-
variant_index: destructured.variant,
278+
variant_index: destructured
279+
.variant
280+
.expect("destructed const of adt without variant id"),
279281
subpatterns: field_pats(destructured.fields),
280282
}
281283
}

src/test/mir-opt/issue-72181-1.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-flags: -Z mir-opt-level=1
2+
// Regression test for #72181, this ICE requires `-Z mir-opt-level=1` flags.
3+
4+
#![feature(never_type)]
5+
#![allow(unused, invalid_value)]
6+
7+
enum Void {}
8+
9+
// EMIT_MIR rustc.f.mir_map.0.mir
10+
fn f(v: Void) -> ! {
11+
match v {}
12+
}
13+
14+
// EMIT_MIR rustc.main.mir_map.0.mir
15+
fn main() {
16+
let v: Void = unsafe {
17+
std::mem::transmute::<(), Void>(())
18+
};
19+
20+
f(v);
21+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// MIR for `f` 0 mir_map
2+
3+
fn f(_1: Void) -> ! {
4+
debug v => _1; // in scope 0 at $DIR/issue-72181-1.rs:10:6: 10:7
5+
let mut _0: !; // return place in scope 0 at $DIR/issue-72181-1.rs:10:18: 10:19
6+
let mut _2: !; // in scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
7+
let mut _3: !; // in scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15
8+
9+
bb0: {
10+
StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
11+
StorageLive(_3); // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15
12+
FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
13+
unreachable; // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
14+
}
15+
16+
bb1 (cleanup): {
17+
resume; // scope 0 at $DIR/issue-72181-1.rs:10:1: 12:2
18+
}
19+
20+
bb2: {
21+
unreachable; // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15
22+
}
23+
24+
bb3: {
25+
StorageDead(_3); // scope 0 at $DIR/issue-72181-1.rs:11:14: 11:15
26+
unreachable; // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
27+
}
28+
29+
bb4: {
30+
StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:12:1: 12:2
31+
goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:12:2: 12:2
32+
}
33+
34+
bb5: {
35+
return; // scope 0 at $DIR/issue-72181-1.rs:12:2: 12:2
36+
}
37+
}

0 commit comments

Comments
 (0)