Skip to content

Commit 6fa7dc6

Browse files
committed
pretty/mir: const value enums with no variants
This commit 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). Signed-off-by: David Wood <[email protected]>
1 parent 63b441a commit 6fa7dc6

21 files changed

+654
-13
lines changed

src/librustc_middle/mir/query.rs

+1-1
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

+2
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

+1
Original file line numberDiff line numberDiff line change
@@ -2339,6 +2339,7 @@ impl<'tcx> AdtDef {
23392339
/// Alternatively, if there is no explicit discriminant, returns the
23402340
/// inferred discriminant directly.
23412341
pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option<DefId>, u32) {
2342+
assert!(!self.variants.is_empty());
23422343
let mut explicit_index = variant_index.as_u32();
23432344
let expr_did;
23442345
loop {

src/librustc_middle/ty/print/pretty.rs

+6-1
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

+3
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

+15-9
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

+5-1
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

+3-1
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

+21
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+
}
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// MIR for `main` 0 mir_map
2+
3+
| User Type Annotations
4+
| 0: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16
5+
| 1: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16
6+
|
7+
fn main() -> () {
8+
let mut _0: (); // return place in scope 0 at $DIR/issue-72181-1.rs:15:11: 15:11
9+
let mut _1: !; // in scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2
10+
let _2: Void as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
11+
let mut _3: (); // in scope 0 at $DIR/issue-72181-1.rs:17:41: 17:43
12+
let _4: !; // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9
13+
let mut _5: Void; // in scope 0 at $DIR/issue-72181-1.rs:20:7: 20:8
14+
scope 1 {
15+
debug v => _2; // in scope 1 at $DIR/issue-72181-1.rs:16:9: 16:10
16+
}
17+
scope 2 {
18+
}
19+
20+
bb0: {
21+
StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
22+
StorageLive(_3); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
23+
_3 = (); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
24+
_2 = const std::intrinsics::transmute::<(), Void>(move _3) -> [return: bb2, unwind: bb1]; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44
25+
// ty::Const
26+
// + ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>}
27+
// + val: Value(Scalar(<ZST>))
28+
// mir::Constant
29+
// + span: $DIR/issue-72181-1.rs:17:9: 17:40
30+
// + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>}, val: Value(Scalar(<ZST>)) }
31+
}
32+
33+
bb1 (cleanup): {
34+
resume; // scope 0 at $DIR/issue-72181-1.rs:15:1: 21:2
35+
}
36+
37+
bb2: {
38+
StorageDead(_3); // scope 2 at $DIR/issue-72181-1.rs:17:43: 17:44
39+
FakeRead(ForLet, _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
40+
AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16
41+
StorageLive(_4); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
42+
StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
43+
_5 = move _2; // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
44+
const f(move _5) -> bb1; // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
45+
// ty::Const
46+
// + ty: fn(Void) -> ! {f}
47+
// + val: Value(Scalar(<ZST>))
48+
// mir::Constant
49+
// + span: $DIR/issue-72181-1.rs:20:5: 20:6
50+
// + literal: Const { ty: fn(Void) -> ! {f}, val: Value(Scalar(<ZST>)) }
51+
}
52+
53+
bb3: {
54+
StorageDead(_5); // scope 1 at $DIR/issue-72181-1.rs:20:8: 20:9
55+
StorageDead(_4); // scope 1 at $DIR/issue-72181-1.rs:20:9: 20:10
56+
StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:21:1: 21:2
57+
unreachable; // scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2
58+
}
59+
60+
bb4: {
61+
goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2
62+
}
63+
64+
bb5: {
65+
return; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2
66+
}
67+
}

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

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// compile-flags: -Z mir-opt-level=1
2+
// Regression test for #72181, this ICE requires `-Z mir-opt-level=1` flags.
3+
4+
use std::mem;
5+
6+
#[derive(Copy, Clone)]
7+
enum Never {}
8+
9+
union Foo {
10+
a: u64,
11+
b: Never
12+
}
13+
14+
// EMIT_MIR_FOR_EACH_BIT_WIDTH
15+
// EMIT_MIR rustc.foo.mir_map.0.mir
16+
fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 }
17+
18+
// EMIT_MIR rustc.bar.mir_map.0.mir
19+
fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x }
20+
21+
// EMIT_MIR_FOR_EACH_BIT_WIDTH
22+
// EMIT_MIR rustc.main.mir_map.0.mir
23+
fn main() {
24+
let _ = mem::size_of::<Foo>();
25+
26+
let f = [Foo { a: 42 }, Foo { a: 10 }];
27+
let _ = unsafe { f[0].a };
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// MIR for `bar` 0 mir_map
2+
3+
fn bar(_1: [(Never, u32); 1]) -> u32 {
4+
let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:19:40: 19:43
5+
let _2: u32; // in scope 0 at $DIR/issue-72181.rs:19:13: 19:14
6+
scope 1 {
7+
debug x => _2; // in scope 1 at $DIR/issue-72181.rs:19:13: 19:14
8+
}
9+
10+
bb0: {
11+
StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14
12+
_2 = (_1[0 of 1].1: u32); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14
13+
_0 = _2; // scope 1 at $DIR/issue-72181.rs:19:46: 19:47
14+
StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:19:48: 19:49
15+
goto -> bb2; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49
16+
}
17+
18+
bb1 (cleanup): {
19+
resume; // scope 0 at $DIR/issue-72181.rs:19:1: 19:49
20+
}
21+
22+
bb2: {
23+
return; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// MIR for `foo` 0 mir_map
2+
3+
fn foo(_1: [(Never, u32); 1]) -> u32 {
4+
debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10
5+
let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37
6+
let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44
7+
let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45
8+
let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45
9+
10+
bb0: {
11+
StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44
12+
_2 = const 0usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44
13+
// ty::Const
14+
// + ty: usize
15+
// + val: Value(Scalar(0x00000000))
16+
// mir::Constant
17+
// + span: $DIR/issue-72181.rs:16:43: 16:44
18+
// + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) }
19+
_3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45
20+
_4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45
21+
assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:16:40: 16:45
22+
}
23+
24+
bb1 (cleanup): {
25+
resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49
26+
}
27+
28+
bb2: {
29+
_0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47
30+
StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49
31+
goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49
32+
}
33+
34+
bb3: {
35+
return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49
36+
}
37+
}

0 commit comments

Comments
 (0)