From c2da477853e1bd8e3a6077d17efabc7dc9b1b792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 17 Feb 2022 00:00:00 +0000 Subject: [PATCH] Fix pretty printing of enums without variants 92d20c4aaddea9507f8ad37fe37c551219153bbf removed no-variants special case from try_destructure_const with expectation that this case would be handled gracefully when read_discriminant returns an error. Alas in that case read_discriminant succeeds while returning a non-existing variant, so the special case is still necessary. --- .../rustc_const_eval/src/const_eval/mod.rs | 4 + .../invalid_constant.main.ConstProp.diff | 95 +++++++++---------- .../mir-opt/const_prop/invalid_constant.rs | 41 +++++--- 3 files changed, 75 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index ba1d5f45bbb..724f92243d0 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -147,6 +147,10 @@ pub(crate) fn try_destructure_const<'tcx>( // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match val.ty().kind() { ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), + // Checks if we have any variants, to avoid downcasting to a non-existing variant (when + // there are no variants `read_discriminant` successfully returns a non-existing variant + // index). + ty::Adt(def, _) if def.variants.is_empty() => throw_ub!(Unreachable), ty::Adt(def, _) => { let variant = ecx.read_discriminant(&op)?.1; let down = ecx.operand_downcast(&op, variant)?; diff --git a/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff b/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff index 1b53318806f..03f827f63f3 100644 --- a/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff @@ -2,68 +2,63 @@ + // MIR for `main` after ConstProp fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/invalid_constant.rs:15:11: 15:11 - let _1: std::option::Option<()>; // in scope 0 at $DIR/invalid_constant.rs:16:5: 16:12 - let mut _2: std::option::Option>; // in scope 0 at $DIR/invalid_constant.rs:16:7: 16:11 - let _3: main::Union; // in scope 0 at $DIR/invalid_constant.rs:22:9: 22:22 + let mut _0: (); // return place in scope 0 at $DIR/invalid_constant.rs:13:11: 13:11 + let _1: main::InvalidChar; // in scope 0 at $DIR/invalid_constant.rs:19:9: 19:22 + let mut _3: main::InvalidTag; // in scope 0 at $DIR/invalid_constant.rs:26:25: 26:46 + let mut _5: main::NoVariants; // in scope 0 at $DIR/invalid_constant.rs:33:35: 33:56 scope 1 { - debug _invalid_char => _3; // in scope 1 at $DIR/invalid_constant.rs:22:9: 22:22 - } - scope 2 (inlined f) { // at $DIR/invalid_constant.rs:16:5: 16:12 - debug x => _2; // in scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 - let mut _4: isize; // in scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 - let _5: std::option::Option<()>; // in scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 - scope 3 { - debug y => _5; // in scope 3 at $DIR/invalid_constant.rs:16:5: 16:12 + debug _invalid_char => _1; // in scope 1 at $DIR/invalid_constant.rs:19:9: 19:22 + let _2: [main::InvalidTag; 1]; // in scope 1 at $DIR/invalid_constant.rs:26:9: 26:21 + scope 2 { + debug _invalid_tag => _2; // in scope 2 at $DIR/invalid_constant.rs:26:9: 26:21 + let _4: [main::NoVariants; 1]; // in scope 2 at $DIR/invalid_constant.rs:33:9: 33:31 + scope 3 { + debug _enum_without_variants => _4; // in scope 3 at $DIR/invalid_constant.rs:33:9: 33:31 + } } } bb0: { - discriminant(_2) = 0; // scope 0 at $DIR/invalid_constant.rs:16:7: 16:11 -- _4 = discriminant(_2); // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 -- switchInt(move _4) -> [0_isize: bb3, otherwise: bb2]; // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 -+ _4 = const 0_isize; // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 -+ switchInt(const 0_isize) -> [0_isize: bb3, otherwise: bb2]; // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 - } - - bb1: { -- _3 = const { Union { int: 0x110001 } }; // scope 0 at $DIR/invalid_constant.rs:22:25: 22:58 -+ _3 = const main::Union { int: 1114113_u32, chr: {transmute(0x00110001): char} }; // scope 0 at $DIR/invalid_constant.rs:22:25: 22:58 + StorageLive(_1); // scope 0 at $DIR/invalid_constant.rs:19:9: 19:22 +- _1 = const { InvalidChar { int: 0x110001 } }; // scope 0 at $DIR/invalid_constant.rs:19:25: 19:64 ++ _1 = const InvalidChar { int: 1114113_u32, chr: {transmute(0x00110001): char} }; // scope 0 at $DIR/invalid_constant.rs:19:25: 19:64 // ty::Const - // + ty: main::Union -- // + val: Unevaluated(main::{constant#0}, [main::Union], None) + // + ty: main::InvalidChar +- // + val: Unevaluated(main::{constant#0}, [main::InvalidChar], None) + // + val: Value(Scalar(0x00110001)) // mir::Constant - // + span: $DIR/invalid_constant.rs:22:25: 22:58 -- // + literal: Const { ty: main::Union, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:8 ~ invalid_constant[726d]::main::{constant#0}), const_param_did: None }, substs: [main::Union], promoted: None }) } -+ // + literal: Const { ty: main::Union, val: Value(Scalar(0x00110001)) } - nop; // scope 0 at $DIR/invalid_constant.rs:15:11: 23:2 - return; // scope 0 at $DIR/invalid_constant.rs:23:2: 23:2 - } - - bb2: { -- _5 = ((_2 as Some).0: std::option::Option<()>); // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 -- _1 = _5; // scope 3 at $DIR/invalid_constant.rs:16:5: 16:12 -+ _5 = const Scalar(0x02): Option::<()>; // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 + // + span: $DIR/invalid_constant.rs:19:25: 19:64 +- // + literal: Const { ty: main::InvalidChar, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:7 ~ invalid_constant[726d]::main::{constant#0}), const_param_did: None }, substs: [main::InvalidChar], promoted: None }) } ++ // + literal: Const { ty: main::InvalidChar, val: Value(Scalar(0x00110001)) } + StorageLive(_2); // scope 1 at $DIR/invalid_constant.rs:26:9: 26:21 + StorageLive(_3); // scope 1 at $DIR/invalid_constant.rs:26:25: 26:46 + (_3.0: u32) = const 4_u32; // scope 1 at $DIR/invalid_constant.rs:26:25: 26:46 +- _2 = [move _3]; // scope 1 at $DIR/invalid_constant.rs:26:24: 26:47 ++ _2 = [const InvalidTag { int: 4_u32, e: Scalar(0x00000004): E }]; // scope 1 at $DIR/invalid_constant.rs:26:24: 26:47 + // ty::Const -+ // + ty: std::option::Option<()> -+ // + val: Value(Scalar(0x02)) ++ // + ty: main::InvalidTag ++ // + val: Value(Scalar(0x00000004)) + // mir::Constant -+ // + span: $DIR/invalid_constant.rs:16:5: 16:12 -+ // + literal: Const { ty: std::option::Option<()>, val: Value(Scalar(0x02)) } -+ _1 = const Scalar(0x02): Option::<()>; // scope 3 at $DIR/invalid_constant.rs:16:5: 16:12 ++ // + span: $DIR/invalid_constant.rs:26:24: 26:47 ++ // + literal: Const { ty: main::InvalidTag, val: Value(Scalar(0x00000004)) } + StorageDead(_3); // scope 1 at $DIR/invalid_constant.rs:26:46: 26:47 + StorageLive(_4); // scope 2 at $DIR/invalid_constant.rs:33:9: 33:31 + StorageLive(_5); // scope 2 at $DIR/invalid_constant.rs:33:35: 33:56 + (_5.0: u32) = const 0_u32; // scope 2 at $DIR/invalid_constant.rs:33:35: 33:56 +- _4 = [move _5]; // scope 2 at $DIR/invalid_constant.rs:33:34: 33:57 ++ _4 = [const NoVariants { int: 0_u32, empty: Scalar(): Empty }]; // scope 2 at $DIR/invalid_constant.rs:33:34: 33:57 + // ty::Const -+ // + ty: std::option::Option<()> -+ // + val: Value(Scalar(0x02)) ++ // + ty: main::NoVariants ++ // + val: Value(Scalar(0x00000000)) + // mir::Constant -+ // + span: $DIR/invalid_constant.rs:16:5: 16:12 -+ // + literal: Const { ty: std::option::Option<()>, val: Value(Scalar(0x02)) } - goto -> bb1; // scope 0 at $DIR/invalid_constant.rs:10:20: 10:21 - } - - bb3: { - discriminant(_1) = 0; // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12 - goto -> bb1; // scope 0 at $DIR/invalid_constant.rs:9:17: 9:21 ++ // + span: $DIR/invalid_constant.rs:33:34: 33:57 ++ // + literal: Const { ty: main::NoVariants, val: Value(Scalar(0x00000000)) } + StorageDead(_5); // scope 2 at $DIR/invalid_constant.rs:33:56: 33:57 + nop; // scope 0 at $DIR/invalid_constant.rs:13:11: 34:2 + StorageDead(_4); // scope 2 at $DIR/invalid_constant.rs:34:1: 34:2 + StorageDead(_2); // scope 1 at $DIR/invalid_constant.rs:34:1: 34:2 + StorageDead(_1); // scope 0 at $DIR/invalid_constant.rs:34:1: 34:2 + return; // scope 0 at $DIR/invalid_constant.rs:34:2: 34:2 } } diff --git a/src/test/mir-opt/const_prop/invalid_constant.rs b/src/test/mir-opt/const_prop/invalid_constant.rs index 4aca9090019..e0879cf4800 100644 --- a/src/test/mir-opt/const_prop/invalid_constant.rs +++ b/src/test/mir-opt/const_prop/invalid_constant.rs @@ -1,23 +1,34 @@ -// Verify that we can pretty print invalid constant introduced -// by constant propagation. Regression test for issue #93688. -// -// compile-flags: -Copt-level=0 -Zinline-mir +// Verify that we can pretty print invalid constants. + #![feature(inline_const)] -#[inline(always)] -pub fn f(x: Option>) -> Option<()> { - match x { - None => None, - Some(y) => y, - } -} + +#[derive(Copy, Clone)] +#[repr(u32)] +enum E { A, B, C } + +#[derive(Copy, Clone)] +enum Empty {} // EMIT_MIR invalid_constant.main.ConstProp.diff fn main() { - f(None); - - union Union { + // An invalid char. + union InvalidChar { int: u32, chr: char, } - let _invalid_char = const { Union { int: 0x110001 } }; + let _invalid_char = const { InvalidChar { int: 0x110001 } }; + + // An enum with an invalid tag. Regression test for #93688. + union InvalidTag { + int: u32, + e: E, + } + let _invalid_tag = [InvalidTag { int: 4 }]; + + // An enum without variants. Regression test for #94073. + union NoVariants { + int: u32, + empty: Empty, + } + let _enum_without_variants = [NoVariants { int: 0 }]; }