Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix incorrect use mut diagnostics
  • Loading branch information
arora-aman committed Jan 29, 2021
commit c748f32ee45ed42eaab4d6a62dc64720b5096c68
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,15 @@ pub struct CapturedPlace<'tcx> {
pub mutability: hir::Mutability,
}

impl CapturedPlace<'tcx> {
pub fn get_root_variable(&self) -> hir::HirId {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see a comment here, something like

/// Returns the hir-id of the root variable for the captured place. e.g., if a.b.c was captured, would return the hir-id for a.

match self.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
base => bug!("Expected upvar, found={:?}", base),
}
}
}

pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
let name = match place.base {
HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
};

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,29 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
));

item_msg = format!("`{}`", access_place_desc.unwrap());
if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
reason = ", as it is not declared as mutable".to_string();
let imm_borrow_derefed = self.upvars[upvar_index.index()]
.place
.place
.deref_tys()
.any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));

// If the place is immutable then:
//
// - Either we deref a immutable ref to get to our final place.
// - We don't capture derefs of raw ptrs
// - Or the final place is immut because the root variable of the capture
// isn't marked mut and we should suggest that to the user.
if imm_borrow_derefed {
// If we deref an immutable ref then the suggestion here doesn't help.
return;
} else {
let name = self.upvars[upvar_index.index()].name;
reason = format!(", as `{}` is not declared as mutable", name);
item_msg = format!("`{}`", access_place_desc.unwrap());
if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
reason = ", as it is not declared as mutable".to_string();
} else {
let name = self.upvars[upvar_index.index()].name;
reason = format!(", as `{}` is not declared as mutable", name);
}
}
}

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

let captured_place = &self.upvars[upvar_index.index()].place;

err.span_label(span, format!("cannot {ACT}", ACT = act));

let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
let upvar_hir_id = captured_place.get_root_variable();

