Skip to content

Commit 942b384

Browse files
committed
Auto merge of #52405 - matthewjasper:mutability-errors, r=pnkfelix
[NLL] Mutability errors cc #51028 cc #51170 cc #46559 Closes #46629 * Better explain why the place is immutable ("immutable item" is gone) * Distinguish &T and *const T * Use better spans when a mutable borrow is for a closure capture r? @pnkfelix
2 parents 606713f + a06b243 commit 942b384

File tree

60 files changed

+1822
-351
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1822
-351
lines changed

src/librustc/mir/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ pub enum BindingForm<'tcx> {
531531
Var(VarBindingForm<'tcx>),
532532
/// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
533533
ImplicitSelf,
534+
/// Reference used in a guard expression to ensure immutability.
535+
RefForGuard,
534536
}
535537

536538
CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
@@ -555,6 +557,7 @@ mod binding_form_impl {
555557
match self {
556558
Var(binding) => binding.hash_stable(hcx, hasher),
557559
ImplicitSelf => (),
560+
RefForGuard => (),
558561
}
559562
}
560563
}

src/librustc_mir/borrow_check/error_reporting.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
202202
/// the local assigned at `location`.
203203
/// This is done by searching in statements succeeding `location`
204204
/// and originating from `maybe_closure_span`.
205-
fn find_closure_span(
205+
pub(super) fn find_closure_span(
206206
&self,
207207
maybe_closure_span: Span,
208208
location: Location,
@@ -742,6 +742,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
742742
autoderef,
743743
&including_downcast,
744744
)?;
745+
} else if let Place::Local(local) = proj.base {
746+
if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
747+
= self.mir.local_decls[local].is_user_variable {
748+
self.append_place_to_string(
749+
&proj.base,
750+
buf,
751+
autoderef,
752+
&including_downcast,
753+
)?;
754+
} else {
755+
buf.push_str(&"*");
756+
self.append_place_to_string(
757+
&proj.base,
758+
buf,
759+
autoderef,
760+
&including_downcast,
761+
)?;
762+
}
745763
} else {
746764
buf.push_str(&"*");
747765
self.append_place_to_string(

src/librustc_mir/borrow_check/mod.rs

+20-209
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc::hir::def_id::DefId;
1616
use rustc::hir::map::definitions::DefPathData;
1717
use rustc::infer::InferCtxt;
1818
use rustc::lint::builtin::UNUSED_MUT;
19-
use rustc::mir::{self, AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
19+
use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
2020
use rustc::mir::{ClearCrossCrate, Local, Location, Mir, Mutability, Operand, Place};
2121
use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind};
2222
use rustc::mir::{Terminator, TerminatorKind};
@@ -43,27 +43,27 @@ use dataflow::{do_dataflow, DebugFormatted};
4343
use dataflow::{EverInitializedPlaces, MovingOutStatements};
4444
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
4545
use util::borrowck_errors::{BorrowckErrors, Origin};
46-
use util::collect_writes::FindAssignments;
47-
use util::suggest_ref_mut;
4846

4947
use self::borrow_set::{BorrowData, BorrowSet};
5048
use self::flows::Flows;
5149
use self::location::LocationTable;
5250
use self::prefixes::PrefixSet;
5351
use self::MutateMode::{JustWrite, WriteAndRead};
52+
use self::mutability_errors::AccessKind;
5453

5554
use self::path_utils::*;
5655

5756
crate mod borrow_set;
5857
mod error_reporting;
5958
mod flows;
6059
mod location;
60+
mod move_errors;
61+
mod mutability_errors;
6162
mod path_utils;
6263
crate mod place_ext;
6364
mod places_conflict;
6465
mod prefixes;
6566
mod used_muts;
66-
mod move_errors;
6767

6868
pub(crate) mod nll;
6969

@@ -922,7 +922,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
922922
}
923923

924924
let mutability_error =
925-
self.check_access_permissions(place_span, rw, is_local_mutation_allowed, flow_state);
925+
self.check_access_permissions(
926+
place_span,
927+
rw,
928+
is_local_mutation_allowed,
929+
flow_state,
930+
context.loc,
931+
);
926932
let conflict_error =
927933
self.check_access_for_conflict(context, place_span, sd, rw, flow_state);
928934

@@ -1668,6 +1674,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16681674
}
16691675
}
16701676

