From c6f2d49ff8be6b42bba94e7dc96e0fff8b5ca580 Mon Sep 17 00:00:00 2001 From: Chenguang Wang <w3cing@gmail.com> Date: Wed, 9 Dec 2020 18:56:27 -0800 Subject: [PATCH] fix issue #78496 --- .../src/transform/early_otherwise_branch.rs | 28 +++++++++++++++++++ src/test/ui/mir/issue-78496.rs | 16 +++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/test/ui/mir/issue-78496.rs diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs index f91477911a4..5829c522935 100644 --- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -283,6 +283,34 @@ impl<'a, 'tcx> Helper<'a, 'tcx> { return None; } + // when one place is the projection of the other, it's not safe to calculate their discriminant values sequentially. + // for example, this should not be optimized: + // + // ```rust + // enum E<'a> { Empty, Some(&'a E<'a>), } + // let Some(Some(_)) = e; + // ``` + // + // ```mir + // bb0: { + // _2 = discriminant(*_1) + // switchInt(move _2) -> [...] + // } + // bb1: { + // _3 = discriminant(*(((*_1) as Some).0: &E)) + // switchInt(move _3) -> [...] + // } + // ``` + let discr_place = discr_info.place_of_adt_discr_read; + let this_discr_place = this_bb_discr_info.place_of_adt_discr_read; + if discr_place.local == this_discr_place.local + && (discr_place.projection.starts_with(this_discr_place.projection) + || this_discr_place.projection.starts_with(discr_place.projection)) + { + trace!("NO: one target is the projection of another"); + return None; + } + // if we reach this point, the optimization applies, and we should be able to optimize this case // store the info that is needed to apply the optimization diff --git a/src/test/ui/mir/issue-78496.rs b/src/test/ui/mir/issue-78496.rs new file mode 100644 index 00000000000..cc45945a2b8 --- /dev/null +++ b/src/test/ui/mir/issue-78496.rs @@ -0,0 +1,16 @@ +// run-pass +// compile-flags: -Z mir-opt-level=2 -C opt-level=0 + +// example from #68867 +pub enum E<'a> { + Empty, + Some(&'a E<'a>), +} + +fn f(e: &E) -> u32 { + if let E::Some(E::Some(_)) = e { 1 } else { 2 } +} + +fn main() { + assert_eq!(f(&E::Empty), 2); +}