if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) {
if let hir::PatKind::Binding(
hir::BindingAnnotation::Unannotated,
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
upvars: &[Upvar<'tcx>],
fr: RegionVid,
) -> Option<(Option<Symbol>, Span)> {
debug!("get_var_name_and_span_for_region(fr={:?})", fr);
Expand All @@ -21,6 +21,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!("get_var_name_and_span_for_region: attempting upvar");
self.get_upvar_index_for_region(tcx, fr)
.map(|index| {
// FIXME(project-rfc-2229#8): Use place span for diagnostics
let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index);
(Some(name), span)
})
Expand Down Expand Up @@ -59,10 +60,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
crate fn get_upvar_name_and_span_for_region(
&self,
tcx: TyCtxt<'tcx>,
upvars: &[Upvar],
upvars: &[Upvar<'tcx>],
upvar_index: usize,
) -> (Symbol, Span) {
let upvar_hir_id = upvars[upvar_index].var_hir_id;
let upvar_hir_id = upvars[upvar_index].place.get_root_variable();
debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);

let upvar_name = tcx.hir().name(upvar_hir_id);
Expand Down
30 changes: 10 additions & 20 deletions compiler/rustc_mir/src/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::{HirId, Node};
use rustc_hir::Node;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::mir::{
traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
PlaceRef, VarDebugInfoContents,
Expand All @@ -18,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind
use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt};
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
use rustc_span::{Span, Symbol, DUMMY_SP};

Expand Down Expand Up @@ -73,16 +72,15 @@ crate use region_infer::RegionInferenceContext;

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

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

/// If true, the capture is behind a reference.
by_ref: bool,

mutability: Mutability,
}

const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref];
Expand Down Expand Up @@ -161,21 +159,13 @@ fn do_mir_borrowck<'a, 'tcx>(
let upvars: Vec<_> = tables
.closure_min_captures_flattened(def.did.to_def_id())
.map(|captured_place| {
let var_hir_id = match captured_place.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
_ => bug!("Expected upvar"),
};
let var_hir_id = captured_place.get_root_variable();
let capture = captured_place.info.capture_kind;
let by_ref = match capture {
ty::UpvarCapture::ByValue(_) => false,
ty::UpvarCapture::ByRef(..) => true,
};
Upvar {
name: tcx.hir().name(var_hir_id),
var_hir_id,
by_ref,
mutability: captured_place.mutability,
}
Upvar { name: tcx.hir().name(var_hir_id), place: captured_place.clone(), by_ref }
})
.collect();

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

/// Information about upvars not necessarily preserved in types or MIR
upvars: Vec<Upvar>,
upvars: Vec<Upvar<'tcx>>,

/// Names of local (user) variables (extracted from `var_debug_info`).
local_names: IndexVec<Local, Option<Symbol>>,
Expand Down Expand Up @@ -2251,7 +2241,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
place={:?}",
upvar, is_local_mutation_allowed, place
);
match (upvar.mutability, is_local_mutation_allowed) {
match (upvar.place.mutability, is_local_mutation_allowed) {
(
Mutability::Not,
LocalMutationIsAllowed::No
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/borrow_check/nll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>,
upvars: &[Upvar],
upvars: &[Upvar<'tcx>],
) -> NllOutput<'tcx> {
let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default());

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/borrow_check/path_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
/// of a closure type.
pub(crate) fn is_upvar_field_projection(
tcx: TyCtxt<'tcx>,
upvars: &[Upvar],
upvars: &[Upvar<'tcx>],
place_ref: PlaceRef<'tcx>,
body: &Body<'tcx>,
) -> Option<Field> {
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_mir/src/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
move_data: &MoveData<'tcx>,
elements: &Rc<RegionValueElements>,
upvars: &[Upvar],
upvars: &[Upvar<'tcx>],
) -> MirTypeckResults<'tcx> {
let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
let mut constraints = MirTypeckRegionConstraints {
Expand Down Expand Up @@ -821,7 +821,7 @@ struct BorrowCheckContext<'a, 'tcx> {
all_facts: &'a mut Option<AllFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
upvars: &'a [Upvar],
upvars: &'a [Upvar<'tcx>],
}

crate struct MirTypeckResults<'tcx> {
Expand Down Expand Up @@ -2490,7 +2490,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
body,
);
let category = if let Some(field) = field {
ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id)
let var_hir_id = self.borrowck_context.upvars[field.index()].place.get_root_variable();
// FIXME(project-rfc-2229#8): Use Place for better diagnostics
ConstraintCategory::ClosureUpvar(var_hir_id)
} else {
ConstraintCategory::Boring
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ fn main() {
let mut c = || {
//~^ ERROR: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
z.0.0.0 = format!("X1");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected us to not try read the place z on its own. When the feature isn't enabled we only read z.0.0.0.

Debug logs from the report_mutability_error when the feature is enabled.

DEBUG rustc_mir::borrow_check::diagnostics::mutability_errors report_mutability_error: access_place_desc=Some("z")
....
DEBUG rustc_mir::borrow_check::diagnostics::mutability_errors report_mutability_error: access_place_desc=Some("z.0.0.0")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely not the error message we want-- we'd like the error to be reported on z.0.0.0, right? I think this may be related to my comment about the used_mut_upvars but I'm not 100% sure.

//~^ ERROR: cannot assign to `z`, as it is not declared as mutable
};

c();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ LL | #![feature(capture_disjoint_fields)]
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error[E0594]: cannot assign to `z`, as it is not declared as mutable
--> $DIR/cant-mutate-imm-borrow.rs:16:9
|
LL | let z = (&mut y, "Z");
| - help: consider changing this to be mutable: `mut z`
...
LL | z.0.0.0 = format!("X1");
| ^^^^^^^ cannot assign

error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
--> $DIR/cant-mutate-imm-borrow.rs:14:17
|
Expand All @@ -25,7 +16,6 @@ LL |
LL | z.0.0.0 = format!("X1");
| - mutable borrow occurs due to use of `z.0.0.0` in closure

error: aborting due to 2 previous errors; 1 warning emitted
error: aborting due to previous error; 1 warning emitted

Some errors have detailed explanations: E0594, E0596.
For more information about an error, try `rustc --explain E0594`.
For more information about this error, try `rustc --explain E0596`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete

// Ensure that diagnostics for mutability error (because the root variable
// isn't mutable) work with `capture_disjoint_fields` enabled.

fn mut_error_struct() {
let x = (10, 10);
let y = (x, 10);
let z = (y, 10);

let mut c = || {
z.0.0.0 = 20;
//~^ ERROR: cannot assign to `z`, as it is not declared as mutable
};

c();
}

fn mut_error_box() {
let x = (10, 10);
let bx = Box::new(x);

let mut c = || {
bx.0 = 20;
//~^ ERROR: cannot assign to `bx`, as it is not declared as mutable
};

c();
}

fn main() {
mut_error_struct();
mut_error_box();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/cant-mutate-imm.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error[E0594]: cannot assign to `z`, as it is not declared as mutable
--> $DIR/cant-mutate-imm.rs:13:9
|
LL | let z = (y, 10);
| - help: consider changing this to be mutable: `mut z`
...
LL | z.0.0.0 = 20;
| ^^^^^^^^^^^^ cannot assign

error[E0594]: cannot assign to `bx`, as it is not declared as mutable
--> $DIR/cant-mutate-imm.rs:25:9
|
LL | let bx = Box::new(x);
| -- help: consider changing this to be mutable: `mut bx`
...
LL | bx.0 = 20;
| ^^^^^^^^^ cannot assign

error: aborting due to 2 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0594`.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ fn imm_mut_ref() {
let c = || {
//~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
**ref_mref_x = y;
//~^ERROR: cannot assign to `ref_mref_x`, as it is not declared as mutable
};

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

c();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ LL | #![feature(capture_disjoint_fields)]
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error[E0594]: cannot assign to `ref_mref_x`, as it is not declared as mutable
--> $DIR/mut_ref.rs:15:9
|
LL | let ref_mref_x = &mref_x;
| ---------- help: consider changing this to be mutable: `mut ref_mref_x`
...
LL | **ref_mref_x = y;
| ^^^^^^^^^^^^ cannot assign

error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
--> $DIR/mut_ref.rs:13:13
|
Expand All @@ -28,25 +19,15 @@ LL |
LL | **ref_mref_x = y;
| ---------- mutable borrow occurs due to use of `**ref_mref_x` in closure

error[E0594]: cannot assign to `mref_ref_x`, as it is not declared as mutable
--> $DIR/mut_ref.rs:30:9
|
LL | let mref_ref_x = &mut ref_x;
| ---------- help: consider changing this to be mutable: `mut mref_ref_x`
...
LL | **mref_ref_x = y;
| ^^^^^^^^^^^^ cannot assign

error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
--> $DIR/mut_ref.rs:28:13
--> $DIR/mut_ref.rs:27:13
|
LL | let c = || {
| ^^ cannot borrow as mutable
LL |
LL | **mref_ref_x = y;
| ---------- mutable borrow occurs due to use of `**mref_ref_x` in closure

error: aborting due to 4 previous errors; 1 warning emitted
error: aborting due to 2 previous errors; 1 warning emitted

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