Skip to content

Commit bed4dbd

Browse files
improve diagnostics so that the message respect the locals storing upvars
... and treat them as such
1 parent d2ba33c commit bed4dbd

File tree

6 files changed

+118
-56
lines changed

6 files changed

+118
-56
lines changed

Diff for: compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+63-42
Original file line numberDiff line numberDiff line change
@@ -393,49 +393,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
393393
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
394394
));
395395

396-
let captured_place = self.upvars[upvar_index.index()];
397-
398-
err.span_label(span, format!("cannot {act}"));
399-
400-
let upvar_hir_id = captured_place.get_root_variable();
401-
402-
if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
403-
&& let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
404-
pat.kind
405-
{
406-
if upvar_ident.name == kw::SelfLower {
407-
for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
408-
if let Some(fn_decl) = node.fn_decl() {
409-
if !matches!(
410-
fn_decl.implicit_self,
411-
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
412-
) {
413-
err.span_suggestion_verbose(
414-
upvar_ident.span.shrink_to_lo(),
415-
"consider changing this to be mutable",
416-
"mut ",
417-
Applicability::MachineApplicable,
418-
);
419-
break;
420-
}
421-
}
422-
}
423-
} else {
424-
err.span_suggestion_verbose(
425-
upvar_ident.span.shrink_to_lo(),
426-
"consider changing this to be mutable",
427-
"mut ",
428-
Applicability::MachineApplicable,
429-
);
430-
}
431-
}
396+
self.suggest_mutable_upvar(*upvar_index, the_place_err, &mut err, span, act);
397+
}
432398

433-
let tcx = self.infcx.tcx;
434-
if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
435-
&& let ty::Closure(id, _) = *ty.kind()
436-
{
437-
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
438-
}
399+
PlaceRef { local, projection: [] }
400+
if let Some(upvar_index) = self
401+
.body
402+
.local_upvar_map
403+
.iter_enumerated()
404+
.filter_map(|(field, &local)| local.map(|local| (field, local)))
405+
.find_map(|(field, relocated)| (relocated == local).then_some(field)) =>
406+
{
407+
self.suggest_mutable_upvar(upvar_index, the_place_err, &mut err, span, act);
439408
}
440409

441410
// complete hack to approximate old AST-borrowck
@@ -542,6 +511,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
542511
}
543512
}
544513

514+
fn suggest_mutable_upvar(
515+
&self,
516+
upvar_index: FieldIdx,
517+
the_place_err: PlaceRef<'tcx>,
518+
err: &mut Diag<'infcx>,
519+
span: Span,
520+
act: &str,
521+
) {
522+
let captured_place = self.upvars[upvar_index.index()];
523+
524+
err.span_label(span, format!("cannot {act}"));
525+
526+
let upvar_hir_id = captured_place.get_root_variable();
527+
528+
if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
529+
&& let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) = pat.kind
530+
{
531+
if upvar_ident.name == kw::SelfLower {
532+
for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
533+
if let Some(fn_decl) = node.fn_decl() {
534+
if !matches!(
535+
fn_decl.implicit_self,
536+
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
537+
) {
538+
err.span_suggestion_verbose(
539+
upvar_ident.span.shrink_to_lo(),
540+
"consider changing this to be mutable",
541+
"mut ",
542+
Applicability::MachineApplicable,
543+
);
544+
break;
545+
}
546+
}
547+
}
548+
} else {
549+
err.span_suggestion_verbose(
550+
upvar_ident.span.shrink_to_lo(),
551+
"consider changing this to be mutable",
552+
"mut ",
553+
Applicability::MachineApplicable,
554+
);
555+
}
556+
}
557+
558+
let tcx = self.infcx.tcx;
559+
if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
560+
&& let ty::Closure(id, _) = *ty.kind()
561+
{
562+
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, err);
563+
}
564+
}
565+
545566
/// Suggest `map[k] = v` => `map.insert(k, v)` and the like.
546567
fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
547568
let Some(adt) = ty.ty_adt_def() else { return };

