From 7be8e8add8ba15c197e8181c74ed6ecb18faa158 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 21:34:40 +0000 Subject: [PATCH] Unelaborate the `&mut` deref temps GVN rewrites into copies From `-C opt-level=1`, GVN rewrites a deref temp `_t = deref_copy P` of a `&mut`-typed place into `_t = copy P`. `extract_elaborated_deref` only recognized `CopyForDeref`, so `_t` survived as an independent local and each such copy was analyzed as its own borrow of `P`. With two of them alive at once (`**w += 3` reads through one and writes through the other) the prophecies contradict, the body is vacuous, and callers verify any assertion, `assert!(false)` included. Treat a copy of a projected `&mut`-typed place as such a deref temp. Whole-local copies are left alone: rustc emits those for ordinary moves of a `&mut` local. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KKfGn15ekWG5r4cKqzoP9a --- src/analyze/local_def.rs | 8 ++++++++ tests/ui/fail/mut_ref_deref_optimized.rs | 13 +++++++++++++ tests/ui/pass/mut_ref_deref_optimized.rs | 13 +++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/ui/fail/mut_ref_deref_optimized.rs create mode 100644 tests/ui/pass/mut_ref_deref_optimized.rs diff --git a/src/analyze/local_def.rs b/src/analyze/local_def.rs index 6a9cdf1b5..6e9acc387 100644 --- a/src/analyze/local_def.rs +++ b/src/analyze/local_def.rs @@ -415,6 +415,14 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> { return Some((lhs_local, *place)); } + // GVN rewrites the deref temp of a `&mut` from `deref_copy` into a plain `copy`. + if let mir::Rvalue::Use(mir::Operand::Copy(place)) = &rvalue { + let ty = place.ty(&self.body.local_decls, self.tcx).ty; + if !place.projection.is_empty() && ty.ref_mutability() == Some(mir::Mutability::Mut) { + return Some((lhs_local, *place)); + } + } + let unique_did = self.ctx.def_ids.unique()?; let nonnull_did = self.ctx.def_ids.nonnull()?; diff --git a/tests/ui/fail/mut_ref_deref_optimized.rs b/tests/ui/fail/mut_ref_deref_optimized.rs new file mode 100644 index 000000000..8b092d39c --- /dev/null +++ b/tests/ui/fail/mut_ref_deref_optimized.rs @@ -0,0 +1,13 @@ +//@error-in-other-file: Unsat +//@compile-flags: -C debug-assertions=off -C opt-level=1 + +fn bump(w: &mut &mut i64) { + **w += 3; +} + +fn main() { + let mut a = 0; + let mut r = &mut a; + bump(&mut r); + assert!(a == 0); +} diff --git a/tests/ui/pass/mut_ref_deref_optimized.rs b/tests/ui/pass/mut_ref_deref_optimized.rs new file mode 100644 index 000000000..1fe487590 --- /dev/null +++ b/tests/ui/pass/mut_ref_deref_optimized.rs @@ -0,0 +1,13 @@ +//@check-pass +//@compile-flags: -C debug-assertions=off -C opt-level=1 + +fn bump(w: &mut &mut i64) { + **w += 3; +} + +fn main() { + let mut a = 0; + let mut r = &mut a; + bump(&mut r); + assert!(a == 3); +}