Skip to content

Commit c748f32

Browse files
committed
Fix incorrect use mut diagnostics
1 parent 604cbdc commit c748f32

File tree

14 files changed

+129
-72
lines changed

14 files changed

+129
-72
lines changed

compiler/rustc_middle/src/ty/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,15 @@ pub struct CapturedPlace<'tcx> {
674674
pub mutability: hir::Mutability,
675675
}
676676

677+
impl CapturedPlace<'tcx> {
678+
pub fn get_root_variable(&self) -> hir::HirId {
679+
match self.place.base {
680+
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
681+
base => bug!("Expected upvar, found={:?}", base),
682+
}
683+
}
684+
}
685+
677686
pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
678687
let name = match place.base {
679688
HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(),

compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
345345
};
346346

347347
let upvar = &self.upvars[upvar_field.unwrap().index()];
348-
let upvar_hir_id = upvar.var_hir_id;
348+
// FIXME(project-rfc-2229#8): Improve borrow-check diagnostics in case of precise
349+
// capture.
350+
let upvar_hir_id = upvar.place.get_root_variable();
349351
let upvar_name = upvar.name;
350352
let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
351353

compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs

+26-6
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,29 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
6464
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
6565
));
6666

67-
item_msg = format!("`{}`", access_place_desc.unwrap());
68-
if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
69-
reason = ", as it is not declared as mutable".to_string();
67+
let imm_borrow_derefed = self.upvars[upvar_index.index()]
68+
.place
69+
.place
70+
.deref_tys()
71+
.any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
72+
73+
// If the place is immutable then:
74+
//
75+
// - Either we deref a immutable ref to get to our final place.
76+
// - We don't capture derefs of raw ptrs
77+
// - Or the final place is immut because the root variable of the capture
78+
// isn't marked mut and we should suggest that to the user.
79+
if imm_borrow_derefed {
80+
// If we deref an immutable ref then the suggestion here doesn't help.
81+
return;
7082
} else {
71-
let name = self.upvars[upvar_index.index()].name;
72-
reason = format!(", as `{}` is not declared as mutable", name);
83+
item_msg = format!("`{}`", access_place_desc.unwrap());
84+
if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
85+
reason = ", as it is not declared as mutable".to_string();
86+
} else {
87+
let name = self.upvars[upvar_index.index()].name;
88+
reason = format!(", as `{}` is not declared as mutable", name);
89+
}
7390
}
7491
}
7592

@@ -259,9 +276,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
259276
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
260277
));
261278

279+
let captured_place = &self.upvars[upvar_index.index()].place;
280+
262281
err.span_label(span, format!("cannot {ACT}", ACT = act));
263282