Diff for: compiler/rustc_borrowck/src/lib.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::ops::{ControlFlow, Deref};
2424
use rustc_abi::FieldIdx;
2525
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
2626
use rustc_data_structures::graph::dominators::Dominators;
27+
use rustc_data_structures::unord::UnordMap;
2728
use rustc_errors::LintDiagnostic;
2829
use rustc_hir as hir;
2930
use rustc_hir::CRATE_HIR_ID;
@@ -262,6 +263,7 @@ fn do_mir_borrowck<'tcx>(
262263
regioncx: &regioncx,
263264
used_mut: Default::default(),
264265
used_mut_upvars: SmallVec::new(),
266+
local_from_upvars: UnordMap::default(),
265267
borrow_set: &borrow_set,
266268
upvars: &[],
267269
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
@@ -287,6 +289,11 @@ fn do_mir_borrowck<'tcx>(
287289
promoted_mbcx.report_move_errors();
288290
}
289291

292+
let mut local_from_upvars = UnordMap::default();
293+
for (field, &local) in body.local_upvar_map.iter_enumerated() {
294+
let Some(local) = local else { continue };
295+
local_from_upvars.insert(local, field);
296+
}
290297
let mut mbcx = MirBorrowckCtxt {
291298
infcx: &infcx,
292299
body,
@@ -301,6 +308,7 @@ fn do_mir_borrowck<'tcx>(
301308
regioncx: &regioncx,
302309
used_mut: Default::default(),
303310
used_mut_upvars: SmallVec::new(),
311+
local_from_upvars,
304312
borrow_set: &borrow_set,
305313
upvars: tcx.closure_captures(def),
306314
local_names,
@@ -556,6 +564,9 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
556564
/// If the function we're checking is a closure, then we'll need to report back the list of
557565
/// mutable upvars that have been used. This field keeps track of them.
558566
used_mut_upvars: SmallVec<[FieldIdx; 8]>,
567+
/// Since upvars are moved to real locals, we need to map mutations to the locals back to
568+
/// the upvars, so that used_mut_upvars is up-to-date.
569+
local_from_upvars: UnordMap<Local, FieldIdx>,
559570
/// Region inference context. This contains the results from region inference and lets us e.g.
560571
/// find out which CFG points are contained in each borrow region.
561572
regioncx: &'a RegionInferenceContext<'tcx>,
@@ -2266,7 +2277,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
22662277

22672278
// at this point, we have set up the error reporting state.
22682279
if let Some(init_index) = previously_initialized {
2269-
if let (AccessKind::Mutate, Some(_)) = (error_access, place.as_local()) {
2280+
if let (AccessKind::Mutate, Some(local)) = (error_access, place.as_local())
2281+
&& self.body.local_upvar_map.iter().flatten().all(|upvar| upvar != &local)
2282+
{
22702283
// If this is a mutate access to an immutable local variable with no projections
22712284
// report the error as an illegal reassignment
22722285
let init = &self.move_data.inits[init_index];
@@ -2294,10 +2307,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
22942307
// If the local may have been initialized, and it is now currently being
22952308
// mutated, then it is justified to be annotated with the `mut`
22962309
// keyword, since the mutation may be a possible reassignment.
2297-
if is_local_mutation_allowed != LocalMutationIsAllowed::Yes
2298-
&& self.is_local_ever_initialized(local, state).is_some()
2299-
{
2300-
self.used_mut.insert(local);
2310+
if !matches!(is_local_mutation_allowed, LocalMutationIsAllowed::Yes) {
2311+
if self.is_local_ever_initialized(local, state).is_some() {
2312+
self.used_mut.insert(local);
2313+
} else if let Some(&field) = self.local_from_upvars.get(&local) {
2314+
self.used_mut_upvars.push(field);
2315+
}
23012316
}
23022317
}
23032318
RootPlace {
@@ -2315,6 +2330,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
23152330
projection: place_projection,
23162331
}) {
23172332
self.used_mut_upvars.push(field);
2333+
} else if let Some(&field) = self.local_from_upvars.get(&place_local) {
2334+
self.used_mut_upvars.push(field);
23182335
}
23192336
}
23202337
}

Diff for: compiler/rustc_mir_transform/src/deref_separator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> {
4040
ty,
4141
self.local_decls[p_ref.local].source_info.span,
4242
LocalInfo::DerefTemp,
43+
false,
4344
);
4445

4546
// We are adding current p_ref's projections to our

Diff for: compiler/rustc_mir_transform/src/pass_manager.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,20 @@ fn run_passes_inner<'tcx>(
294294
if dump_enabled {
295295
dump_mir_for_pass(tcx, body, name, true);
296296
}
297+
let (dialect, phase_num) = body.phase.index();
297298
if validate {
298-
validate_body(tcx, body, format!("after pass {name}"));
299+
validate_body(
300+
tcx,
301+
body,
302+
format!("after pass {name} {dialect}-{phase_num}-{:03}", body.pass_count),
303+
);
299304
}
300305
if lint {
301-
lint_body(tcx, body, format!("after pass {name}"));
306+
lint_body(
307+
tcx,
308+
body,
309+
format!("after pass {name} {dialect}-{phase_num}-{:03}", body.pass_count),
310+
);
302311
}
303312

304313
body.pass_count += 1;

Diff for: compiler/rustc_mir_transform/src/patch.rs

+4
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,14 @@ impl<'tcx> MirPatch<'tcx> {
154154
ty: Ty<'tcx>,
155155
span: Span,
156156
local_info: LocalInfo<'tcx>,
157+
immutable: bool,
157158
) -> Local {
158159
let index = self.next_local;
159160
self.next_local += 1;
160161
let mut new_decl = LocalDecl::new(ty, span);
162+
if immutable {
163+
new_decl = new_decl.immutable();
164+
}
161165
**new_decl.local_info.as_mut().unwrap_crate_local() = local_info;
162166
self.new_locals.push(new_decl);
163167
Local::new(index)

Diff for: compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::borrow::Cow;
55
use std::iter;
66

77
use itertools::{EitherOrBoth, Itertools};
8-
use rustc_abi::ExternAbi;
8+
use rustc_abi::{ExternAbi, FIRST_VARIANT};
99
use rustc_data_structures::fx::FxHashSet;
1010
use rustc_data_structures::stack::ensure_sufficient_stack;
1111
use rustc_errors::codes::*;
@@ -2360,18 +2360,28 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
23602360
&& let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did)
23612361
{
23622362
debug!(?coroutine_info);
2363-
'find_source: for (variant, source_info) in
2364-
coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info)
2363+
// Variants are scanned "in reverse" because suspension points
2364+
// tend to contain more diagnostic information than the unresumed state.
2365+
'find_source: for ((variant_idx, variant), source_info) in coroutine_info
2366+
.variant_fields
2367+
.iter_enumerated()
2368+
.zip(&coroutine_info.variant_source_info)
2369+
.rev()
23652370
{
23662371
debug!(?variant);
23672372
for &local in variant {
23682373
let decl = &coroutine_info.field_tys[local];
23692374
debug!(?decl);
23702375
if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits {
2371-
interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(
2372-
decl.source_info.span,
2373-
Some((source_info.span, from_awaited_ty)),
2374-
));
2376+
let span = decl.source_info.span;
2377+
if variant_idx == FIRST_VARIANT {
2378+
interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Upvar(span));
2379+
} else {
2380+
interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(
2381+
span,
2382+
Some((source_info.span, from_awaited_ty)),
2383+
));
2384+
}
23752385
break 'find_source;
23762386
}
23772387
}

0 commit comments

Comments
 (0)