From 690071c28a6fbce6cc1f6d958a8f60a5cd1b80be Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 20:14:21 +0800 Subject: [PATCH 1/4] Add regression test for 147485 --- tests/crashes/147485-2.rs | 16 +++++++++ tests/crashes/147485.rs | 14 ++++++++ ...buginfo_locals.DestinationPropagation.diff | 35 ++++++++++++++++++ tests/mir-opt/debuginfo/dest_prop.rs | 36 +++++++++++++++++++ ...debuginfo_locals.ReferencePropagation.diff | 33 +++++++++++++++++ tests/mir-opt/debuginfo/ref_prop.rs | 26 ++++++++++++++ 6 files changed, 160 insertions(+) create mode 100644 tests/crashes/147485-2.rs create mode 100644 tests/crashes/147485.rs create mode 100644 tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff create mode 100644 tests/mir-opt/debuginfo/dest_prop.rs create mode 100644 tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff create mode 100644 tests/mir-opt/debuginfo/ref_prop.rs diff --git a/tests/crashes/147485-2.rs b/tests/crashes/147485-2.rs new file mode 100644 index 0000000000000..da092a3af76aa --- /dev/null +++ b/tests/crashes/147485-2.rs @@ -0,0 +1,16 @@ +//@ known-bug: #147485 +//@ compile-flags: -g -O + +#![crate_type = "lib"] + +pub fn foo(a: bool, b: bool) -> bool { + let mut c = &a; + if false { + return *c; + } + let d = b && a; + if d { + c = &b; + } + b +} diff --git a/tests/crashes/147485.rs b/tests/crashes/147485.rs new file mode 100644 index 0000000000000..86f3705e636b4 --- /dev/null +++ b/tests/crashes/147485.rs @@ -0,0 +1,14 @@ +//@ known-bug: #147485 +//@ compile-flags: -g -O + +#![crate_type = "lib"] + +pub fn f(x: *const usize) -> &'static usize { + let mut a = unsafe { &*x }; + a = unsafe { &*x }; + a +} + +pub fn g() { + f(&0); +} diff --git a/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff new file mode 100644 index 0000000000000..6f3233d85c9fc --- /dev/null +++ b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff @@ -0,0 +1,35 @@ +- // MIR for `remap_debuginfo_locals` before DestinationPropagation ++ // MIR for `remap_debuginfo_locals` after DestinationPropagation + + fn remap_debuginfo_locals(_1: bool, _2: &bool) -> &bool { +- debug c => _3; ++ debug c => _2; + let mut _0: &bool; + let mut _3: &bool; + let mut _4: bool; + + bb0: { + // DBG: _3 = &_1; +- StorageLive(_4); +- _4 = copy _1; +- _3 = copy _2; +- switchInt(copy _4) -> [1: bb1, otherwise: bb2]; ++ nop; ++ nop; ++ nop; ++ switchInt(copy _1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { + goto -> bb2; + } + + bb2: { +- StorageDead(_4); +- _0 = copy _3; ++ nop; ++ _0 = copy _2; + return; + } + } + diff --git a/tests/mir-opt/debuginfo/dest_prop.rs b/tests/mir-opt/debuginfo/dest_prop.rs new file mode 100644 index 0000000000000..9c9d95ebfb3f3 --- /dev/null +++ b/tests/mir-opt/debuginfo/dest_prop.rs @@ -0,0 +1,36 @@ +// skip-filecheck +//@ test-mir-pass: DestinationPropagation +//@ compile-flags: -g -Zmir-enable-passes=+DeadStoreElimination-initial + +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +// EMIT_MIR dest_prop.remap_debuginfo_locals.DestinationPropagation.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn remap_debuginfo_locals(a: bool, b: &bool) -> &bool { + mir! { + let _3: &bool; + let _4: bool; + debug c => _3; + { + _3 = &a; + StorageLive(_4); + _4 = a; + _3 = b; + match _4 { + true => bb1, + _ => bb2, + } + } + bb1 = { + Goto(bb2) + } + bb2 = { + StorageDead(_4); + RET = _3; + Return() + } + } +} diff --git a/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff new file mode 100644 index 0000000000000..87941aadab4dd --- /dev/null +++ b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff @@ -0,0 +1,33 @@ +- // MIR for `remap_debuginfo_locals` before ReferencePropagation ++ // MIR for `remap_debuginfo_locals` after ReferencePropagation + + fn remap_debuginfo_locals() -> () { + let mut _0: (); + let _1: &usize; + let mut _2: *const usize; + let _3: &usize; + let _4: usize; + let mut _5: &usize; + scope 1 (inlined foo) { +- debug a => _1; ++ debug a => _5; + } + + bb0: { +- StorageLive(_1); +- StorageLive(_2); +- StorageLive(_3); + _5 = const remap_debuginfo_locals::promoted[0]; +- _3 = &(*_5); +- _2 = &raw const (*_3); +- // DBG: _1 = &(*_2); +- _1 = &(*_2); +- StorageDead(_2); +- StorageDead(_3); +- StorageDead(_1); ++ // DBG: _1 = &(*_5); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/ref_prop.rs b/tests/mir-opt/debuginfo/ref_prop.rs new file mode 100644 index 0000000000000..5f2f1ce09c707 --- /dev/null +++ b/tests/mir-opt/debuginfo/ref_prop.rs @@ -0,0 +1,26 @@ +// skip-filecheck +//@ test-mir-pass: ReferencePropagation +//@ compile-flags: -g -Zub_checks=false -Zinline-mir -Zmir-enable-passes=+DeadStoreElimination-initial + +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +// EMIT_MIR ref_prop.remap_debuginfo_locals.ReferencePropagation.diff +pub fn remap_debuginfo_locals() { + foo(&0); +} + +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +#[inline] +fn foo(x: *const usize) -> &'static usize { + mir! { + debug a => RET; + { + RET = &*x; + RET = &*x; + Return() + } + } +} From 9462e7301fb831f011b927494fd74c9f31c24b9b Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 21:32:23 +0800 Subject: [PATCH 2/4] MIR validation: ensure that debuginfo records are not emitted for locals that are not in debuginfo --- compiler/rustc_mir_transform/src/validate.rs | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 95873484b6529..6790dda780c5d 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{ self, CoroutineArgsExt, InstanceKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt, Upcast, Variance, }; use rustc_middle::{bug, span_bug}; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_trait_selection::traits::ObligationCtxt; use crate::util::{self, is_within_packed}; @@ -80,6 +81,11 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { cfg_checker.fail(location, msg); } + // Ensure that debuginfo records are not emitted for locals that are not in debuginfo. + for (location, msg) in validate_debuginfos(body) { + cfg_checker.fail(location, msg); + } + if let MirPhase::Runtime(_) = body.phase && let ty::InstanceKind::Item(_) = body.source.instance && body.has_free_regions() @@ -1595,3 +1601,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.super_terminator(terminator, location); } } + +pub(super) fn validate_debuginfos<'tcx>(body: &Body<'tcx>) -> Vec<(Location, String)> { + let mut debuginfo_checker = + DebuginfoChecker { debuginfo_locals: debuginfo_locals(body), failures: Vec::new() }; + debuginfo_checker.visit_body(body); + debuginfo_checker.failures +} + +struct DebuginfoChecker { + debuginfo_locals: DenseBitSet, + failures: Vec<(Location, String)>, +} + +impl<'tcx> Visitor<'tcx> for DebuginfoChecker { + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: &StmtDebugInfo<'tcx>, + location: Location, + ) { + let local = match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => *local, + }; + if !self.debuginfo_locals.contains(local) { + self.failures.push((location, format!("{local:?} is not in debuginfo"))); + } + } +} From b2e81b00e5cd6f0ad7f883413b983b2c6e25c25e Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 9 Oct 2025 21:53:05 +0800 Subject: [PATCH 3/4] Replace locals in debuginfo records during ref_prop --- compiler/rustc_middle/src/mir/visit.rs | 8 ++-- compiler/rustc_mir_transform/src/ref_prop.rs | 45 +++++++++++++++---- ...debuginfo_locals.ReferencePropagation.diff | 2 +- tests/mir-opt/debuginfo/ref_prop.rs | 6 ++- .../debuginfo/ref_prop_debuginfo-147485.rs} | 4 +- 5 files changed, 49 insertions(+), 16 deletions(-) rename tests/{crashes/147485.rs => ui/debuginfo/ref_prop_debuginfo-147485.rs} (79%) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 9654e189f2edf..29cf3977dc807 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1098,6 +1098,10 @@ macro_rules! super_body { } } + for var_debug_info in &$($mutability)? $body.var_debug_info { + $self.visit_var_debug_info(var_debug_info); + } + for (bb, data) in basic_blocks_iter!($body, $($mutability, $invalidate)?) { $self.visit_basic_block_data(bb, data); } @@ -1127,10 +1131,6 @@ macro_rules! super_body { ); } - for var_debug_info in &$($mutability)? $body.var_debug_info { - $self.visit_var_debug_info(var_debug_info); - } - $self.visit_span($(& $mutability)? $body.span); if let Some(required_consts) = &$($mutability)? $body.required_consts { diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index deb0a146476c3..c5b53dbbd309b 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -302,6 +302,7 @@ fn compute_replacement<'tcx>( return Replacer { tcx, targets: finder.targets, + remap_var_debug_infos: IndexVec::from_elem(None, body.local_decls()), storage_to_remove, allowed_replacements, any_replacement: false, @@ -381,6 +382,7 @@ fn fully_replaceable_locals(ssa: &SsaLocals) -> DenseBitSet { struct Replacer<'tcx> { tcx: TyCtxt<'tcx>, targets: IndexVec>, + remap_var_debug_infos: IndexVec>, storage_to_remove: DenseBitSet, allowed_replacements: FxHashSet<(Local, Location)>, any_replacement: bool, @@ -392,21 +394,45 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } fn visit_var_debug_info(&mut self, debuginfo: &mut VarDebugInfo<'tcx>) { - // If the debuginfo is a pointer to another place - // and it's a reborrow: see through it - while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value + if let VarDebugInfoContents::Place(ref mut place) = debuginfo.value && place.projection.is_empty() - && let Value::Pointer(target, _) = self.targets[place.local] - && let &[PlaceElem::Deref] = &target.projection[..] { - *place = Place::from(target.local); - self.any_replacement = true; + let mut new_local = place.local; + + // If the debuginfo is a pointer to another place + // and it's a reborrow: see through it + while let Value::Pointer(target, _) = self.targets[new_local] + && let &[PlaceElem::Deref] = &target.projection[..] + { + new_local = target.local; + } + if place.local != new_local { + self.remap_var_debug_infos[place.local] = Some(new_local); + place.local = new_local; + + self.any_replacement = true; + } } // Simplify eventual projections left inside `debuginfo`. self.super_var_debug_info(debuginfo); } + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: &mut StmtDebugInfo<'tcx>, + location: Location, + ) { + let local = match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => local, + }; + if let Some(target) = self.remap_var_debug_infos[*local] { + *local = target; + self.any_replacement = true; + } + self.super_statement_debuginfo(stmt_debuginfo, location); + } + fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) { loop { let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { return }; @@ -437,8 +463,9 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { { stmt.make_nop(true); } - // Do not remove assignments as they may still be useful for debuginfo. - _ => self.super_statement(stmt, loc), + _ => {} } + // Do not remove assignments as they may still be useful for debuginfo. + self.super_statement(stmt, loc); } } diff --git a/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff index 87941aadab4dd..2d93b1d842fa1 100644 --- a/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff +++ b/tests/mir-opt/debuginfo/ref_prop.remap_debuginfo_locals.ReferencePropagation.diff @@ -25,7 +25,7 @@ - StorageDead(_2); - StorageDead(_3); - StorageDead(_1); -+ // DBG: _1 = &(*_5); ++ // DBG: _5 = &(*_5); _0 = const (); return; } diff --git a/tests/mir-opt/debuginfo/ref_prop.rs b/tests/mir-opt/debuginfo/ref_prop.rs index 5f2f1ce09c707..60d68ba178a7b 100644 --- a/tests/mir-opt/debuginfo/ref_prop.rs +++ b/tests/mir-opt/debuginfo/ref_prop.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ test-mir-pass: ReferencePropagation //@ compile-flags: -g -Zub_checks=false -Zinline-mir -Zmir-enable-passes=+DeadStoreElimination-initial @@ -9,6 +8,11 @@ use std::intrinsics::mir::*; // EMIT_MIR ref_prop.remap_debuginfo_locals.ReferencePropagation.diff pub fn remap_debuginfo_locals() { + // CHECK-LABEL: fn remap_debuginfo_locals() + // CHECK: debug a => [[a:_.*]]; + // CHECK: bb0: + // CHECK-NEXT: [[a]] = const + // CHECK-NEXT: DBG: [[a]] = &(*[[a]]); foo(&0); } diff --git a/tests/crashes/147485.rs b/tests/ui/debuginfo/ref_prop_debuginfo-147485.rs similarity index 79% rename from tests/crashes/147485.rs rename to tests/ui/debuginfo/ref_prop_debuginfo-147485.rs index 86f3705e636b4..15899626aff7b 100644 --- a/tests/crashes/147485.rs +++ b/tests/ui/debuginfo/ref_prop_debuginfo-147485.rs @@ -1,6 +1,8 @@ -//@ known-bug: #147485 +//@ build-pass //@ compile-flags: -g -O +// Regression test for #147485. + #![crate_type = "lib"] pub fn f(x: *const usize) -> &'static usize { From 1ee2c58e8925315a45500d73c5527b7a340cc8a5 Mon Sep 17 00:00:00 2001 From: dianqk Date: Fri, 10 Oct 2025 13:50:01 +0800 Subject: [PATCH 4/4] Replace locals in debuginfo records during dest_prop --- compiler/rustc_mir_transform/src/dest_prop.rs | 1 - ...t_prop.remap_debuginfo_locals.DestinationPropagation.diff | 3 ++- tests/mir-opt/debuginfo/dest_prop.rs | 5 ++++- .../debuginfo/dest_prop_debuginfo-147485.rs} | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) rename tests/{crashes/147485-2.rs => ui/debuginfo/dest_prop_debuginfo-147485.rs} (81%) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 1f38433fa5a41..92556e7c5f6c5 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -277,7 +277,6 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { if self.merged_locals.contains(*local) => { statement.make_nop(true); - return; } _ => (), }; diff --git a/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff index 6f3233d85c9fc..fe00da67e8bb0 100644 --- a/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff +++ b/tests/mir-opt/debuginfo/dest_prop.remap_debuginfo_locals.DestinationPropagation.diff @@ -9,11 +9,12 @@ let mut _4: bool; bb0: { - // DBG: _3 = &_1; +- // DBG: _3 = &_1; - StorageLive(_4); - _4 = copy _1; - _3 = copy _2; - switchInt(copy _4) -> [1: bb1, otherwise: bb2]; ++ // DBG: _2 = &_1; + nop; + nop; + nop; diff --git a/tests/mir-opt/debuginfo/dest_prop.rs b/tests/mir-opt/debuginfo/dest_prop.rs index 9c9d95ebfb3f3..a734cacb4d29a 100644 --- a/tests/mir-opt/debuginfo/dest_prop.rs +++ b/tests/mir-opt/debuginfo/dest_prop.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ test-mir-pass: DestinationPropagation //@ compile-flags: -g -Zmir-enable-passes=+DeadStoreElimination-initial @@ -10,6 +9,10 @@ use std::intrinsics::mir::*; // EMIT_MIR dest_prop.remap_debuginfo_locals.DestinationPropagation.diff #[custom_mir(dialect = "runtime", phase = "post-cleanup")] pub fn remap_debuginfo_locals(a: bool, b: &bool) -> &bool { + // CHECK-LABEL: fn remap_debuginfo_locals( + // CHECK: debug c => [[c:_.*]]; + // CHECK: bb0: + // CHECK-NEXT: DBG: [[c]] = &_1; mir! { let _3: &bool; let _4: bool; diff --git a/tests/crashes/147485-2.rs b/tests/ui/debuginfo/dest_prop_debuginfo-147485.rs similarity index 81% rename from tests/crashes/147485-2.rs rename to tests/ui/debuginfo/dest_prop_debuginfo-147485.rs index da092a3af76aa..652660d473b15 100644 --- a/tests/crashes/147485-2.rs +++ b/tests/ui/debuginfo/dest_prop_debuginfo-147485.rs @@ -1,6 +1,8 @@ -//@ known-bug: #147485 +//@ build-pass //@ compile-flags: -g -O +// Regression test for #147485. + #![crate_type = "lib"] pub fn foo(a: bool, b: bool) -> bool {