diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index a520acda5e6c8..26712fdf44e79 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2462,8 +2462,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("Exiting guard building context with locals: {:?}", guard_frame); for &(_, temp, _) in fake_borrows { + // We put fake reads of fake borrows on the guard's failure path to make sure + // they're reachable if we continue matching (#161578) and we put them on the + // success path to make sure we can create by-value bindings. This won't keep fake + // borrows live on paths where the guard diverges unconditionally, but that should + // be sound since we can't continue matching or make bindings after diverging. let cause = FakeReadCause::ForMatchGuard; self.cfg.push_fake_read(guard_true_block, guard_end, cause, Place::from(temp)); + self.cfg.push_fake_read(guard_false_block, guard_end, cause, Place::from(temp)); } self.cfg.goto(guard_false_block, source_info, sub_branch.otherwise_block); diff --git a/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir b/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir index 4b0cdcfbb8662..8cff5af29cce9 100644 --- a/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir +++ b/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir @@ -106,6 +106,7 @@ fn full_tested_match() -> () { bb13: { StorageDead(_7); StorageDead(_6); + FakeRead(ForMatchGuard, _3); goto -> bb9; } diff --git a/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir b/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir index 63ec71fdf5133..f9d956b6948fc 100644 --- a/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir +++ b/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir @@ -106,6 +106,7 @@ fn full_tested_match2() -> () { bb13: { StorageDead(_7); StorageDead(_6); + FakeRead(ForMatchGuard, _3); goto -> bb9; } diff --git a/tests/mir-opt/building/match/match_false_edges.main.built.after.mir b/tests/mir-opt/building/match/match_false_edges.main.built.after.mir index 3b10adb499cd5..5aefc47ab3b45 100644 --- a/tests/mir-opt/building/match/match_false_edges.main.built.after.mir +++ b/tests/mir-opt/building/match/match_false_edges.main.built.after.mir @@ -137,6 +137,7 @@ fn main() -> () { bb17: { StorageDead(_8); StorageDead(_7); + FakeRead(ForMatchGuard, _3); goto -> bb13; } @@ -165,6 +166,7 @@ fn main() -> () { StorageDead(_13); StorageDead(_12); StorageDead(_11); + FakeRead(ForMatchGuard, _3); goto -> bb10; } diff --git a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir index 4d13d087586e7..8df9a66cf4a8f 100644 --- a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir @@ -111,6 +111,9 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { bb17: { StorageDead(_13); + FakeRead(ForMatchGuard, _6); + FakeRead(ForMatchGuard, _7); + FakeRead(ForMatchGuard, _8); falseEdge -> [real: bb3, imaginary: bb5]; } diff --git a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir index d625168081435..b2bab9be79af8 100644 --- a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir @@ -79,6 +79,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 { bb13: { StorageDead(_8); + FakeRead(ForMatchGuard, _3); falseEdge -> [real: bb1, imaginary: bb3]; } diff --git a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir index 4c9ca11f82834..40191c7ca9b0f 100644 --- a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir +++ b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir @@ -59,6 +59,8 @@ yields () bb9: { StorageDead(_6); + FakeRead(ForMatchGuard, _3); + FakeRead(ForMatchGuard, _4); goto -> bb6; } diff --git a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir index e80fdea7051dc..c9ac873d18729 100644 --- a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir @@ -43,6 +43,8 @@ yields () bb5: { StorageDead(_6); + FakeRead(ForMatchGuard, _3); + FakeRead(ForMatchGuard, _4); falseEdge -> [real: bb1, imaginary: bb1]; } diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 3b5b917af5c75..a21c40f82dd49 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -152,6 +152,8 @@ StorageDead(_9); StorageDead(_8); StorageDead(_6); +- FakeRead(ForMatchGuard, _3); +- FakeRead(ForMatchGuard, _4); - falseEdge -> [real: bb3, imaginary: bb3]; + goto -> bb2; } @@ -194,6 +196,8 @@ StorageDead(_12); StorageDead(_8); StorageDead(_6); +- FakeRead(ForMatchGuard, _3); +- FakeRead(ForMatchGuard, _4); - falseEdge -> [real: bb1, imaginary: bb1]; + goto -> bb1; } diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 3b5b917af5c75..a21c40f82dd49 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -152,6 +152,8 @@ StorageDead(_9); StorageDead(_8); StorageDead(_6); +- FakeRead(ForMatchGuard, _3); +- FakeRead(ForMatchGuard, _4); - falseEdge -> [real: bb3, imaginary: bb3]; + goto -> bb2; } @@ -194,6 +196,8 @@ StorageDead(_12); StorageDead(_8); StorageDead(_6); +- FakeRead(ForMatchGuard, _3); +- FakeRead(ForMatchGuard, _4); - falseEdge -> [real: bb1, imaginary: bb1]; + goto -> bb1; } diff --git a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff index 8c6c5e0d99349..2163181fb4bdf 100644 --- a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff +++ b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff @@ -62,7 +62,15 @@ bb6: { StorageDead(_8); +- FakeRead(ForMatchGuard, _3); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _5); +- FakeRead(ForMatchGuard, _6); - falseEdge -> [real: bb1, imaginary: bb1]; ++ nop; ++ nop; ++ nop; ++ nop; + goto -> bb1; } diff --git a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff index 8c6c5e0d99349..2163181fb4bdf 100644 --- a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff +++ b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff @@ -62,7 +62,15 @@ bb6: { StorageDead(_8); +- FakeRead(ForMatchGuard, _3); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _5); +- FakeRead(ForMatchGuard, _6); - falseEdge -> [real: bb1, imaginary: bb1]; ++ nop; ++ nop; ++ nop; ++ nop; + goto -> bb1; } diff --git a/tests/ui/match/divergent-guard-soundness-failure.rs b/tests/ui/match/divergent-guard-soundness-failure.rs new file mode 100644 index 0000000000000..09d239edaed33 --- /dev/null +++ b/tests/ui/match/divergent-guard-soundness-failure.rs @@ -0,0 +1,16 @@ +//! Regression test for : the fake borrow on `x` +//! below was ignored previously because the fake read keeping it live was unreachable. + +fn main() { + let mut x: Option> = Some(Box::new(7)); + match x { + Some(_) if { x = None; false } && return => {} + //~^ ERROR: cannot assign `x` in match guard + Some(_) if { x = None; false } || return => {} + //~^ ERROR: cannot assign `x` in match guard + Some(_) if false && ({ x = None; false } || return) => {} + //~^ ERROR: cannot assign `x` in match guard + Some(b) => println!("{b}"), + None => println!("none"), + } +} diff --git a/tests/ui/match/divergent-guard-soundness-failure.stderr b/tests/ui/match/divergent-guard-soundness-failure.stderr new file mode 100644 index 0000000000000..200baf48c6c95 --- /dev/null +++ b/tests/ui/match/divergent-guard-soundness-failure.stderr @@ -0,0 +1,29 @@ +error[E0510]: cannot assign `x` in match guard + --> $DIR/divergent-guard-soundness-failure.rs:7:22 + | +LL | match x { + | - value is immutable in match guard +LL | Some(_) if { x = None; false } && return => {} + | ^ cannot assign + +error[E0510]: cannot assign `x` in match guard + --> $DIR/divergent-guard-soundness-failure.rs:9:22 + | +LL | match x { + | - value is immutable in match guard +... +LL | Some(_) if { x = None; false } || return => {} + | ^ cannot assign + +error[E0510]: cannot assign `x` in match guard + --> $DIR/divergent-guard-soundness-failure.rs:11:32 + | +LL | match x { + | - value is immutable in match guard +... +LL | Some(_) if false && ({ x = None; false } || return) => {} + | ^ cannot assign + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0510`. diff --git a/tests/ui/match/divergent-guard-soundness-success.rs b/tests/ui/match/divergent-guard-soundness-success.rs new file mode 100644 index 0000000000000..cfa7385bee706 --- /dev/null +++ b/tests/ui/match/divergent-guard-soundness-success.rs @@ -0,0 +1,15 @@ +//! Test to document strange behavior related to : +//! in these gaurds, the guard's success and failures are both unreachable after mutating `x`. This +//! means the fake reads on the fake borrow on `x` are unreachable when assigning to `x`, so `x` can +//! be overwritten. This is fine since we don't create bindings and don't continue matching. +//@ check-pass + +fn main() { + let mut x: Option<&u64> = Some(&7); + match x { + Some(&y) if { x = None; return } => {} + Some(&y) if true || { x = None; return } => {} + Some(b) => println!("{b}"), + None => println!("none"), + } +}