1677+
16711678
/// Check the permissions for the given place and read or write kind
16721679
///
16731680
/// Returns true if an error is reported, false otherwise.
@@ -1677,17 +1684,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16771684
kind: ReadOrWrite,
16781685
is_local_mutation_allowed: LocalMutationIsAllowed,
16791686
flow_state: &Flows<'cx, 'gcx, 'tcx>,
1687+
location: Location,
16801688
) -> bool {
16811689
debug!(
16821690
"check_access_permissions({:?}, {:?}, {:?})",
16831691
place, kind, is_local_mutation_allowed
16841692
);
16851693

1686-
#[derive(Copy, Clone, Debug)]
1687-
enum AccessKind {
1688-
MutableBorrow,
1689-
Mutate,
1690-
}
16911694
let error_access;
16921695
let the_place_err;
16931696

@@ -1756,206 +1759,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17561759
}
17571760

17581761
// at this point, we have set up the error reporting state.
1759-
1760-
let mut err;
1761-
let item_msg = match self.describe_place(place) {
1762-
Some(name) => format!("immutable item `{}`", name),
1763-
None => "immutable item".to_owned(),
1764-
};
1765-
1766-
// `act` and `acted_on` are strings that let us abstract over
1767-
// the verbs used in some diagnostic messages.
1768-
let act;
1769-
let acted_on;
1770-
1771-
match error_access {
1772-
AccessKind::Mutate => {
1773-
let item_msg = match the_place_err {
1774-
Place::Projection(box Projection {
1775-
base: _,
1776-
elem: ProjectionElem::Deref,
1777-
}) => match self.describe_place(place) {
1778-
Some(description) => {
1779-
format!("`{}` which is behind a `&` reference", description)
1780-
}
1781-
None => format!("data in a `&` reference"),
1782-
},
1783-
_ => item_msg,
1784-
};
1785-
err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
1786-
act = "assign";
1787-
acted_on = "written";
1788-
}
1789-
AccessKind::MutableBorrow => {
1790-
err = self
1791-
.tcx
1792-
.cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir);
1793-
act = "borrow as mutable";
1794-
acted_on = "borrowed as mutable";
1795-
}
1796-
}
1797-
1798-
match the_place_err {
1799-
// We want to suggest users use `let mut` for local (user
1800-
// variable) mutations...
1801-
Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
1802-
// ... but it doesn't make sense to suggest it on
1803-
// variables that are `ref x`, `ref mut x`, `&self`,
1804-
// or `&mut self` (such variables are simply not
1805-
// mutable)..
1806-
let local_decl = &self.mir.local_decls[*local];
1807-
assert_eq!(local_decl.mutability, Mutability::Not);
1808-
1809-
err.span_label(span, format!("cannot {ACT}", ACT = act));
1810-
err.span_suggestion(
1811-
local_decl.source_info.span,
1812-
"consider changing this to be mutable",
1813-
format!("mut {}", local_decl.name.unwrap()),
1814-
);
1815-
}
1816-
1817-
// complete hack to approximate old AST-borrowck
1818-
// diagnostic: if the span starts with a mutable borrow of
1819-
// a local variable, then just suggest the user remove it.
1820-
Place::Local(_)
1821-
if {
1822-
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
1823-
snippet.starts_with("&mut ")
1824-
} else {
1825-
false
1826-
}
1827-
} =>
1828-
{
1829-
err.span_label(span, format!("cannot {ACT}", ACT = act));
1830-
err.span_label(span, "try removing `&mut` here");
1831-
}
1832-
1833-
// We want to point out when a `&` can be readily replaced
1834-
// with an `&mut`.
1835-
//
1836-
// FIXME: can this case be generalized to work for an
1837-
// arbitrary base for the projection?
1838-
Place::Projection(box Projection {
1839-
base: Place::Local(local),
1840-
elem: ProjectionElem::Deref,
1841-
}) if self.mir.local_decls[*local].is_user_variable.is_some() => {
1842-
let local_decl = &self.mir.local_decls[*local];
1843-
let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
1844-
ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => {
1845-
Some(suggest_ampmut_self(local_decl))
1846-
},
1847-
1848-
ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
1849-
binding_mode: ty::BindingMode::BindByValue(_),
1850-
opt_ty_info,
1851-
..
1852-
})) => Some(suggest_ampmut(
1853-
self.tcx,
1854-
self.mir,
1855-
*local,
1856-
local_decl,
1857-
*opt_ty_info,
1858-
)),
1859-
1860-
ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
1861-
binding_mode: ty::BindingMode::BindByReference(_),
1862-
..
1863-
})) => suggest_ref_mut(self.tcx, local_decl.source_info.span),
1864-
1865-
ClearCrossCrate::Clear => bug!("saw cleared local state"),
1866-
};
1867-
1868-
if let Some((err_help_span, suggested_code)) = suggestion {
1869-
err.span_suggestion(
1870-
err_help_span,
1871-
"consider changing this to be a mutable reference",
1872-
suggested_code,
1873-
);
1874-
}
1875-
1876-
if let Some(name) = local_decl.name {
1877-
err.span_label(
1878-
span,
1879-
format!(
1880-
"`{NAME}` is a `&` reference, \
1881-
so the data it refers to cannot be {ACTED_ON}",
1882-
NAME = name,
1883-
ACTED_ON = acted_on
1884-
),
1885-
);
1886-
} else {
1887-
err.span_label(
1888-
span,
1889-
format!("cannot {ACT} through `&`-reference", ACT = act),
1890-
);
1891-
}
1892-
}
1893-
1894-
_ => {
1895-
err.span_label(span, format!("cannot {ACT}", ACT = act));
1896-
}
1897-
}
1898-
1899-
err.emit();
1762+
self.report_mutability_error(
1763+
place,
1764+
span,
1765+
the_place_err,
1766+
error_access,
1767+
location,
1768+
);
19001769
return true;
1901-
1902-
fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(
1903-
local_decl: &mir::LocalDecl<'tcx>,
1904-
) -> (Span, String) {
1905-
(local_decl.source_info.span, "&mut self".to_string())
1906-
}
1907-
1908-
// When we want to suggest a user change a local variable to be a `&mut`, there
1909-
// are three potential "obvious" things to highlight:
1910-
//
1911-
// let ident [: Type] [= RightHandSideExpression];
1912-
// ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
1913-
// (1.) (2.) (3.)
1914-
//
1915-
// We can always fallback on highlighting the first. But chances are good that
1916-
// the user experience will be better if we highlight one of the others if possible;
1917-
// for example, if the RHS is present and the Type is not, then the type is going to
1918-
// be inferred *from* the RHS, which means we should highlight that (and suggest
1919-
// that they borrow the RHS mutably).
1920-
//
1921-
// This implementation attempts to emulate AST-borrowck prioritization
1922-
// by trying (3.), then (2.) and finally falling back on (1.).
1923-
fn suggest_ampmut<'cx, 'gcx, 'tcx>(
1924-
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
1925-
mir: &Mir<'tcx>,
1926-
local: Local,
1927-
local_decl: &mir::LocalDecl<'tcx>,
1928-
opt_ty_info: Option<Span>,
1929-
) -> (Span, String) {
1930-
let locations = mir.find_assignments(local);
1931-
if locations.len() > 0 {
1932-
let assignment_rhs_span = mir.source_info(locations[0]).span;
1933-
let snippet = tcx.sess.codemap().span_to_snippet(assignment_rhs_span);
1934-
if let Ok(src) = snippet {
1935-
if src.starts_with('&') {
1936-
let borrowed_expr = src[1..].to_string();
1937-
return (
1938-
assignment_rhs_span,
1939-
format!("&mut {}", borrowed_expr),
1940-
);
1941-
}
1942-
}
1943-
}
1944-
1945-
let highlight_span = match opt_ty_info {
1946-
// if this is a variable binding with an explicit type,
1947-
// try to highlight that for the suggestion.
1948-
Some(ty_span) => ty_span,
1949-
1950-
// otherwise, just highlight the span associated with
1951-
// the (MIR) LocalDecl.
1952-
None => local_decl.source_info.span,
1953-
};
1954-
1955-
let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
1956-
assert_eq!(ty_mut.mutbl, hir::MutImmutable);
1957-
(highlight_span, format!("&mut {}", ty_mut.ty))
1958-
}
19591770
}
19601771

19611772
/// Adds the place into the used mutable variables set

0 commit comments

Comments
 (0)