264-
let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
283+
let upvar_hir_id = captured_place.get_root_variable();
284+
265285
if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) {
266286
if let hir::PatKind::Binding(
267287
hir::BindingAnnotation::Unannotated,

compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
1212
tcx: TyCtxt<'tcx>,
1313
body: &Body<'tcx>,
1414
local_names: &IndexVec<Local, Option<Symbol>>,
15-
upvars: &[Upvar],
15+
upvars: &[Upvar<'tcx>],
1616
fr: RegionVid,
1717
) -> Option<(Option<Symbol>, Span)> {
1818
debug!("get_var_name_and_span_for_region(fr={:?})", fr);
@@ -21,6 +21,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
2121
debug!("get_var_name_and_span_for_region: attempting upvar");
2222
self.get_upvar_index_for_region(tcx, fr)
2323
.map(|index| {
24+
// FIXME(project-rfc-2229#8): Use place span for diagnostics
2425
let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index);
2526
(Some(name), span)
2627
})
@@ -59,10 +60,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
5960
crate fn get_upvar_name_and_span_for_region(
6061
&self,
6162
tcx: TyCtxt<'tcx>,
62-
upvars: &[Upvar],
63+
upvars: &[Upvar<'tcx>],
6364
upvar_index: usize,
6465
) -> (Symbol, Span) {
65-
let upvar_hir_id = upvars[upvar_index].var_hir_id;
66+
let upvar_hir_id = upvars[upvar_index].place.get_root_variable();
6667
debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);
6768

6869
let upvar_name = tcx.hir().name(upvar_hir_id);

compiler/rustc_mir/src/borrow_check/mod.rs

+10-20
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ use rustc_data_structures::graph::dominators::Dominators;
55
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
66
use rustc_hir as hir;
77
use rustc_hir::def_id::LocalDefId;
8-
use rustc_hir::{HirId, Node};
8+
use rustc_hir::Node;
99
use rustc_index::bit_set::BitSet;
1010
use rustc_index::vec::IndexVec;
1111
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
12-
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
1312
use rustc_middle::mir::{
1413
traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
1514
PlaceRef, VarDebugInfoContents,
@@ -18,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind
1817
use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
1918
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
2019
use rustc_middle::ty::query::Providers;
21-
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt};
20+
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
2221
use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
2322
use rustc_span::{Span, Symbol, DUMMY_SP};
2423

@@ -73,16 +72,15 @@ crate use region_infer::RegionInferenceContext;
7372

7473
// FIXME(eddyb) perhaps move this somewhere more centrally.
7574
#[derive(Debug)]
76-
crate struct Upvar {
75+
crate struct Upvar<'tcx> {
76+
// FIXME(project-rfc-2229#8): ty::CapturePlace should have a to_string(), or similar
77+
// then this should not be needed.
7778
name: Symbol,
7879

79-
// FIXME(project-rfc-2229#8): This should use Place or something similar
80-
var_hir_id: HirId,
80+
place: CapturedPlace<'tcx>,
8181

8282
/// If true, the capture is behind a reference.
8383
by_ref: bool,
84-
85-
mutability: Mutability,
8684
}
8785

8886
const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref];
@@ -161,21 +159,13 @@ fn do_mir_borrowck<'a, 'tcx>(
161159
let upvars: Vec<_> = tables
162160
.closure_min_captures_flattened(def.did.to_def_id())
163161
.map(|captured_place| {
164-
let var_hir_id = match captured_place.place.base {
165-
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
166-
_ => bug!("Expected upvar"),
167-
};
162+
let var_hir_id = captured_place.get_root_variable();
168163
let capture = captured_place.info.capture_kind;
169164
let by_ref = match capture {
170165
ty::UpvarCapture::ByValue(_) => false,
171166
ty::UpvarCapture::ByRef(..) => true,
172167
};
173-
Upvar {
174-
name: tcx.hir().name(var_hir_id),
175-
var_hir_id,
176-
by_ref,
177-
mutability: captured_place.mutability,
178-
}
168+
Upvar { name: tcx.hir().name(var_hir_id), place: captured_place.clone(), by_ref }
179169
})
180170
.collect();
181171

@@ -544,7 +534,7 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
544534
dominators: Dominators<BasicBlock>,
545535

546536
/// Information about upvars not necessarily preserved in types or MIR
547-
upvars: Vec<Upvar>,
537+
upvars: Vec<Upvar<'tcx>>,
548538

549539
/// Names of local (user) variables (extracted from `var_debug_info`).
550540
local_names: IndexVec<Local, Option<Symbol>>,
@@ -2251,7 +2241,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
22512241
place={:?}",
22522242
upvar, is_local_mutation_allowed, place
22532243
);
2254-
match (upvar.mutability, is_local_mutation_allowed) {
2244+
match (upvar.place.mutability, is_local_mutation_allowed) {
22552245
(
22562246
Mutability::Not,
22572247
LocalMutationIsAllowed::No

compiler/rustc_mir/src/borrow_check/nll.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
165165
flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
166166
move_data: &MoveData<'tcx>,
167167
borrow_set: &BorrowSet<'tcx>,
168-
upvars: &[Upvar],
168+
upvars: &[Upvar<'tcx>],
169169
) -> NllOutput<'tcx> {
170170
let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default());
171171

compiler/rustc_mir/src/borrow_check/path_utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
143143
/// of a closure type.
144144
pub(crate) fn is_upvar_field_projection(
145145
tcx: TyCtxt<'tcx>,
146-
upvars: &[Upvar],
146+
upvars: &[Upvar<'tcx>],
147147
place_ref: PlaceRef<'tcx>,
148148
body: &Body<'tcx>,
149149
) -> Option<Field> {

compiler/rustc_mir/src/borrow_check/type_check/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
132132
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
133133
move_data: &MoveData<'tcx>,
134134
elements: &Rc<RegionValueElements>,
135-
upvars: &[Upvar],
135+
upvars: &[Upvar<'tcx>],
136136
) -> MirTypeckResults<'tcx> {
137137
let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
138138
let mut constraints = MirTypeckRegionConstraints {
@@ -821,7 +821,7 @@ struct BorrowCheckContext<'a, 'tcx> {
821821
all_facts: &'a mut Option<AllFacts>,
822822
borrow_set: &'a BorrowSet<'tcx>,
823823
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
824-
upvars: &'a [Upvar],
824+
upvars: &'a [Upvar<'tcx>],
825825
}
826826

827827
crate struct MirTypeckResults<'tcx> {
@@ -2490,7 +2490,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
24902490
body,
24912491
);
24922492
let category = if let Some(field) = field {
2493-
ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id)
2493+
let var_hir_id = self.borrowck_context.upvars[field.index()].place.get_root_variable();
2494+
// FIXME(project-rfc-2229#8): Use Place for better diagnostics
2495+
ConstraintCategory::ClosureUpvar(var_hir_id)
24942496
} else {
24952497
ConstraintCategory::Boring
24962498
};

src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ fn main() {
1414
let mut c = || {
1515
//~^ ERROR: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
1616
z.0.0.0 = format!("X1");
17-
//~^ ERROR: cannot assign to `z`, as it is not declared as mutable
1817
};
1918

2019
c();

src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr

+2-12
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@ LL | #![feature(capture_disjoint_fields)]
77
= note: `#[warn(incomplete_features)]` on by default
88
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
99

10-
error[E0594]: cannot assign to `z`, as it is not declared as mutable
11-
--> $DIR/cant-mutate-imm-borrow.rs:16:9
12-
|
13-
LL | let z = (&mut y, "Z");
14-
| - help: consider changing this to be mutable: `mut z`
15-
...
16-
LL | z.0.0.0 = format!("X1");
17-
| ^^^^^^^ cannot assign
18-
1910
error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
2011
--> $DIR/cant-mutate-imm-borrow.rs:14:17
2112
|
@@ -25,7 +16,6 @@ LL |
2516
LL | z.0.0.0 = format!("X1");
2617
| - mutable borrow occurs due to use of `z.0.0.0` in closure
2718

28-
error: aborting due to 2 previous errors; 1 warning emitted
19+
error: aborting due to previous error; 1 warning emitted
2920

30-
Some errors have detailed explanations: E0594, E0596.
31-
For more information about an error, try `rustc --explain E0594`.
21+
For more information about this error, try `rustc --explain E0596`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![feature(capture_disjoint_fields)]
2+
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
3+
4+
// Ensure that diagnostics for mutability error (because the root variable
5+
// isn't mutable) work with `capture_disjoint_fields` enabled.
6+
7+
fn mut_error_struct() {
8+
let x = (10, 10);
9+
let y = (x, 10);
10+
let z = (y, 10);
11+
12+
let mut c = || {
13+
z.0.0.0 = 20;
14+
//~^ ERROR: cannot assign to `z`, as it is not declared as mutable
15+
};
16+
17+
c();
18+
}
19+
20+
fn mut_error_box() {
21+
let x = (10, 10);
22+
let bx = Box::new(x);
23+
24+
let mut c = || {
25+
bx.0 = 20;
26+
//~^ ERROR: cannot assign to `bx`, as it is not declared as mutable
27+
};
28+
29+
c();
30+
}
31+
32+
fn main() {
33+
mut_error_struct();
34+
mut_error_box();
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/cant-mutate-imm.rs:1:12
3+
|
4+
LL | #![feature(capture_disjoint_fields)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
9+
10+
error[E0594]: cannot assign to `z`, as it is not declared as mutable
11+
--> $DIR/cant-mutate-imm.rs:13:9
12+
|
13+
LL | let z = (y, 10);
14+
| - help: consider changing this to be mutable: `mut z`
15+
...
16+
LL | z.0.0.0 = 20;
17+
| ^^^^^^^^^^^^ cannot assign
18+
19+
error[E0594]: cannot assign to `bx`, as it is not declared as mutable
20+
--> $DIR/cant-mutate-imm.rs:25:9
21+
|
22+
LL | let bx = Box::new(x);
23+
| -- help: consider changing this to be mutable: `mut bx`
24+
...
25+
LL | bx.0 = 20;
26+
| ^^^^^^^^^ cannot assign
27+
28+
error: aborting due to 2 previous errors; 1 warning emitted
29+
30+
For more information about this error, try `rustc --explain E0594`.

src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs

-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ fn imm_mut_ref() {
1313
let c = || {
1414
//~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
1515
**ref_mref_x = y;
16-
//~^ERROR: cannot assign to `ref_mref_x`, as it is not declared as mutable
1716
};
1817

1918
c();
@@ -28,7 +27,6 @@ fn mut_imm_ref() {
2827
let c = || {
2928
//~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
3029
**mref_ref_x = y;
31-
//~^ERROR: cannot assign to `mref_ref_x`, as it is not declared as mutable
3230
};
3331

3432
c();

src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr

+3-22
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@ LL | #![feature(capture_disjoint_fields)]
77
= note: `#[warn(incomplete_features)]` on by default
88
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
99

10-
error[E0594]: cannot assign to `ref_mref_x`, as it is not declared as mutable
11-
--> $DIR/mut_ref.rs:15:9
12-
|
13-
LL | let ref_mref_x = &mref_x;
14-
| ---------- help: consider changing this to be mutable: `mut ref_mref_x`
15-
...
16-
LL | **ref_mref_x = y;
17-
| ^^^^^^^^^^^^ cannot assign
18-
1910
error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
2011
--> $DIR/mut_ref.rs:13:13
2112
|
@@ -28,25 +19,15 @@ LL |
2819
LL | **ref_mref_x = y;
2920
| ---------- mutable borrow occurs due to use of `**ref_mref_x` in closure
3021

31-
error[E0594]: cannot assign to `mref_ref_x`, as it is not declared as mutable
32-
--> $DIR/mut_ref.rs:30:9
33-
|
34-
LL | let mref_ref_x = &mut ref_x;
35-
| ---------- help: consider changing this to be mutable: `mut mref_ref_x`
36-
...
37-
LL | **mref_ref_x = y;
38-
| ^^^^^^^^^^^^ cannot assign
39-
4022
error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
41-
--> $DIR/mut_ref.rs:28:13
23+
--> $DIR/mut_ref.rs:27:13
4224
|
4325
LL | let c = || {
4426
| ^^ cannot borrow as mutable
4527
LL |
4628
LL | **mref_ref_x = y;
4729
| ---------- mutable borrow occurs due to use of `**mref_ref_x` in closure
4830

49-
error: aborting due to 4 previous errors; 1 warning emitted
31+
error: aborting due to 2 previous errors; 1 warning emitted
5032

51-
Some errors have detailed explanations: E0594, E0596.
52-
For more information about an error, try `rustc --explain E0594`.
33+
For more information about this error, try `rustc --explain E0596`.

0 commit comments

Comments
 (0)