Skip to content

Commit

Permalink
Rollup merge of #73442 - davidtwco:issue-72181-pretty-print-const-val…
Browse files Browse the repository at this point in the history
…-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.
  • Loading branch information
Manishearth committed Jun 20, 2020
2 parents d69d4c3 + 6fa7dc6 commit db7203d
Show file tree
Hide file tree
Showing 21 changed files with 654 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/librustc_middle/mir/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,6 @@ pub enum ClosureOutlivesSubject<'tcx> {
/// The constituent parts of an ADT or array.
#[derive(Copy, Clone, Debug, HashStable)]
pub struct DestructuredConst<'tcx> {
pub variant: VariantIdx,
pub variant: Option<VariantIdx>,
pub fields: &'tcx [&'tcx ty::Const<'tcx>],
}
2 changes: 2 additions & 0 deletions src/librustc_middle/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,8 @@ where
}

let fields = match this.ty.kind {
ty::Adt(def, _) if def.variants.is_empty() =>
bug!("for_variant called on zero-variant enum"),
ty::Adt(def, _) => def.variants[variant_index].fields.len(),
_ => bug!(),
};
Expand Down
1 change: 1 addition & 0 deletions src/librustc_middle/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2352,6 +2352,7 @@ impl<'tcx> AdtDef {
/// Alternatively, if there is no explicit discriminant, returns the
/// inferred discriminant directly.
pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option<DefId>, u32) {
assert!(!self.variants.is_empty());
let mut explicit_index = variant_index.as_u32();
let expr_did;
loop {
Expand Down
7 changes: 6 additions & 1 deletion src/librustc_middle/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1177,8 +1177,13 @@ pub trait PrettyPrinter<'tcx>:
}
p!(write(")"));
}
ty::Adt(def, substs) if def.variants.is_empty() => {
p!(print_value_path(def.did, substs));
}
ty::Adt(def, substs) => {
let variant_def = &def.variants[contents.variant];
let variant_id =
contents.variant.expect("destructed const of adt without variant id");
let variant_def = &def.variants[variant_id];
p!(print_value_path(variant_def.def_id, substs));

match variant_def.ctor_kind {
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_middle/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2099,6 +2099,9 @@ impl<'tcx> TyS<'tcx> {
variant_index: VariantIdx,
) -> Option<Discr<'tcx>> {
match self.kind {
TyKind::Adt(adt, _) if adt.variants.is_empty() => {
bug!("discriminant_for_variant called on zero variant enum");
}
TyKind::Adt(adt, _) if adt.is_enum() => {
Some(adt.discriminant_for_variant(tcx, variant_index))
}
Expand Down
24 changes: 15 additions & 9 deletions src/librustc_mir/const_eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ pub(crate) fn const_caller_location(
ConstValue::Scalar(loc_place.ptr)
}

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

let variant = ecx.read_discriminant(op).unwrap().1;

// We go to `usize` as we cannot allocate anything bigger anyway.
let field_count = match val.ty.kind {
ty::Array(_, len) => usize::try_from(len.eval_usize(tcx, param_env)).unwrap(),
ty::Adt(def, _) => def.variants[variant].fields.len(),
ty::Tuple(substs) => substs.len(),
let (field_count, variant, down) = match val.ty.kind {
ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
ty::Adt(def, _) if def.variants.is_empty() => {
return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) };
}
ty::Adt(def, _) => {
let variant = ecx.read_discriminant(op).unwrap().1;
let down = ecx.operand_downcast(op, variant).unwrap();
(def.variants[variant].fields.len(), Some(variant), down)
}
ty::Tuple(substs) => (substs.len(), None, op),
_ => bug!("cannot destructure constant {:?}", val),
};

let down = ecx.operand_downcast(op, variant).unwrap();
let fields_iter = (0..field_count).map(|i| {
let field_op = ecx.operand_field(down, i).unwrap();
let val = op_to_const(&ecx, field_op);
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_mir_build/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,11 @@ impl<'tcx> Constructor<'tcx> {
assert!(!adt.is_enum());
VariantIdx::new(0)
}
ConstantValue(c) => cx.tcx.destructure_const(cx.param_env.and(c)).variant,
ConstantValue(c) => cx
.tcx
.destructure_const(cx.param_env.and(c))
.variant
.expect("destructed const of adt without variant id"),
_ => bug!("bad constructor {:?} for adt {:?}", self, adt),
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_mir_build/hair/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
PatKind::Variant {
adt_def,
substs,
variant_index: destructured.variant,
variant_index: destructured
.variant
.expect("destructed const of adt without variant id"),
subpatterns: field_pats(destructured.fields),
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/test/mir-opt/issue-72181-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// compile-flags: -Z mir-opt-level=1
// Regression test for #72181, this ICE requires `-Z mir-opt-level=1` flags.

#![feature(never_type)]
#![allow(unused, invalid_value)]

enum Void {}

// EMIT_MIR rustc.f.mir_map.0.mir
fn f(v: Void) -> ! {
match v {}
}

// EMIT_MIR rustc.main.mir_map.0.mir
fn main() {
let v: Void = unsafe {
std::mem::transmute::<(), Void>(())
};

f(v);
}
37 changes: 37 additions & 0 deletions src/test/mir-opt/issue-72181-1/rustc.f.mir_map.0.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// MIR for `f` 0 mir_map

fn f(_1: Void) -> ! {
debug v => _1; // in scope 0 at $DIR/issue-72181-1.rs:10:6: 10:7
let mut _0: !; // return place in scope 0 at $DIR/issue-72181-1.rs:10:18: 10:19
let mut _2: !; // in scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
let mut _3: !; // in scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15

bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
StorageLive(_3); // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15
FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
unreachable; // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
}

bb1 (cleanup): {
resume; // scope 0 at $DIR/issue-72181-1.rs:10:1: 12:2
}

bb2: {
unreachable; // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15
}

bb3: {
StorageDead(_3); // scope 0 at $DIR/issue-72181-1.rs:11:14: 11:15
unreachable; // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
}

bb4: {
StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:12:1: 12:2
goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:12:2: 12:2
}

bb5: {
return; // scope 0 at $DIR/issue-72181-1.rs:12:2: 12:2
}
}
67 changes: 67 additions & 0 deletions src/test/mir-opt/issue-72181-1/rustc.main.mir_map.0.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// MIR for `main` 0 mir_map

| User Type Annotations
| 0: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16
| 1: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16
|
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/issue-72181-1.rs:15:11: 15:11
let mut _1: !; // in scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2
let _2: Void as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
let mut _3: (); // in scope 0 at $DIR/issue-72181-1.rs:17:41: 17:43
let _4: !; // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9
let mut _5: Void; // in scope 0 at $DIR/issue-72181-1.rs:20:7: 20:8
scope 1 {
debug v => _2; // in scope 1 at $DIR/issue-72181-1.rs:16:9: 16:10
}
scope 2 {
}

bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
StorageLive(_3); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
_3 = (); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
_2 = const std::intrinsics::transmute::<(), Void>(move _3) -> [return: bb2, unwind: bb1]; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44
// ty::Const
// + ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>}
// + val: Value(Scalar(<ZST>))
// mir::Constant
// + span: $DIR/issue-72181-1.rs:17:9: 17:40
// + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>}, val: Value(Scalar(<ZST>)) }
}

bb1 (cleanup): {
resume; // scope 0 at $DIR/issue-72181-1.rs:15:1: 21:2
}

bb2: {
StorageDead(_3); // scope 2 at $DIR/issue-72181-1.rs:17:43: 17:44
FakeRead(ForLet, _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16
StorageLive(_4); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
_5 = move _2; // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
const f(move _5) -> bb1; // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
// ty::Const
// + ty: fn(Void) -> ! {f}
// + val: Value(Scalar(<ZST>))
// mir::Constant
// + span: $DIR/issue-72181-1.rs:20:5: 20:6
// + literal: Const { ty: fn(Void) -> ! {f}, val: Value(Scalar(<ZST>)) }
}

bb3: {
StorageDead(_5); // scope 1 at $DIR/issue-72181-1.rs:20:8: 20:9
StorageDead(_4); // scope 1 at $DIR/issue-72181-1.rs:20:9: 20:10
StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:21:1: 21:2
unreachable; // scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2
}

bb4: {
goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2
}

bb5: {
return; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2
}
}
28 changes: 28 additions & 0 deletions src/test/mir-opt/issue-72181.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// compile-flags: -Z mir-opt-level=1
// Regression test for #72181, this ICE requires `-Z mir-opt-level=1` flags.

use std::mem;

#[derive(Copy, Clone)]
enum Never {}

union Foo {
a: u64,
b: Never
}

// EMIT_MIR_FOR_EACH_BIT_WIDTH
// EMIT_MIR rustc.foo.mir_map.0.mir
fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 }

// EMIT_MIR rustc.bar.mir_map.0.mir
fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x }

// EMIT_MIR_FOR_EACH_BIT_WIDTH
// EMIT_MIR rustc.main.mir_map.0.mir
fn main() {
let _ = mem::size_of::<Foo>();

let f = [Foo { a: 42 }, Foo { a: 10 }];
let _ = unsafe { f[0].a };
}
25 changes: 25 additions & 0 deletions src/test/mir-opt/issue-72181/32bit/rustc.bar.mir_map.0.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// MIR for `bar` 0 mir_map

fn bar(_1: [(Never, u32); 1]) -> u32 {
let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:19:40: 19:43
let _2: u32; // in scope 0 at $DIR/issue-72181.rs:19:13: 19:14
scope 1 {
debug x => _2; // in scope 1 at $DIR/issue-72181.rs:19:13: 19:14
}

bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14
_2 = (_1[0 of 1].1: u32); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14
_0 = _2; // scope 1 at $DIR/issue-72181.rs:19:46: 19:47
StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:19:48: 19:49
goto -> bb2; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49
}

bb1 (cleanup): {
resume; // scope 0 at $DIR/issue-72181.rs:19:1: 19:49
}

bb2: {
return; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49
}
}
37 changes: 37 additions & 0 deletions src/test/mir-opt/issue-72181/32bit/rustc.foo.mir_map.0.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// MIR for `foo` 0 mir_map

fn foo(_1: [(Never, u32); 1]) -> u32 {
debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10
let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37
let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44
let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45
let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45

bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44
_2 = const 0usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44
// ty::Const
// + ty: usize
// + val: Value(Scalar(0x00000000))
// mir::Constant
// + span: $DIR/issue-72181.rs:16:43: 16:44
// + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) }
_3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45
_4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45
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
}

bb1 (cleanup): {
resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49
}

bb2: {
_0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47
StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49
goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49
}

bb3: {
return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49
}
}
Loading

0 comments on commit db7203d

Please sign in to comment.