Rollup merge of #142882 - kornelski:var-debug-info-lazy, r=petrochenkov

Lazy init diagnostics-only local_names in borrowck

`local_names` is not used during successful compilation, so not initializing it saves a little bit of work.

I've also made it accessible only from the diagnostics module to make it clearer that the names are from `var_debug_info` which is technically optional and could be absent.
This commit is contained in:
Matthias Krüger
2025-06-24 20:46:05 +02:00
committed by GitHub
7 changed files with 63 additions and 45 deletions
@@ -71,7 +71,6 @@ pub(crate) fn add_explanation_to_diagnostic<G: EmissionGuarantee>(
) {
let tcx = cx.infcx.tcx;
let body = cx.body;
let local_names = &cx.local_names;
if let Some(span) = borrow_span {
let def_id = body.source.def_id();
@@ -220,7 +219,7 @@ pub(crate) fn add_explanation_to_diagnostic<G: EmissionGuarantee>(
_ => ("destructor", format!("type `{}`", local_decl.ty)),
};
match local_names[dropped_local] {
match cx.local_name(dropped_local) {
Some(local_name) if !local_decl.from_compiler_desugaring() => {
let message = format!(
"{borrow_desc}borrow might be used here, when `{local_name}` is dropped \
@@ -670,10 +669,10 @@ pub(crate) fn explain_why_borrow_contains_point(
Some(Cause::DropVar(local, location)) => {
let mut should_note_order = false;
if self.local_names[local].is_some()
if self.local_name(local).is_some()
&& let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
&& let Some(borrowed_local) = place.as_local()
&& self.local_names[borrowed_local].is_some()
&& self.local_name(borrowed_local).is_some()
&& local != borrowed_local
{
should_note_order = true;
@@ -748,7 +747,7 @@ fn later_use_kind(
Operand::Copy(place) | Operand::Move(place) => {
if let Some(l) = place.as_local() {
let local_decl = &self.body.local_decls[l];
if self.local_names[l].is_none() {
if self.local_name(l).is_none() {
local_decl.source_info.span
} else {
span
@@ -793,7 +792,7 @@ fn later_use_kind(
Operand::Copy(place) | Operand::Move(place) => {
if let Some(l) = place.as_local() {
let local_decl = &self.body.local_decls[l];
if self.local_names[l].is_none() {
if self.local_name(l).is_none() {
local_decl.source_info.span
} else {
span
+39 -4
View File
@@ -7,17 +7,17 @@
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{self as hir, CoroutineKind, LangItem};
use rustc_index::IndexSlice;
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
use rustc_infer::traits::SelectionError;
use rustc_middle::bug;
use rustc_middle::mir::{
AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
StatementKind, Terminator, TerminatorKind, find_self_call,
StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call,
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Spanned;
@@ -190,6 +190,36 @@ pub(crate) fn has_move_error(
) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
self.diags_buffer.buffered_move_errors.get(move_out_indices)
}
/// Uses `body.var_debug_info` to find the symbol
fn local_name(&self, index: Local) -> Option<Symbol> {
*self.local_names().get(index)?
}
fn local_names(&self) -> &IndexSlice<Local, Option<Symbol>> {
self.local_names.get_or_init(|| {
let mut local_names = IndexVec::from_elem(None, &self.body.local_decls);
for var_debug_info in &self.body.var_debug_info {
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
if let Some(local) = place.as_local() {
if let Some(prev_name) = local_names[local]
&& var_debug_info.name != prev_name
{
span_bug!(
var_debug_info.source_info.span,
"local {:?} has many names (`{}` vs `{}`)",
local,
prev_name,
var_debug_info.name
);
}
local_names[local] = Some(var_debug_info.name);
}
}
}
local_names
})
}
}
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
@@ -430,7 +460,7 @@ fn describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol> {
/// a name, or its name was generated by the compiler, then `Err` is returned
fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
let decl = &self.body.local_decls[local];
match self.local_names[local] {
match self.local_name(local) {
Some(name) if !decl.from_compiler_desugaring() => {
buf.push_str(name.as_str());
Ok(())
@@ -1500,4 +1530,9 @@ fn explain_captures(
}
}
}
/// Skip over locals that begin with an underscore or have no name
pub(crate) fn local_excluded_from_unused_mut_lint(&self, index: Local) -> bool {
self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
}
}
@@ -465,11 +465,15 @@ fn report_cannot_move_from_borrowed_content(
if let PlaceRef { local, projection: [] } = deref_base {
let decl = &self.body.local_decls[local];
let local_name = self.local_name(local).map(|sym| format!("`{sym}`"));
if decl.is_ref_for_guard() {
return self
.cannot_move_out_of(
span,
&format!("`{}` in pattern guard", self.local_names[local].unwrap()),
&format!(
"{} in pattern guard",
local_name.as_deref().unwrap_or("the place")
),
)
.with_note(
"variables bound in patterns cannot be moved from \
@@ -825,7 +829,7 @@ fn add_move_error_details(&self, err: &mut Diag<'_>, binds_to: &[Local]) {
}
if binds_to.len() == 1 {
let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
let place_desc = self.local_name(*local).map(|sym| format!("`{sym}`"));
if let Some(expr) = self.find_expr(binding_span) {
self.suggest_cloning(err, bind_to.ty, expr, None);
@@ -834,7 +838,7 @@ fn add_move_error_details(&self, err: &mut Diag<'_>, binds_to: &[Local]) {
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move: false,
ty: bind_to.ty,
place: place_desc,
place: place_desc.as_deref().unwrap_or("the place"),
span: binding_span,
});
}
@@ -60,7 +60,7 @@ pub(crate) fn report_mutability_error(
if access_place.as_local().is_some() {
reason = ", as it is not declared as mutable".to_string();
} else {
let name = self.local_names[local].expect("immutable unnamed local");
let name = self.local_name(local).expect("immutable unnamed local");
reason = format!(", as `{name}` is not declared as mutable");
}
}
@@ -285,7 +285,7 @@ pub(crate) fn report_mutability_error(
.body
.local_decls
.get(local)
.is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) =>
.is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
{
let decl = &self.body.local_decls[local];
err.span_label(span, format!("cannot {act}"));
@@ -481,7 +481,7 @@ pub(crate) fn report_mutability_error(
let (pointer_sigil, pointer_desc) =
if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
match self.local_names[local] {
match self.local_name(local) {
Some(name) if !local_decl.from_compiler_desugaring() => {
err.span_label(
span,
@@ -664,14 +664,14 @@ fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<
let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
self.infcx.tcx,
self.body,
&self.local_names,
&self.local_names(),
&self.upvars,
errci.fr,
);
let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
self.infcx.tcx,
self.body,
&self.local_names,
&self.local_names(),
&self.upvars,
errci.outlived_fr,
);
@@ -399,7 +399,7 @@ fn give_name_if_anonymous_region_appears_in_arguments(
[implicit_inputs + argument_index];
let (_, span) = self.regioncx.get_argument_name_and_span_for_region(
self.body,
&self.local_names,
self.local_names(),
argument_index,
);
@@ -973,7 +973,7 @@ fn give_name_if_anonymous_region_appears_in_arg_position_impl_trait(
{
let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region(
self.body,
&self.local_names,
self.local_names(),
arg_index,
);
let region_name = self.synthesize_region_name();
+5 -25
View File
@@ -16,7 +16,7 @@
// tidy-alphabetical-end
use std::borrow::Cow;
use std::cell::RefCell;
use std::cell::{OnceCell, RefCell};
use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref};
@@ -391,7 +391,7 @@ fn do_mir_borrowck<'tcx>(
used_mut_upvars: SmallVec::new(),
borrow_set: &borrow_set,
upvars: &[],
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
local_names: OnceCell::from(IndexVec::from_elem(None, &promoted_body.local_decls)),
region_names: RefCell::default(),
next_region_name: RefCell::new(1),
polonius_output: None,
@@ -414,26 +414,6 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
promoted_mbcx.report_move_errors();
}
let mut local_names = IndexVec::from_elem(None, &body.local_decls);
for var_debug_info in &body.var_debug_info {
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
if let Some(local) = place.as_local() {
if let Some(prev_name) = local_names[local]
&& var_debug_info.name != prev_name
{
span_bug!(
var_debug_info.source_info.span,
"local {:?} has many names (`{}` vs `{}`)",
local,
prev_name,
var_debug_info.name
);
}
local_names[local] = Some(var_debug_info.name);
}
}
}
let mut mbcx = MirBorrowckCtxt {
root_cx,
infcx: &infcx,
@@ -450,7 +430,7 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
used_mut_upvars: SmallVec::new(),
borrow_set: &borrow_set,
upvars: tcx.closure_captures(def),
local_names,
local_names: OnceCell::new(),
region_names: RefCell::default(),
next_region_name: RefCell::new(1),
move_errors: Vec::new(),
@@ -682,7 +662,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
upvars: &'tcx [&'tcx ty::CapturedPlace<'tcx>],
/// Names of local (user) variables (extracted from `var_debug_info`).
local_names: IndexVec<Local, Option<Symbol>>,
local_names: OnceCell<IndexVec<Local, Option<Symbol>>>,
/// Record the region names generated for each region in the given
/// MIR def so that we can reuse them later in help/error messages.
@@ -2610,7 +2590,7 @@ fn lint_unused_mut(&self) {
};
// Skip over locals that begin with an underscore or have no name
if self.local_names[local].is_none_or(|name| name.as_str().starts_with('_')) {
if self.local_excluded_from_unused_mut_lint(local) {
continue;
}