diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 29cbb7f07e87a..5cd236b10a440 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -89,6 +89,9 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( } ty::Adt(def, _) => { let variant = ecx.read_discriminant(&op).ok()?; + if op.layout.for_variant(&ecx, variant).abi.is_uninhabited() { + return None; + } let down = ecx.project_downcast(&op, variant).ok()?; (def.variants()[variant].fields.len(), Some(variant), down) } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 390ec3e1a36ac..f26ce0404ff54 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -577,7 +577,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } } - ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index), + ProjectionElem::Downcast(name, index) => { + if let Some(ct) = self.eval_to_const(value) + && ct.layout.for_variant(&self.ecx, index).abi.is_uninhabited() + { + return None; + } + ProjectionElem::Downcast(name, index) + } ProjectionElem::Field(f, ty) => { if let Value::Aggregate(_, _, fields) = self.get(value) { return Some(fields[f.as_usize()]); diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 7a70ed5cb7f0e..87493f37d1b4c 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -370,7 +370,12 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { constant, &mut |elem, op| match elem { TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(), - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), + TrackElem::Variant(idx) => { + if op.layout.for_variant(&self.ecx, idx).abi.is_uninhabited() { + return None; + } + self.ecx.project_downcast(op, idx).ok() + } TrackElem::Discriminant => { let variant = self.ecx.read_discriminant(op).ok()?; let discr_value = diff --git a/tests/mir-opt/gvn_uninhabited.f.GVN.panic-abort.diff b/tests/mir-opt/gvn_uninhabited.f.GVN.panic-abort.diff new file mode 100644 index 0000000000000..c642baa8b92d7 --- /dev/null +++ b/tests/mir-opt/gvn_uninhabited.f.GVN.panic-abort.diff @@ -0,0 +1,34 @@ +- // MIR for `f` before GVN ++ // MIR for `f` after GVN + + fn f() -> u32 { + let mut _0: u32; + let _1: u32; + let mut _2: E; + let mut _3: &U; + let _4: U; + scope 1 { + debug i => _1; + } + scope 2 { + let mut _5: &U; + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _5 = const _; + _3 = &(*_5); + _2 = ((*_3).1: E); +- StorageLive(_1); ++ nop; + _1 = ((_2 as A).1: u32); + StorageDead(_3); + StorageDead(_2); + _0 = _1; +- StorageDead(_1); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn_uninhabited.f.GVN.panic-unwind.diff b/tests/mir-opt/gvn_uninhabited.f.GVN.panic-unwind.diff new file mode 100644 index 0000000000000..c642baa8b92d7 --- /dev/null +++ b/tests/mir-opt/gvn_uninhabited.f.GVN.panic-unwind.diff @@ -0,0 +1,34 @@ +- // MIR for `f` before GVN ++ // MIR for `f` after GVN + + fn f() -> u32 { + let mut _0: u32; + let _1: u32; + let mut _2: E; + let mut _3: &U; + let _4: U; + scope 1 { + debug i => _1; + } + scope 2 { + let mut _5: &U; + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _5 = const _; + _3 = &(*_5); + _2 = ((*_3).1: E); +- StorageLive(_1); ++ nop; + _1 = ((_2 as A).1: u32); + StorageDead(_3); + StorageDead(_2); + _0 = _1; +- StorageDead(_1); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn_uninhabited.rs b/tests/mir-opt/gvn_uninhabited.rs new file mode 100644 index 0000000000000..a55b2dd763ac5 --- /dev/null +++ b/tests/mir-opt/gvn_uninhabited.rs @@ -0,0 +1,24 @@ +// unit-test: GVN +// compile-flags: -O +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// skip-filecheck + +#![feature(never_type)] + +#[derive(Copy, Clone)] +pub enum E { + A(!, u32), +} + +pub union U { + i: u32, + e: E, +} + +// EMIT_MIR gvn_uninhabited.f.GVN.diff +pub const fn f() -> u32 { + let E::A(_, i) = unsafe { (&U { i: 0 }).e }; + i +} + +fn main() {} diff --git a/tests/mir-opt/jump_threading_uninhabited.rs b/tests/mir-opt/jump_threading_uninhabited.rs new file mode 100644 index 0000000000000..b608021a51c8d --- /dev/null +++ b/tests/mir-opt/jump_threading_uninhabited.rs @@ -0,0 +1,16 @@ +// unit-test: JumpThreading +// compile-flags: -Zmir-opt-level=3 -Zunsound-mir-opts +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// skip-filecheck + +pub enum HiddenType {} + +pub struct Wrap(T); + +// EMIT_MIR jump_threading_uninhabited.test_questionmark.JumpThreading.diff +fn test_questionmark() -> Result<(), ()> { + Ok(Ok(()))??; + Ok(()) +} + +fn main() {} diff --git a/tests/mir-opt/jump_threading_uninhabited.test_questionmark.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading_uninhabited.test_questionmark.JumpThreading.panic-abort.diff new file mode 100644 index 0000000000000..1172928f99740 --- /dev/null +++ b/tests/mir-opt/jump_threading_uninhabited.test_questionmark.JumpThreading.panic-abort.diff @@ -0,0 +1,157 @@ +- // MIR for `test_questionmark` before JumpThreading ++ // MIR for `test_questionmark` after JumpThreading + + fn test_questionmark() -> Result<(), ()> { + let mut _0: std::result::Result<(), ()>; + let mut _1: std::ops::ControlFlow>; + let mut _2: std::result::Result<(), ()>; + let mut _3: std::ops::ControlFlow, std::result::Result<(), ()>>; + let mut _4: std::result::Result, ()>; + let mut _5: isize; + let _6: std::result::Result<(), ()>; + let mut _7: isize; + scope 1 { + debug residual => const Result::::Err(()); + scope 2 { + scope 15 (inlined #[track_caller] as FromResidual>>::from_residual) { + debug residual => const Result::::Err(()); + scope 16 { + debug e => const (); + scope 17 (inlined <() as From<()>>::from) { + debug t => const (); + } + } + } + } + } + scope 3 { + debug val => const Result::<(), ()>::Ok(()); + scope 4 { + } + } + scope 5 { + debug residual => const Result::::Err(()); + scope 6 { + scope 18 (inlined #[track_caller] as FromResidual>>::from_residual) { + debug residual => const Result::::Err(()); + scope 19 { + debug e => const (); + scope 20 (inlined <() as From<()>>::from) { + debug t => const (); + } + } + } + } + } + scope 7 { + debug val => const (); + scope 8 { + } + } + scope 9 (inlined , ()> as Try>::branch) { + debug self => const Result::, ()>::Ok(Result::<(), ()>::Ok(())); + let _8: std::result::Result<(), ()>; + scope 10 { + debug v => const Result::<(), ()>::Ok(()); + } + scope 11 { + debug e => const (); + } + } + scope 12 (inlined as Try>::branch) { + debug self => const Result::<(), ()>::Ok(()); + let mut _9: isize; + scope 13 { + debug v => const (); + } + scope 14 { + debug e => const (); + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = const Result::, ()>::Ok(Result::<(), ()>::Ok(())); + StorageLive(_8); + goto -> bb8; + } + + bb1: { + _6 = const Result::<(), ()>::Ok(()); + _2 = const Result::<(), ()>::Ok(()); + StorageLive(_9); + _9 = const 0_isize; + goto -> bb10; + } + + bb2: { + _0 = const Result::<(), ()>::Err(()); + StorageDead(_2); + goto -> bb5; + } + + bb3: { + StorageDead(_3); + StorageDead(_1); + _0 = const Result::<(), ()>::Ok(()); + goto -> bb6; + } + + bb4: { + _0 = const Result::<(), ()>::Err(()); + goto -> bb5; + } + + bb5: { + StorageDead(_3); + StorageDead(_1); + goto -> bb6; + } + + bb6: { + return; + } + + bb7: { + _3 = const ControlFlow::, Result<(), ()>>::Break(Result::::Err(())); + StorageDead(_8); + StorageDead(_4); + _5 = discriminant(_3); +- switchInt(move _5) -> [0: bb1, 1: bb2, otherwise: bb11]; ++ goto -> bb2; + } + + bb8: { + _8 = const Result::<(), ()>::Ok(()); + _3 = const ControlFlow::, Result<(), ()>>::Continue(Result::<(), ()>::Ok(())); + StorageDead(_8); + StorageDead(_4); + _5 = const 0_isize; + goto -> bb1; + } + + bb9: { + _1 = const ControlFlow::>::Break(Result::::Err(())); + StorageDead(_9); + StorageDead(_2); + _7 = discriminant(_1); +- switchInt(move _7) -> [0: bb3, 1: bb4, otherwise: bb11]; ++ goto -> bb4; + } + + bb10: { + _1 = const ControlFlow::>::Continue(()); + StorageDead(_9); + StorageDead(_2); + _7 = const 0_isize; + goto -> bb3; + } + + bb11: { + unreachable; + } + } + diff --git a/tests/mir-opt/jump_threading_uninhabited.test_questionmark.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading_uninhabited.test_questionmark.JumpThreading.panic-unwind.diff new file mode 100644 index 0000000000000..1172928f99740 --- /dev/null +++ b/tests/mir-opt/jump_threading_uninhabited.test_questionmark.JumpThreading.panic-unwind.diff @@ -0,0 +1,157 @@ +- // MIR for `test_questionmark` before JumpThreading ++ // MIR for `test_questionmark` after JumpThreading + + fn test_questionmark() -> Result<(), ()> { + let mut _0: std::result::Result<(), ()>; + let mut _1: std::ops::ControlFlow>; + let mut _2: std::result::Result<(), ()>; + let mut _3: std::ops::ControlFlow, std::result::Result<(), ()>>; + let mut _4: std::result::Result, ()>; + let mut _5: isize; + let _6: std::result::Result<(), ()>; + let mut _7: isize; + scope 1 { + debug residual => const Result::::Err(()); + scope 2 { + scope 15 (inlined #[track_caller] as FromResidual>>::from_residual) { + debug residual => const Result::::Err(()); + scope 16 { + debug e => const (); + scope 17 (inlined <() as From<()>>::from) { + debug t => const (); + } + } + } + } + } + scope 3 { + debug val => const Result::<(), ()>::Ok(()); + scope 4 { + } + } + scope 5 { + debug residual => const Result::::Err(()); + scope 6 { + scope 18 (inlined #[track_caller] as FromResidual>>::from_residual) { + debug residual => const Result::::Err(()); + scope 19 { + debug e => const (); + scope 20 (inlined <() as From<()>>::from) { + debug t => const (); + } + } + } + } + } + scope 7 { + debug val => const (); + scope 8 { + } + } + scope 9 (inlined , ()> as Try>::branch) { + debug self => const Result::, ()>::Ok(Result::<(), ()>::Ok(())); + let _8: std::result::Result<(), ()>; + scope 10 { + debug v => const Result::<(), ()>::Ok(()); + } + scope 11 { + debug e => const (); + } + } + scope 12 (inlined as Try>::branch) { + debug self => const Result::<(), ()>::Ok(()); + let mut _9: isize; + scope 13 { + debug v => const (); + } + scope 14 { + debug e => const (); + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = const Result::, ()>::Ok(Result::<(), ()>::Ok(())); + StorageLive(_8); + goto -> bb8; + } + + bb1: { + _6 = const Result::<(), ()>::Ok(()); + _2 = const Result::<(), ()>::Ok(()); + StorageLive(_9); + _9 = const 0_isize; + goto -> bb10; + } + + bb2: { + _0 = const Result::<(), ()>::Err(()); + StorageDead(_2); + goto -> bb5; + } + + bb3: { + StorageDead(_3); + StorageDead(_1); + _0 = const Result::<(), ()>::Ok(()); + goto -> bb6; + } + + bb4: { + _0 = const Result::<(), ()>::Err(()); + goto -> bb5; + } + + bb5: { + StorageDead(_3); + StorageDead(_1); + goto -> bb6; + } + + bb6: { + return; + } + + bb7: { + _3 = const ControlFlow::, Result<(), ()>>::Break(Result::::Err(())); + StorageDead(_8); + StorageDead(_4); + _5 = discriminant(_3); +- switchInt(move _5) -> [0: bb1, 1: bb2, otherwise: bb11]; ++ goto -> bb2; + } + + bb8: { + _8 = const Result::<(), ()>::Ok(()); + _3 = const ControlFlow::, Result<(), ()>>::Continue(Result::<(), ()>::Ok(())); + StorageDead(_8); + StorageDead(_4); + _5 = const 0_isize; + goto -> bb1; + } + + bb9: { + _1 = const ControlFlow::>::Break(Result::::Err(())); + StorageDead(_9); + StorageDead(_2); + _7 = discriminant(_1); +- switchInt(move _7) -> [0: bb3, 1: bb4, otherwise: bb11]; ++ goto -> bb4; + } + + bb10: { + _1 = const ControlFlow::>::Continue(()); + StorageDead(_9); + StorageDead(_2); + _7 = const 0_isize; + goto -> bb3; + } + + bb11: { + unreachable; + } + } +