Auto merge of #61590 - matthewjasper:remove-borrowck-mir-dependency, r=pnkfelix

Remove rustc_mir dependency from rustc_borrowck

Also renames `rustc_borrowck` to `rustc_ast_borrowck` and removes all error reporting from it.

cc #59193
This commit is contained in:
bors 2019-07-12 04:56:05 +00:00
commit 1b1b538843
23 changed files with 834 additions and 2651 deletions

View file

@ -2946,7 +2946,7 @@ dependencies = [
]
[[package]]
name = "rustc_borrowck"
name = "rustc_ast_borrowck"
version = "0.0.0"
dependencies = [
"graphviz 0.0.0",
@ -2954,7 +2954,6 @@ dependencies = [
"rustc 0.0.0",
"rustc_data_structures 0.0.0",
"rustc_errors 0.0.0",
"rustc_mir 0.0.0",
"syntax 0.0.0",
"syntax_pos 0.0.0",
]
@ -3045,7 +3044,7 @@ dependencies = [
"rustc 0.0.0",
"rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_allocator 0.0.0",
"rustc_borrowck 0.0.0",
"rustc_ast_borrowck 0.0.0",
"rustc_codegen_utils 0.0.0",
"rustc_data_structures 0.0.0",
"rustc_errors 0.0.0",
@ -3110,7 +3109,7 @@ dependencies = [
"rustc 0.0.0",
"rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_allocator 0.0.0",
"rustc_borrowck 0.0.0",
"rustc_ast_borrowck 0.0.0",
"rustc_codegen_ssa 0.0.0",
"rustc_codegen_utils 0.0.0",
"rustc_data_structures 0.0.0",

View file

@ -1,6 +1,4 @@
use crate::ich::StableHashingContext;
use crate::hir::HirId;
use crate::util::nodemap::FxHashSet;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
StableHasherResult};
@ -18,7 +16,6 @@ impl_stable_hash_for!(enum self::SignalledError { SawSomeError, NoErrorsSeen });
#[derive(Debug, Default, RustcEncodable, RustcDecodable)]
pub struct BorrowCheckResult {
pub used_mut_nodes: FxHashSet<HirId>,
pub signalled_any_error: SignalledError,
}
@ -27,10 +24,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for BorrowCheckResult {
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
let BorrowCheckResult {
ref used_mut_nodes,
ref signalled_any_error,
} = *self;
used_mut_nodes.hash_stable(hcx, hasher);
signalled_any_error.hash_stable(hcx, hasher);
}
}

View file

@ -66,7 +66,6 @@ use crate::hir::def::{CtorOf, Res, DefKind, CtorKind};
use crate::ty::adjustment;
use crate::ty::{self, DefIdTree, Ty, TyCtxt};
use crate::ty::fold::TypeFoldable;
use crate::ty::layout::VariantIdx;
use crate::hir::{MutImmutable, MutMutable, PatKind};
use crate::hir::pat_util::EnumerateAndAdjustIterator;
@ -79,7 +78,6 @@ use std::borrow::Cow;
use std::fmt;
use std::hash::{Hash, Hasher};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::indexed_vec::Idx;
use std::rc::Rc;
use crate::util::nodemap::ItemLocalSet;
@ -198,79 +196,6 @@ pub struct cmt_<'tcx> {
pub type cmt<'tcx> = Rc<cmt_<'tcx>>;
pub enum ImmutabilityBlame<'tcx> {
ImmLocal(hir::HirId),
ClosureEnv(LocalDefId),
LocalDeref(hir::HirId),
AdtFieldDeref(&'tcx ty::AdtDef, &'tcx ty::FieldDef)
}
impl<'tcx> cmt_<'tcx> {
fn resolve_field(&self, field_index: usize) -> Option<(&'tcx ty::AdtDef, &'tcx ty::FieldDef)>
{
let adt_def = match self.ty.sty {
ty::Adt(def, _) => def,
ty::Tuple(..) => return None,
// closures get `Categorization::Upvar` rather than `Categorization::Interior`
_ => bug!("interior cmt {:?} is not an ADT", self)
};
let variant_def = match self.cat {
Categorization::Downcast(_, variant_did) => {
adt_def.variant_with_id(variant_did)
}
_ => {
assert_eq!(adt_def.variants.len(), 1);
&adt_def.variants[VariantIdx::new(0)]
}
};
Some((adt_def, &variant_def.fields[field_index]))
}
pub fn immutability_blame(&self) -> Option<ImmutabilityBlame<'tcx>> {
match self.cat {
Categorization::Deref(ref base_cmt, BorrowedPtr(ty::ImmBorrow, _)) => {
// try to figure out where the immutable reference came from
match base_cmt.cat {
Categorization::Local(hir_id) =>
Some(ImmutabilityBlame::LocalDeref(hir_id)),
Categorization::Interior(ref base_cmt, InteriorField(field_index)) => {
base_cmt.resolve_field(field_index.0).map(|(adt_def, field_def)| {
ImmutabilityBlame::AdtFieldDeref(adt_def, field_def)
})
}
Categorization::Upvar(Upvar { id, .. }) => {
if let NoteClosureEnv(..) = self.note {
Some(ImmutabilityBlame::ClosureEnv(id.closure_expr_id))
} else {
None
}
}
_ => None
}
}
Categorization::Local(hir_id) => {
Some(ImmutabilityBlame::ImmLocal(hir_id))
}
Categorization::Rvalue(..) |
Categorization::Upvar(..) |
Categorization::Deref(_, UnsafePtr(..)) => {
// This should not be reachable up to inference limitations.
None
}
Categorization::Interior(ref base_cmt, _) |
Categorization::Downcast(ref base_cmt, _) |
Categorization::Deref(ref base_cmt, _) => {
base_cmt.immutability_blame()
}
Categorization::ThreadLocal(..) |
Categorization::StaticItem => {
// Do we want to do something here?
None
}
}
}
}
pub trait HirNode {
fn hir_id(&self) -> hir::HirId;
fn span(&self) -> Span;

View file

@ -1,11 +1,11 @@
[package]
authors = ["The Rust Project Developers"]
name = "rustc_borrowck"
name = "rustc_ast_borrowck"
version = "0.0.0"
edition = "2018"
[lib]
name = "rustc_borrowck"
name = "rustc_ast_borrowck"
path = "lib.rs"
test = false
doctest = false
@ -18,6 +18,5 @@ syntax_pos = { path = "../libsyntax_pos" }
# refers to the borrowck-specific graphviz adapter traits.
dot = { path = "../libgraphviz", package = "graphviz" }
rustc = { path = "../librustc" }
rustc_mir = { path = "../librustc_mir" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_data_structures = { path = "../librustc_data_structures" }

View file

@ -7,8 +7,6 @@
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
use UseError::*;
use crate::borrowck::*;
use crate::borrowck::InteriorKind::{InteriorElement, InteriorField};
use rustc::middle::expr_use_visitor as euv;
@ -20,7 +18,6 @@ use rustc::ty::{self, TyCtxt, RegionKind};
use syntax_pos::Span;
use rustc::hir;
use rustc::hir::Node;
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use log::debug;
use std::rc::Rc;
@ -89,13 +86,12 @@ struct CheckLoanCtxt<'a, 'tcx> {
impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
fn consume(&mut self,
consume_id: hir::HirId,
consume_span: Span,
_: Span,
cmt: &mc::cmt_<'tcx>,
mode: euv::ConsumeMode) {
debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
consume_id, cmt, mode);
debug!("consume(consume_id={}, cmt={:?})", consume_id, cmt);
self.consume_common(consume_id.local_id, consume_span, cmt, mode);
self.consume_common(consume_id.local_id, cmt, mode);
}
fn matched_pat(&mut self,
@ -107,12 +103,9 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
consume_pat: &hir::Pat,
cmt: &mc::cmt_<'tcx>,
mode: euv::ConsumeMode) {
debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
consume_pat,
cmt,
mode);
debug!("consume_pat(consume_pat={:?}, cmt={:?})", consume_pat, cmt);
self.consume_common(consume_pat.hir_id.local_id, consume_pat.span, cmt, mode);
self.consume_common(consume_pat.hir_id.local_id, cmt, mode);
}
fn borrow(&mut self,
@ -129,11 +122,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
bk, loan_cause);
if let Some(lp) = opt_loan_path(cmt) {
let moved_value_use_kind = match loan_cause {
euv::ClosureCapture(_) => MovedInCapture,
_ => MovedInUse,
};
self.check_if_path_is_moved(borrow_id.local_id, borrow_span, moved_value_use_kind, &lp);
self.check_if_path_is_moved(borrow_id.local_id, &lp);
}
self.check_for_conflicting_loans(borrow_id.local_id);
@ -143,7 +132,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
fn mutate(&mut self,
assignment_id: hir::HirId,
assignment_span: Span,
_: Span,
assignee_cmt: &mc::cmt_<'tcx>,
mode: euv::MutateMode)
{
@ -157,23 +146,18 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
// have to be *FULLY* initialized, but we still
// must be careful lest it contains derefs of
// pointers.
self.check_if_assigned_path_is_moved(assignee_cmt.hir_id.local_id,
assignment_span,
MovedInUse,
&lp);
self.check_if_assigned_path_is_moved(assignee_cmt.hir_id.local_id, &lp);
}
MutateMode::WriteAndRead => {
// In a case like `path += 1`, then path must be
// fully initialized, since we will read it before
// we write it.
self.check_if_path_is_moved(assignee_cmt.hir_id.local_id,
assignment_span,
MovedInUse,
&lp);
}
}
}
self.check_assignment(assignment_id.local_id, assignment_span, assignee_cmt);
self.check_assignment(assignment_id.local_id, assignee_cmt);
}
fn decl_without_init(&mut self, _id: hir::HirId, _span: Span) { }
@ -218,12 +202,6 @@ pub fn check_loans<'a, 'tcx>(
.consume_body(body);
}
#[derive(PartialEq)]
enum UseError<'tcx> {
UseOk,
UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
}
fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
borrow_kind2: ty::BorrowKind)
-> bool {
@ -433,15 +411,9 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
return;
}
if let Some(yield_span) = self.bccx
.region_scope_tree
.yield_in_scope_for_expr(scope,
cmt.hir_id,
self.bccx.body)
if let Some(_) = self.bccx.region_scope_tree
.yield_in_scope_for_expr(scope, cmt.hir_id, self.bccx.body)
{
self.bccx.cannot_borrow_across_generator_yield(borrow_span,
yield_span,
Origin::Ast).emit();
self.bccx.signal_error();
}
}
@ -478,10 +450,11 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
}
}
pub fn report_error_if_loans_conflict(&self,
old_loan: &Loan<'tcx>,
new_loan: &Loan<'tcx>)
-> bool {
pub fn report_error_if_loans_conflict(
&self,
old_loan: &Loan<'tcx>,
new_loan: &Loan<'tcx>,
) -> bool {
//! Checks whether `old_loan` and `new_loan` can safely be issued
//! simultaneously.
@ -493,266 +466,87 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
assert!(self.bccx.region_scope_tree.scopes_intersect(old_loan.kill_scope,
new_loan.kill_scope));
let err_old_new = self.report_error_if_loan_conflicts_with_restriction(
old_loan, new_loan, old_loan, new_loan).err();
let err_new_old = self.report_error_if_loan_conflicts_with_restriction(
new_loan, old_loan, old_loan, new_loan).err();
match (err_old_new, err_new_old) {
(Some(mut err), None) | (None, Some(mut err)) => {
err.emit();
self.bccx.signal_error();
}
(Some(mut err_old), Some(mut err_new)) => {
err_old.emit();
self.bccx.signal_error();
err_new.cancel();
}
(None, None) => return true,
}
false
self.report_error_if_loan_conflicts_with_restriction(
old_loan, new_loan)
&& self.report_error_if_loan_conflicts_with_restriction(
new_loan, old_loan)
}
pub fn report_error_if_loan_conflicts_with_restriction(&self,
loan1: &Loan<'tcx>,
loan2: &Loan<'tcx>,
old_loan: &Loan<'tcx>,
new_loan: &Loan<'tcx>)
-> Result<(), DiagnosticBuilder<'a>> {
pub fn report_error_if_loan_conflicts_with_restriction(
&self,
loan1: &Loan<'tcx>,
loan2: &Loan<'tcx>,
) -> bool {
//! Checks whether the restrictions introduced by `loan1` would
//! prohibit `loan2`. Returns false if an error is reported.
//! prohibit `loan2`.
debug!("report_error_if_loan_conflicts_with_restriction(\
loan1={:?}, loan2={:?})",
loan1,
loan2);
if compatible_borrow_kinds(loan1.kind, loan2.kind) {
return Ok(());
return true;
}
let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
for restr_path in &loan1.restricted_paths {
if *restr_path != loan2_base_path { continue; }
// If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
// normally generate a rather confusing message (in this case, for multiple mutable
// borrows):
//
// error: cannot borrow `x.b` as mutable more than once at a time
// note: previous borrow of `x.a` occurs here; the mutable borrow prevents
// subsequent moves, borrows, or modification of `x.a` until the borrow ends
//
// What we want to do instead is get the 'common ancestor' of the two borrow paths and
// use that for most of the message instead, giving is something like this:
//
// error: cannot borrow `x` as mutable more than once at a time
// note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
// borrow prevents subsequent moves, borrows, or modification of `x` until the
// borrow ends
let common = new_loan.loan_path.common(&old_loan.loan_path);
let (nl, ol, new_loan_msg, old_loan_msg) = {
if new_loan.loan_path.has_fork(&old_loan.loan_path) && common.is_some() {
let nl = self.bccx.loan_path_to_string(&common.unwrap());
let ol = nl.clone();
let new_loan_msg = self.bccx.loan_path_to_string(&new_loan.loan_path);
let old_loan_msg = self.bccx.loan_path_to_string(&old_loan.loan_path);
(nl, ol, new_loan_msg, old_loan_msg)
} else {
(self.bccx.loan_path_to_string(&new_loan.loan_path),
self.bccx.loan_path_to_string(&old_loan.loan_path),
String::new(),
String::new())
}
};
let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
"it".to_string()
} else {
format!("`{}`", ol)
};
// We want to assemble all the relevant locations for the error.
//
// 1. Where did the new loan occur.
// - if due to closure creation, where was the variable used in closure?
// 2. Where did old loan occur.
// 3. Where does old loan expire.
let previous_end_span =
Some(self.tcx().sess.source_map().end_point(
old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree)));
let mut err = match (new_loan.kind, old_loan.kind) {
(ty::MutBorrow, ty::MutBorrow) =>
self.bccx.cannot_mutably_borrow_multiply(
new_loan.span, &nl, &new_loan_msg, old_loan.span, &old_loan_msg,
previous_end_span, Origin::Ast),
(ty::UniqueImmBorrow, ty::UniqueImmBorrow) =>
self.bccx.cannot_uniquely_borrow_by_two_closures(
new_loan.span, &nl, old_loan.span, previous_end_span, Origin::Ast),
(ty::UniqueImmBorrow, _) =>
self.bccx.cannot_uniquely_borrow_by_one_closure(
new_loan.span, "closure", &nl, &new_loan_msg,
old_loan.span, &ol_pronoun, &old_loan_msg, previous_end_span, Origin::Ast),
(_, ty::UniqueImmBorrow) => {
let new_loan_str = &new_loan.kind.to_user_str();
self.bccx.cannot_reborrow_already_uniquely_borrowed(
new_loan.span, "closure", &nl, &new_loan_msg, new_loan_str,
old_loan.span, &old_loan_msg, previous_end_span, "", Origin::Ast)
}
(..) =>
self.bccx.cannot_reborrow_already_borrowed(
new_loan.span,
&nl, &new_loan_msg, &new_loan.kind.to_user_str(),
old_loan.span, &ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg,
previous_end_span, Origin::Ast)
};
match new_loan.cause {
euv::ClosureCapture(span) => {
err.span_label(
span,
format!("borrow occurs due to use of `{}` in closure", nl));
}
_ => { }
}
match old_loan.cause {
euv::ClosureCapture(span) => {
err.span_label(
span,
format!("previous borrow occurs due to use of `{}` in closure",
ol));
}
_ => { }
}
return Err(err);
self.bccx.signal_error();
return false;
}
Ok(())
true
}
fn consume_common(&self,
id: hir::ItemLocalId,
span: Span,
cmt: &mc::cmt_<'tcx>,
mode: euv::ConsumeMode) {
fn consume_common(
&self,
id: hir::ItemLocalId,
cmt: &mc::cmt_<'tcx>,
mode: euv::ConsumeMode,
) {
if let Some(lp) = opt_loan_path(cmt) {
let moved_value_use_kind = match mode {
match mode {
euv::Copy => {
self.check_for_copy_of_frozen_path(id, span, &lp);
MovedInUse
self.check_for_copy_of_frozen_path(id, &lp);
}
euv::Move(_) => {
match self.move_data.kind_of_move_of_path(id, &lp) {
None => {
// Sometimes moves don't have a move kind;
// this either means that the original move
// was from something illegal to move,
// or was moved from referent of an unsafe
// pointer or something like that.
MovedInUse
}
Some(move_kind) => {
self.check_for_move_of_borrowed_path(id, span,
&lp, move_kind);
if move_kind == move_data::Captured {
MovedInCapture
} else {
MovedInUse
}
}
// Sometimes moves aren't from a move path;
// this either means that the original move
// was from something illegal to move,
// or was moved from referent of an unsafe
// pointer or something like that.
if self.move_data.is_move_path(id, &lp) {
self.check_for_move_of_borrowed_path(id, &lp);
}
}
};
self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
}
self.check_if_path_is_moved(id, &lp);
}
}
fn check_for_copy_of_frozen_path(&self,
id: hir::ItemLocalId,
span: Span,
copy_path: &LoanPath<'tcx>) {
match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
UseOk => { }
UseWhileBorrowed(loan_path, loan_span) => {
let desc = self.bccx.loan_path_to_string(copy_path);
self.bccx.cannot_use_when_mutably_borrowed(
span, &desc,
loan_span, &self.bccx.loan_path_to_string(&loan_path),
Origin::Ast)
.emit();
self.bccx.signal_error();
}
}
self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow);
}
fn check_for_move_of_borrowed_path(&self,
id: hir::ItemLocalId,
span: Span,
move_path: &LoanPath<'tcx>,
move_kind: move_data::MoveKind) {
move_path: &LoanPath<'tcx>) {
// We want to detect if there are any loans at all, so we search for
// any loans incompatible with MutBorrrow, since all other kinds of
// loans are incompatible with that.
match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
UseOk => { }
UseWhileBorrowed(loan_path, loan_span) => {
let mut err = match move_kind {
move_data::Captured => {
let mut err = self.bccx.cannot_move_into_closure(
span, &self.bccx.loan_path_to_string(move_path), Origin::Ast);
err.span_label(
loan_span,
format!("borrow of `{}` occurs here",
&self.bccx.loan_path_to_string(&loan_path))
);
err.span_label(
span,
"move into closure occurs here"
);
err
}
move_data::Declared |
move_data::MoveExpr |
move_data::MovePat => {
let desc = self.bccx.loan_path_to_string(move_path);
let mut err = self.bccx.cannot_move_when_borrowed(span, &desc, Origin::Ast);
err.span_label(
loan_span,
format!("borrow of `{}` occurs here",
&self.bccx.loan_path_to_string(&loan_path))
);
err.span_label(
span,
format!("move out of `{}` occurs here",
&self.bccx.loan_path_to_string(move_path))
);
err
}
};
err.emit();
self.bccx.signal_error();
}
}
self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow);
}
pub fn analyze_restrictions_on_use(&self,
fn analyze_restrictions_on_use(&self,
expr_id: hir::ItemLocalId,
use_path: &LoanPath<'tcx>,
borrow_kind: ty::BorrowKind)
-> UseError<'tcx> {
borrow_kind: ty::BorrowKind) {
debug!("analyze_restrictions_on_use(expr_id={:?}, use_path={:?})",
expr_id, use_path);
let mut ret = UseOk;
let scope = region::Scope {
id: expr_id,
data: region::ScopeData::Node
@ -760,38 +554,28 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
self.each_in_scope_loan_affecting_path(
scope, use_path, |loan| {
if !compatible_borrow_kinds(loan.kind, borrow_kind) {
ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
self.bccx.signal_error();
false
} else {
true
}
});
return ret;
}
/// Reports an error if `expr` (which should be a path)
/// is using a moved/uninitialized value
fn check_if_path_is_moved(&self,
id: hir::ItemLocalId,
span: Span,
use_kind: MovedValueUseKind,
lp: &Rc<LoanPath<'tcx>>) {
debug!("check_if_path_is_moved(id={:?}, use_kind={:?}, lp={:?})",
id, use_kind, lp);
debug!("check_if_path_is_moved(id={:?}, lp={:?})", id, lp);
// FIXME: if you find yourself tempted to cut and paste
// the body below and then specializing the error reporting,
// consider refactoring this instead!
let base_lp = owned_ptr_base_path_rc(lp);
self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
self.bccx.report_use_of_moved_value(
span,
use_kind,
&lp,
the_move,
moved_lp);
self.move_data.each_move_of(id, &base_lp, |_, _| {
self.bccx.signal_error();
false
});
}
@ -820,8 +604,6 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
/// ```
fn check_if_assigned_path_is_moved(&self,
id: hir::ItemLocalId,
span: Span,
use_kind: MovedValueUseKind,
lp: &Rc<LoanPath<'tcx>>)
{
match lp.kind {
@ -830,8 +612,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
}
LpDowncast(ref lp_base, _) => {
// assigning to `(P->Variant).f` is ok if assigning to `P` is ok
self.check_if_assigned_path_is_moved(id, span,
use_kind, lp_base);
self.check_if_assigned_path_is_moved(id, lp_base);
}
LpExtend(ref lp_base, _, LpInterior(_, InteriorField(_))) => {
match lp_base.to_type().sty {
@ -845,9 +626,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
let loan_path = owned_ptr_base_path_rc(lp_base);
self.move_data.each_move_of(id, &loan_path, |_, _| {
self.bccx
.report_partial_reinitialization_of_uninitialized_structure(
span,
&loan_path);
.signal_error();
false
});
return;
@ -856,21 +635,19 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
}
// assigning to `P.f` is ok if assigning to `P` is ok
self.check_if_assigned_path_is_moved(id, span,
use_kind, lp_base);
self.check_if_assigned_path_is_moved(id, lp_base);
}
LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) |
LpExtend(ref lp_base, _, LpDeref(_)) => {
// assigning to `P[i]` requires `P` is initialized
// assigning to `(*P)` requires `P` is initialized
self.check_if_path_is_moved(id, span, use_kind, lp_base);
self.check_if_path_is_moved(id, lp_base);
}
}
}
fn check_assignment(&self,
assignment_id: hir::ItemLocalId,
assignment_span: Span,
assignee_cmt: &mc::cmt_<'tcx>) {
debug!("check_assignment(assignee_cmt={:?})", assignee_cmt);
@ -880,8 +657,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
id: assignment_id,
data: region::ScopeData::Node
};
self.each_in_scope_loan_affecting_path(scope, &loan_path, |loan| {
self.report_illegal_mutation(assignment_span, &loan_path, loan);
self.each_in_scope_loan_affecting_path(scope, &loan_path, |_| {
self.bccx.signal_error();
false
});
}
@ -889,30 +666,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
// Check for reassignments to (immutable) local variables. This
// needs to be done here instead of in check_loans because we
// depend on move data.
if let Categorization::Local(hir_id) = assignee_cmt.cat {
if let Categorization::Local(_) = assignee_cmt.cat {
let lp = opt_loan_path(assignee_cmt).unwrap();
self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
if assignee_cmt.mutbl.is_mutable() {
self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
} else {
self.bccx.report_reassigned_immutable_variable(
assignment_span,
&lp,
assign);
self.move_data.each_assignment_of(assignment_id, &lp, |_| {
if !assignee_cmt.mutbl.is_mutable() {
self.bccx.signal_error();
}
false
});
return
}
}
pub fn report_illegal_mutation(&self,
span: Span,
loan_path: &LoanPath<'tcx>,
loan: &Loan<'_>) {
self.bccx.cannot_assign_to_borrowed(
span, loan.span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast)
.emit();
self.bccx.signal_error();
}
}

View file

@ -1,10 +1,7 @@
//! Computes moves.
use crate::borrowck::*;
use crate::borrowck::gather_loans::move_error::MovePlace;
use crate::borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
use crate::borrowck::move_data::*;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
@ -12,56 +9,11 @@ use rustc::ty::{self, Ty};
use std::rc::Rc;
use syntax_pos::Span;
use rustc::hir::*;
use rustc::hir::Node;
use log::debug;
struct GatherMoveInfo<'c, 'tcx> {
id: hir::ItemLocalId,
kind: MoveKind,
cmt: &'c mc::cmt_<'tcx>,
span_path_opt: Option<MovePlace<'tcx>>,
}
/// Represents the kind of pattern
#[derive(Debug, Clone, Copy)]
pub enum PatternSource<'tcx> {
MatchExpr(&'tcx Expr),
LetDecl(&'tcx Local),
Other,
}
/// Analyzes the context where the pattern appears to determine the
/// kind of hint we want to give. In particular, if the pattern is in a `match`
/// or nested within other patterns, we want to suggest a `ref` binding:
///
/// let (a, b) = v[0]; // like the `a` and `b` patterns here
/// match v[0] { a => ... } // or the `a` pattern here
///
/// But if the pattern is the outermost pattern in a `let`, we would rather
/// suggest that the author add a `&` to the initializer:
///
/// let x = v[0]; // suggest `&v[0]` here
///
/// In this latter case, this function will return `PatternSource::LetDecl`
/// with a reference to the let
fn get_pattern_source<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat) -> PatternSource<'tcx> {
let parent = tcx.hir().get_parent_node(pat.hir_id);
match tcx.hir().get(parent) {
Node::Expr(ref e) => {
// the enclosing expression must be a `match` or something else
assert!(match e.node {
ExprKind::Match(..) => true,
_ => return PatternSource::Other,
});
PatternSource::MatchExpr(e)
}
Node::Local(local) => PatternSource::LetDecl(local),
_ => return PatternSource::Other,
}
}
pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
@ -69,82 +21,54 @@ pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
var_id: hir::HirId,
var_ty: Ty<'tcx>) {
let loan_path = Rc::new(LoanPath::new(LpVar(var_id), var_ty));
move_data.add_move(bccx.tcx, loan_path, var_id.local_id, Declared);
move_data.add_move(bccx.tcx, loan_path, var_id.local_id);
}
pub fn gather_move_from_expr<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
move_error_collector: &mut MoveErrorCollector<'tcx>,
move_expr_id: hir::ItemLocalId,
cmt: &mc::cmt_<'tcx>,
move_reason: euv::MoveReason) {
let kind = match move_reason {
euv::DirectRefMove | euv::PatBindingMove => MoveExpr,
euv::CaptureMove => Captured
};
cmt: &mc::cmt_<'tcx>) {
let move_info = GatherMoveInfo {
id: move_expr_id,
kind,
cmt,
span_path_opt: None,
};
gather_move(bccx, move_data, move_error_collector, move_info);
gather_move(bccx, move_data, move_info);
}
pub fn gather_move_from_pat<'a, 'c, 'tcx>(
bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
move_error_collector: &mut MoveErrorCollector<'tcx>,
move_pat: &hir::Pat,
cmt: &'c mc::cmt_<'tcx>,
) {
let source = get_pattern_source(bccx.tcx,move_pat);
let pat_span_path_opt = match move_pat.node {
PatKind::Binding(_, _, ident, _) => {
Some(MovePlace {
span: move_pat.span,
name: ident.name,
pat_source: source,
})
}
_ => None,
};
let move_info = GatherMoveInfo {
id: move_pat.hir_id.local_id,
kind: MovePat,
cmt,
span_path_opt: pat_span_path_opt,
};
debug!("gather_move_from_pat: move_pat={:?} source={:?}",
move_pat,
source);
debug!("gather_move_from_pat: move_pat={:?}", move_pat);
gather_move(bccx, move_data, move_error_collector, move_info);
gather_move(bccx, move_data, move_info);
}
fn gather_move<'a, 'c, 'tcx>(
bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
move_error_collector: &mut MoveErrorCollector<'tcx>,
move_info: GatherMoveInfo<'c, 'tcx>,
) {
debug!("gather_move(move_id={:?}, cmt={:?})",
move_info.id, move_info.cmt);
let potentially_illegal_move = check_and_get_illegal_move_origin(bccx, move_info.cmt);
if let Some(illegal_move_origin) = potentially_illegal_move {
debug!("illegal_move_origin={:?}", illegal_move_origin);
let error = MoveError::with_move_info(Rc::new(illegal_move_origin),
move_info.span_path_opt);
move_error_collector.add_error(error);
if let Some(_) = potentially_illegal_move {
bccx.signal_error();
return;
}
match opt_loan_path(&move_info.cmt) {
Some(loan_path) => {
move_data.add_move(bccx.tcx, loan_path,
move_info.id, move_info.kind);
move_info.id);
}
None => {
// move from rvalue or raw pointer, hence ok

View file

@ -3,21 +3,17 @@
use crate::borrowck::*;
use rustc::hir::HirId;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::region;
use rustc::ty;
use syntax_pos::Span;
use log::debug;
type R = Result<(),()>;
pub fn guarantee_lifetime<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
item_scope: region::Scope,
span: Span,
cause: euv::LoanCause,
cmt: &'a mc::cmt_<'tcx>,
loan_region: ty::Region<'tcx>)
-> Result<(),()> {
@ -26,12 +22,7 @@ pub fn guarantee_lifetime<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
//! and is scope of `cmt` otherwise.
debug!("guarantee_lifetime(cmt={:?}, loan_region={:?})",
cmt, loan_region);
let ctxt = GuaranteeLifetimeContext {bccx: bccx,
item_scope,
span,
cause,
loan_region,
cmt_original: cmt};
let ctxt = GuaranteeLifetimeContext { bccx, item_scope, loan_region };
ctxt.check(cmt, None)
}
@ -44,10 +35,7 @@ struct GuaranteeLifetimeContext<'a, 'tcx> {
// the scope of the function body for the enclosing item
item_scope: region::Scope,
span: Span,
cause: euv::LoanCause,
loan_region: ty::Region<'tcx>,
cmt_original: &'a mc::cmt_<'tcx>
}
impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
@ -85,7 +73,7 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
//! Reports an error if `loan_region` is larger than `max_scope`
if !self.bccx.is_subregion_of(self.loan_region, max_scope) {
Err(self.report_error(err_out_of_scope(max_scope, self.loan_region, self.cause)))
Err(self.bccx.signal_error())
} else {
Ok(())
}
@ -122,11 +110,4 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
}
}
}
fn report_error(&self, code: bckerr_code<'tcx>) {
self.bccx.report(BckError { cmt: self.cmt_original,
span: self.span,
cause: BorrowViolation(self.cause),
code: code });
}
}

View file

@ -23,7 +23,6 @@ use restrictions::RestrictionResult;
mod lifetime;
mod restrictions;
mod gather_moves;
mod move_error;
pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
body: hir::BodyId)
@ -38,7 +37,6 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
data: region::ScopeData::Node
},
move_data: MoveData::default(),
move_error_collector: move_error::MoveErrorCollector::new(),
};
let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
@ -51,7 +49,6 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
Some(rvalue_promotable_map))
.consume_body(bccx.body);
glcx.report_potential_errors();
let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
(all_loans, move_data)
}
@ -59,7 +56,6 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
struct GatherLoanCtxt<'a, 'tcx> {
bccx: &'a BorrowckCtxt<'a, 'tcx>,
move_data: move_data::MoveData<'tcx>,
move_error_collector: move_error::MoveErrorCollector<'tcx>,
all_loans: Vec<Loan<'tcx>>,
/// `item_ub` is used as an upper-bound on the lifetime whenever we
/// ask for the scope of an expression categorized as an upvar.
@ -76,10 +72,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
consume_id, cmt, mode);
match mode {
euv::Move(move_reason) => {
euv::Move(_) => {
gather_moves::gather_move_from_expr(
self.bccx, &self.move_data, &mut self.move_error_collector,
consume_id.local_id, cmt, move_reason);
self.bccx, &self.move_data,
consume_id.local_id, cmt);
}
euv::Copy => { }
}
@ -110,13 +106,13 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
}
gather_moves::gather_move_from_pat(
self.bccx, &self.move_data, &mut self.move_error_collector,
self.bccx, &self.move_data,
consume_pat, cmt);
}
fn borrow(&mut self,
borrow_id: hir::HirId,
borrow_span: Span,
_: Span,
cmt: &mc::cmt_<'tcx>,
loan_region: ty::Region<'tcx>,
bk: ty::BorrowKind,
@ -128,11 +124,9 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
bk, loan_cause);
self.guarantee_valid(borrow_id.local_id,
borrow_span,
cmt,
bk,
loan_region,
loan_cause);
loan_region);
}
fn mutate(&mut self,
@ -174,8 +168,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
/// Implements the A-* rules in README.md.
fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
borrow_span: Span,
loan_cause: AliasableViolationKind,
cmt: &mc::cmt_<'tcx>,
req_kind: ty::BorrowKind)
-> Result<(),()> {
@ -198,13 +190,9 @@ fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
// user knows what they're doing in these cases.
Ok(())
}
(mc::Aliasability::FreelyAliasable(alias_cause), ty::UniqueImmBorrow) |
(mc::Aliasability::FreelyAliasable(alias_cause), ty::MutBorrow) => {
bccx.report_aliasability_violation(
borrow_span,
loan_cause,
alias_cause,
cmt);
(mc::Aliasability::FreelyAliasable(_), ty::UniqueImmBorrow) |
(mc::Aliasability::FreelyAliasable(_), ty::MutBorrow) => {
bccx.signal_error();
Err(())
}
(..) => {
@ -215,13 +203,10 @@ fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
/// Implements the M-* rules in README.md.
fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
borrow_span: Span,
cause: AliasableViolationKind,
cmt: &mc::cmt_<'tcx>,
req_kind: ty::BorrowKind)
-> Result<(),()> {
debug!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
cause, cmt, req_kind);
debug!("check_mutability(cmt={:?} req_kind={:?}", cmt, req_kind);
match req_kind {
ty::UniqueImmBorrow | ty::ImmBorrow => {
match cmt.mutbl {
@ -239,10 +224,7 @@ fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
ty::MutBorrow => {
// Only mutable data can be lent as mutable.
if !cmt.mutbl.is_mutable() {
Err(bccx.report(BckError { span: borrow_span,
cause,
cmt,
code: err_mutbl }))
Err(bccx.signal_error())
} else {
Ok(())
}
@ -268,26 +250,18 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
// mutable - this is checked in check_loans.
} else {
// Check that we don't allow assignments to non-mutable data.
if check_mutability(self.bccx, assignment_span, MutabilityViolation,
cmt, ty::MutBorrow).is_err() {
if check_mutability(self.bccx, cmt, ty::MutBorrow).is_err() {
return; // reported an error, no sense in reporting more.
}
}
// Check that we don't allow assignments to aliasable data
if check_aliasability(self.bccx, assignment_span, MutabilityViolation,
cmt, ty::MutBorrow).is_err() {
if check_aliasability(self.bccx, cmt, ty::MutBorrow).is_err() {
return; // reported an error, no sense in reporting more.
}
match opt_lp {
Some(lp) => {
if let Categorization::Local(..) = cmt.cat {
// Only re-assignments to locals require it to be
// mutable - this is checked in check_loans.
} else {
self.mark_loan_path_as_mutated(&lp);
}
gather_moves::gather_assignment(self.bccx, &self.move_data,
assignment_id.local_id,
assignment_span,
@ -306,11 +280,9 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
/// `req_loan_map`.
fn guarantee_valid(&mut self,
borrow_id: hir::ItemLocalId,
borrow_span: Span,
cmt: &mc::cmt_<'tcx>,
req_kind: ty::BorrowKind,
loan_region: ty::Region<'tcx>,
cause: euv::LoanCause) {
loan_region: ty::Region<'tcx>) {
debug!("guarantee_valid(borrow_id={:?}, cmt={:?}, \
req_mutbl={:?}, loan_region={:?})",
borrow_id,
@ -326,27 +298,23 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
// Check that the lifetime of the borrow does not exceed
// the lifetime of the data being borrowed.
if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
borrow_span, cause, cmt, loan_region).is_err() {
if lifetime::guarantee_lifetime(self.bccx, self.item_ub, cmt, loan_region).is_err() {
return; // reported an error, no sense in reporting more.
}
// Check that we don't allow mutable borrows of non-mutable data.
if check_mutability(self.bccx, borrow_span, BorrowViolation(cause),
cmt, req_kind).is_err() {
if check_mutability(self.bccx, cmt, req_kind).is_err() {
return; // reported an error, no sense in reporting more.
}
// Check that we don't allow mutable borrows of aliasable data.
if check_aliasability(self.bccx, borrow_span, BorrowViolation(cause),
cmt, req_kind).is_err() {
if check_aliasability(self.bccx, cmt, req_kind).is_err() {
return; // reported an error, no sense in reporting more.
}
// Compute the restrictions that are required to enforce the
// loan is safe.
let restr = restrictions::compute_restrictions(
self.bccx, borrow_span, cause, &cmt, loan_region);
let restr = restrictions::compute_restrictions(self.bccx, &cmt, loan_region);
debug!("guarantee_valid(): restrictions={:?}", restr);
@ -395,19 +363,13 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
let kill_scope = self.compute_kill_scope(loan_scope, &loan_path);
debug!("kill_scope = {:?}", kill_scope);
if req_kind == ty::MutBorrow {
self.mark_loan_path_as_mutated(&loan_path);
}
Loan {
index: self.all_loans.len(),
loan_path,
kind: req_kind,
gen_scope,
kill_scope,
span: borrow_span,
restricted_paths,
cause,
}
}
};
@ -419,70 +381,6 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
// let loan_gen_scope = loan.gen_scope;
// let loan_kill_scope = loan.kill_scope;
self.all_loans.push(loan);
// if loan_gen_scope != borrow_id {
// FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls
//
// Typically, the scope of the loan includes the point at
// which the loan is originated. This
// This is a subtle case. See the test case
// <compile-fail/borrowck-bad-nested-calls-free.rs>
// to see what we are guarding against.
//let restr = restrictions::compute_restrictions(
// self.bccx, borrow_span, cmt, RESTR_EMPTY);
//let loan = {
// Loan {
// index: self.all_loans.len(),
// loan_path,
// cmt,
// mutbl: ConstMutability,
// gen_scope: borrow_id,
// kill_scope,
// span: borrow_span,
// restrictions,
// }
// }
}
pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath<'_>) {
//! For mutable loans of content whose mutability derives
//! from a local variable, mark the mutability decl as necessary.
let mut wrapped_path = Some(loan_path);
let mut through_borrow = false;
while let Some(current_path) = wrapped_path {
wrapped_path = match current_path.kind {
LpVar(hir_id) => {
if !through_borrow {
self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
}
None
}
LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath { hir_id }, closure_expr_id: _ }) => {
self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
None
}
LpExtend(ref base, mc::McInherited, LpDeref(pointer_kind)) |
LpExtend(ref base, mc::McDeclared, LpDeref(pointer_kind)) => {
if pointer_kind != mc::Unique {
through_borrow = true;
}
Some(base)
}
LpDowncast(ref base, _) |
LpExtend(ref base, mc::McInherited, _) |
LpExtend(ref base, mc::McDeclared, _) => {
Some(base)
}
LpExtend(_, mc::McImmutable, _) => {
// Nothing to do.
None
}
}
}
}
pub fn compute_gen_scope(&self,
@ -532,8 +430,4 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
loan_scope
}
}
pub fn report_potential_errors(&self) {
self.move_error_collector.report_potential_errors(self.bccx);
}
}

View file

@ -1,11 +1,9 @@
//! Computes the restrictions that result from a borrow.
use crate::borrowck::*;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::ty;
use syntax_pos::Span;
use log::debug;
use crate::borrowck::ToInteriorKind;
@ -19,17 +17,10 @@ pub enum RestrictionResult<'tcx> {
}
pub fn compute_restrictions<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
span: Span,
cause: euv::LoanCause,
cmt: &mc::cmt_<'tcx>,
loan_region: ty::Region<'tcx>)
-> RestrictionResult<'tcx> {
let ctxt = RestrictionsContext {
bccx,
span,
cause,
loan_region,
};
let ctxt = RestrictionsContext { bccx, loan_region };
ctxt.restrict(cmt)
}
@ -39,9 +30,7 @@ pub fn compute_restrictions<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
struct RestrictionsContext<'a, 'tcx> {
bccx: &'a BorrowckCtxt<'a, 'tcx>,
span: Span,
loan_region: ty::Region<'tcx>,
cause: euv::LoanCause,
}
impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
@ -149,13 +138,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
mc::BorrowedPtr(bk, lt) => {
// R-Deref-[Mut-]Borrowed
if !self.bccx.is_subregion_of(self.loan_region, lt) {
self.bccx.report(
BckError {
span: self.span,
cause: BorrowViolation(self.cause),
cmt: &cmt_base,
code: err_borrowed_pointer_too_short(
self.loan_region, lt)});
self.bccx.signal_error();
return RestrictionResult::Safe;
}

View file

@ -0,0 +1,614 @@
//! See The Book chapter on the borrow checker for more details.
#![allow(non_camel_case_types)]
pub use LoanPathKind::*;
pub use LoanPathElem::*;
use InteriorKind::*;
use rustc::hir::HirId;
use rustc::hir::Node;
use rustc::cfg;
use rustc::middle::borrowck::{BorrowCheckResult, SignalledError};
use rustc::hir::def_id::{DefId, LocalDefId};
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::region;
use rustc::middle::free_region::RegionRelations;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::query::Providers;
use std::borrow::Cow;
use std::cell::{Cell};
use std::fmt;
use std::rc::Rc;
use std::hash::{Hash, Hasher};
use log::debug;
use rustc::hir;
use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom};
pub mod check_loans;
pub mod gather_loans;
pub mod move_data;
#[derive(Clone, Copy)]
pub struct LoanDataFlowOperator;
pub type LoanDataFlow<'tcx> = DataFlowContext<'tcx, LoanDataFlowOperator>;
pub fn check_crate(tcx: TyCtxt<'_>) {
tcx.par_body_owners(|body_owner_def_id| {
tcx.ensure().borrowck(body_owner_def_id);
});
}
pub fn provide(providers: &mut Providers<'_>) {
*providers = Providers {
borrowck,
..*providers
};
}
/// Collection of conclusions determined via borrow checker analyses.
pub struct AnalysisData<'tcx> {
pub all_loans: Vec<Loan<'tcx>>,
pub loans: DataFlowContext<'tcx, LoanDataFlowOperator>,
pub move_data: move_data::FlowedMoveData<'tcx>,
}
fn borrowck(tcx: TyCtxt<'_>, owner_def_id: DefId) -> &BorrowCheckResult {
assert!(tcx.use_ast_borrowck() || tcx.migrate_borrowck());
debug!("borrowck(body_owner_def_id={:?})", owner_def_id);
let owner_id = tcx.hir().as_local_hir_id(owner_def_id).unwrap();
match tcx.hir().get(owner_id) {
Node::Ctor(..) => {
// We get invoked with anything that has MIR, but some of
// those things (notably the synthesized constructors from
// tuple structs/variants) do not have an associated body
// and do not need borrowchecking.
return tcx.arena.alloc(BorrowCheckResult {
signalled_any_error: SignalledError::NoErrorsSeen,
})
}
_ => { }
}
let body_id = tcx.hir().body_owned_by(owner_id);
let tables = tcx.typeck_tables_of(owner_def_id);
let region_scope_tree = tcx.region_scope_tree(owner_def_id);
let body = tcx.hir().body(body_id);
let mut bccx = BorrowckCtxt {
tcx,
tables,
region_scope_tree,
owner_def_id,
body,
signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
};
// Eventually, borrowck will always read the MIR, but at the
// moment we do not. So, for now, we always force MIR to be
// constructed for a given fn, since this may result in errors
// being reported and we want that to happen.
//
// Note that `mir_validated` is a "stealable" result; the
// thief, `optimized_mir()`, forces borrowck, so we know that
// is not yet stolen.
tcx.ensure().mir_validated(owner_def_id);
// option dance because you can't capture an uninitialized variable
// by mut-ref.
let mut cfg = None;
if let Some(AnalysisData { all_loans,
loans: loan_dfcx,
move_data: flowed_moves }) =
build_borrowck_dataflow_data(&mut bccx, false, body_id,
|bccx| {
cfg = Some(cfg::CFG::new(bccx.tcx, &body));
cfg.as_mut().unwrap()
})
{
check_loans::check_loans(&mut bccx, &loan_dfcx, &flowed_moves, &all_loans, body);
}
tcx.arena.alloc(BorrowCheckResult {
signalled_any_error: bccx.signalled_any_error.into_inner(),
})
}
fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tcx>,
force_analysis: bool,
body_id: hir::BodyId,
get_cfg: F)
-> Option<AnalysisData<'tcx>>
where F: FnOnce(&mut BorrowckCtxt<'a, 'tcx>) -> &'c cfg::CFG
{
// Check the body of fn items.
let (all_loans, move_data) =
gather_loans::gather_loans_in_fn(this, body_id);
if !force_analysis && move_data.is_empty() && all_loans.is_empty() {
// large arrays of data inserted as constants can take a lot of
// time and memory to borrow-check - see issue #36799. However,
// they don't have places, so no borrow-check is actually needed.
// Recognize that case and skip borrow-checking.
debug!("skipping loan propagation for {:?} because of no loans", body_id);
return None;
} else {
debug!("propagating loans in {:?}", body_id);
}
let cfg = get_cfg(this);
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
"borrowck",
Some(this.body),
cfg,
LoanDataFlowOperator,
all_loans.len());
for (loan_idx, loan) in all_loans.iter().enumerate() {
loan_dfcx.add_gen(loan.gen_scope.item_local_id(), loan_idx);
loan_dfcx.add_kill(KillFrom::ScopeEnd,
loan.kill_scope.item_local_id(),
loan_idx);
}
loan_dfcx.add_kills_from_flow_exits(cfg);
loan_dfcx.propagate(cfg, this.body);
let flowed_moves = move_data::FlowedMoveData::new(move_data,
this,
cfg,
this.body);
Some(AnalysisData { all_loans,
loans: loan_dfcx,
move_data:flowed_moves })
}
/// Accessor for introspective clients inspecting `AnalysisData` and
/// the `BorrowckCtxt` itself , e.g., the flowgraph visualizer.
pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
tcx: TyCtxt<'tcx>,
body_id: hir::BodyId,
cfg: &cfg::CFG)
-> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'tcx>)
{
let owner_id = tcx.hir().body_owner(body_id);
let owner_def_id = tcx.hir().local_def_id(owner_id);
let tables = tcx.typeck_tables_of(owner_def_id);
let region_scope_tree = tcx.region_scope_tree(owner_def_id);
let body = tcx.hir().body(body_id);
let mut bccx = BorrowckCtxt {
tcx,
tables,
region_scope_tree,
owner_def_id,
body,
signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
};
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
(bccx, dataflow_data.unwrap())
}
// ----------------------------------------------------------------------
// Type definitions
pub struct BorrowckCtxt<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
// tables for the current thing we are checking; set to
// Some in `borrowck_fn` and cleared later
tables: &'a ty::TypeckTables<'tcx>,
region_scope_tree: &'tcx region::ScopeTree,
owner_def_id: DefId,
body: &'tcx hir::Body,
signalled_any_error: Cell<SignalledError>,
}
impl<'a, 'tcx: 'a> BorrowckCtxt<'a, 'tcx> {
fn signal_error(&self) {
self.signalled_any_error.set(SignalledError::SawSomeError);
}
}
///////////////////////////////////////////////////////////////////////////
// Loans and loan paths
/// Record of a loan that was issued.
pub struct Loan<'tcx> {
index: usize,
loan_path: Rc<LoanPath<'tcx>>,
kind: ty::BorrowKind,
restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
/// gen_scope indicates where loan is introduced. Typically the
/// loan is introduced at the point of the borrow, but in some
/// cases, notably method arguments, the loan may be introduced
/// only later, once it comes into scope. See also
/// `GatherLoanCtxt::compute_gen_scope`.
gen_scope: region::Scope,
/// kill_scope indicates when the loan goes out of scope. This is
/// either when the lifetime expires or when the local variable
/// which roots the loan-path goes out of scope, whichever happens
/// faster. See also `GatherLoanCtxt::compute_kill_scope`.
kill_scope: region::Scope,
}
impl<'tcx> Loan<'tcx> {
pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
self.loan_path.clone()
}
}
#[derive(Eq)]
pub struct LoanPath<'tcx> {
kind: LoanPathKind<'tcx>,
ty: Ty<'tcx>,
}
impl<'tcx> PartialEq for LoanPath<'tcx> {
fn eq(&self, that: &LoanPath<'tcx>) -> bool {
self.kind == that.kind
}
}
impl<'tcx> Hash for LoanPath<'tcx> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.kind.hash(state);
}
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub enum LoanPathKind<'tcx> {
LpVar(hir::HirId), // `x` in README.md
LpUpvar(ty::UpvarId), // `x` captured by-value into closure
LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant
LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem<'tcx>)
}
impl<'tcx> LoanPath<'tcx> {
fn new(kind: LoanPathKind<'tcx>, ty: Ty<'tcx>) -> LoanPath<'tcx> {
LoanPath { kind: kind, ty: ty }
}
fn to_type(&self) -> Ty<'tcx> { self.ty }
}
// FIXME (pnkfelix): See discussion here
// https://github.com/pnkfelix/rust/commit/
// b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
const DOWNCAST_PRINTED_OPERATOR: &'static str = " as ";
// A local, "cleaned" version of `mc::InteriorKind` that drops
// information that is not relevant to loan-path analysis. (In
// particular, the distinction between how precisely an array-element
// is tracked is irrelevant here.)
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum InteriorKind {
InteriorField(mc::FieldIndex),
InteriorElement,
}
trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
impl ToInteriorKind for mc::InteriorKind {
fn cleaned(self) -> InteriorKind {
match self {
mc::InteriorField(name) => InteriorField(name),
mc::InteriorElement(_) => InteriorElement,
}
}
}
// This can be:
// - a pointer dereference (`*P` in README.md)
// - a field reference, with an optional definition of the containing
// enum variant (`P.f` in README.md)
// `DefId` is present when the field is part of struct that is in
// a variant of an enum. For instance in:
// `enum E { X { foo: u32 }, Y { foo: u32 }}`
// each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum LoanPathElem<'tcx> {
LpDeref(mc::PointerKind<'tcx>),
LpInterior(Option<DefId>, InteriorKind),
}
fn closure_to_block(closure_id: LocalDefId, tcx: TyCtxt<'_>) -> HirId {
let closure_id = tcx.hir().local_def_id_to_hir_id(closure_id);
match tcx.hir().get(closure_id) {
Node::Expr(expr) => match expr.node {
hir::ExprKind::Closure(.., body_id, _, _) => {
body_id.hir_id
}
_ => {
bug!("encountered non-closure id: {}", closure_id)
}
},
_ => bug!("encountered non-expr id: {}", closure_id)
}
}
impl<'a, 'tcx> LoanPath<'tcx> {
pub fn kill_scope(&self, bccx: &BorrowckCtxt<'a, 'tcx>) -> region::Scope {
match self.kind {
LpVar(hir_id) => {
bccx.region_scope_tree.var_scope(hir_id.local_id)
}
LpUpvar(upvar_id) => {
let block_id = closure_to_block(upvar_id.closure_expr_id, bccx.tcx);
region::Scope { id: block_id.local_id, data: region::ScopeData::Node }
}
LpDowncast(ref base, _) |
LpExtend(ref base, ..) => base.kill_scope(bccx),
}
}
}
// Avoid "cannot borrow immutable field `self.x` as mutable" as that implies that a field *can* be
// mutable independently of the struct it belongs to. (#35937)
pub fn opt_loan_path_is_field<'tcx>(cmt: &mc::cmt_<'tcx>) -> (Option<Rc<LoanPath<'tcx>>>, bool) {
let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
match cmt.cat {
Categorization::Rvalue(..) |
Categorization::ThreadLocal(..) |
Categorization::StaticItem => {
(None, false)
}
Categorization::Local(id) => {
(Some(new_lp(LpVar(id))), false)
}
Categorization::Upvar(mc::Upvar { id, .. }) => {
(Some(new_lp(LpUpvar(id))), false)
}
Categorization::Deref(ref cmt_base, pk) => {
let lp = opt_loan_path_is_field(cmt_base);
(lp.0.map(|lp| {
new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
}), lp.1)
}
Categorization::Interior(ref cmt_base, ik) => {
(opt_loan_path(cmt_base).map(|lp| {
let opt_variant_id = match cmt_base.cat {
Categorization::Downcast(_, did) => Some(did),
_ => None
};
new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned())))
}), true)
}
Categorization::Downcast(ref cmt_base, variant_def_id) => {
let lp = opt_loan_path_is_field(cmt_base);
(lp.0.map(|lp| {
new_lp(LpDowncast(lp, variant_def_id))
}), lp.1)
}
}
}
/// Computes the `LoanPath` (if any) for a `cmt`.
/// Note that this logic is somewhat duplicated in
/// the method `compute()` found in `gather_loans::restrictions`,
/// which allows it to share common loan path pieces as it
/// traverses the CMT.
pub fn opt_loan_path<'tcx>(cmt: &mc::cmt_<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
opt_loan_path_is_field(cmt).0
}
///////////////////////////////////////////////////////////////////////////
// Misc
impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
pub fn is_subregion_of(&self,
r_sub: ty::Region<'tcx>,
r_sup: ty::Region<'tcx>)
-> bool
{
let region_rels = RegionRelations::new(self.tcx,
self.owner_def_id,
&self.region_scope_tree,
&self.tables.free_region_map);
region_rels.is_subregion_of(r_sub, r_sup)
}
pub fn append_loan_path_to_string(&self,
loan_path: &LoanPath<'tcx>,
out: &mut String) {
match loan_path.kind {
LpUpvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id: id }, closure_expr_id: _ }) => {
out.push_str(&self.tcx.hir().name(id).as_str());
}
LpVar(id) => {
out.push_str(&self.tcx.hir().name(id).as_str());
}
LpDowncast(ref lp_base, variant_def_id) => {
out.push('(');
self.append_loan_path_to_string(&lp_base, out);
out.push_str(DOWNCAST_PRINTED_OPERATOR);
out.push_str(&self.tcx.def_path_str(variant_def_id));
out.push(')');
}
LpExtend(ref lp_base, _, LpInterior(_, InteriorField(mc::FieldIndex(_, info)))) => {
self.append_autoderefd_loan_path_to_string(&lp_base, out);
out.push('.');
out.push_str(&info.as_str());
}
LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) => {
self.append_autoderefd_loan_path_to_string(&lp_base, out);
out.push_str("[..]");
}
LpExtend(ref lp_base, _, LpDeref(_)) => {
out.push('*');
self.append_loan_path_to_string(&lp_base, out);
}
}
}
pub fn append_autoderefd_loan_path_to_string(&self,
loan_path: &LoanPath<'tcx>,
out: &mut String) {
match loan_path.kind {
LpExtend(ref lp_base, _, LpDeref(_)) => {
// For a path like `(*x).f` or `(*x)[3]`, autoderef
// rules would normally allow users to omit the `*x`.
// So just serialize such paths to `x.f` or x[3]` respectively.
self.append_autoderefd_loan_path_to_string(&lp_base, out)
}
LpDowncast(ref lp_base, variant_def_id) => {
out.push('(');
self.append_autoderefd_loan_path_to_string(&lp_base, out);
out.push_str(DOWNCAST_PRINTED_OPERATOR);
out.push_str(&self.tcx.def_path_str(variant_def_id));
out.push(')');
}
LpVar(..) | LpUpvar(..) | LpExtend(.., LpInterior(..)) => {
self.append_loan_path_to_string(loan_path, out)
}
}
}
pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
let mut result = String::new();
self.append_loan_path_to_string(loan_path, &mut result);
result
}
pub fn cmt_to_cow_str(&self, cmt: &mc::cmt_<'tcx>) -> Cow<'static, str> {
cmt.descriptive_string(self.tcx)
}
pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
match opt_loan_path(cmt) {
Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)),
None => self.cmt_to_cow_str(cmt).into_owned(),
}
}
}
impl BitwiseOperator for LoanDataFlowOperator {
#[inline]
fn join(&self, succ: usize, pred: usize) -> usize {
succ | pred // loans from both preds are in scope
}
}
impl DataFlowOperator for LoanDataFlowOperator {
#[inline]
fn initial_value(&self) -> bool {
false // no loans in scope by default
}
}
impl fmt::Debug for InteriorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
InteriorField(mc::FieldIndex(_, info)) => write!(f, "{}", info),
InteriorElement => write!(f, "[]"),
}
}
}
impl<'tcx> fmt::Debug for Loan<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
self.index,
self.loan_path,
self.kind,
self.gen_scope,
self.kill_scope,
self.restricted_paths)
}
}
impl<'tcx> fmt::Debug for LoanPath<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
LpVar(id) => {
write!(f, "$({})", ty::tls::with(|tcx| tcx.hir().node_to_string(id)))
}
LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath {hir_id: var_id}, closure_expr_id }) => {
let s = ty::tls::with(|tcx| {
tcx.hir().node_to_string(var_id)
});
write!(f, "$({} captured by id={:?})", s, closure_expr_id)
}
LpDowncast(ref lp, variant_def_id) => {
let variant_str = if variant_def_id.is_local() {
ty::tls::with(|tcx| tcx.def_path_str(variant_def_id))
} else {
format!("{:?}", variant_def_id)
};
write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
}
LpExtend(ref lp, _, LpDeref(_)) => {
write!(f, "{:?}.*", lp)
}
LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
write!(f, "{:?}.{:?}", lp, interior)
}
}
}
}
impl<'tcx> fmt::Display for LoanPath<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
LpVar(id) => {
write!(f, "$({})", ty::tls::with(|tcx| tcx.hir().hir_to_user_string(id)))
}
LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath { hir_id }, closure_expr_id: _ }) => {
let s = ty::tls::with(|tcx| {
tcx.hir().node_to_string(hir_id)
});
write!(f, "$({} captured by closure)", s)
}
LpDowncast(ref lp, variant_def_id) => {
let variant_str = if variant_def_id.is_local() {
ty::tls::with(|tcx| tcx.def_path_str(variant_def_id))
} else {
format!("{:?}", variant_def_id)
};
write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
}
LpExtend(ref lp, _, LpDeref(_)) => {
write!(f, "{}.*", lp)
}
LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
write!(f, "{}.{:?}", lp, interior)
}
}
}
}

View file

@ -1,8 +1,6 @@
//! Data structures used for tracking moves. Please see the extensive
//! comments in the section "Moves and initialization" in `README.md`.
pub use MoveKind::*;
use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom};
use crate::borrowck::*;
@ -101,13 +99,6 @@ pub struct MovePath<'tcx> {
pub next_sibling: MovePathIndex,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum MoveKind {
Declared, // When declared, variables start out "moved".
MoveExpr, // Expression or binding that moves a variable
MovePat, // By-move binding
Captured // Closure creation that moves a value
}
#[derive(Copy, Clone)]
pub struct Move {
@ -117,9 +108,6 @@ pub struct Move {
/// ID of node that is doing the move.
pub id: hir::ItemLocalId,
/// Kind of move, for error messages.
pub kind: MoveKind,
/// Next node in linked list of moves from `path`, or `InvalidMoveIndex`
pub next_move: MoveIndex
}
@ -315,7 +303,6 @@ impl MoveData<'tcx> {
tcx: TyCtxt<'tcx>,
orig_lp: Rc<LoanPath<'tcx>>,
id: hir::ItemLocalId,
kind: MoveKind,
) {
// Moving one union field automatically moves all its fields. Also move siblings of
// all parent union fields, moves do not propagate upwards automatically.
@ -331,7 +318,7 @@ impl MoveData<'tcx> {
let sibling_lp_kind =
LpExtend(base_lp.clone(), mutbl, LpInterior(opt_variant_id, field));
let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, tcx.types.err));
self.add_move_helper(tcx, sibling_lp, id, kind);
self.add_move_helper(tcx, sibling_lp, id);
}
}
}
@ -339,7 +326,7 @@ impl MoveData<'tcx> {
lp = base_lp.clone();
}
self.add_move_helper(tcx, orig_lp, id, kind);
self.add_move_helper(tcx, orig_lp, id);
}
fn add_move_helper(
@ -347,12 +334,8 @@ impl MoveData<'tcx> {
tcx: TyCtxt<'tcx>,
lp: Rc<LoanPath<'tcx>>,
id: hir::ItemLocalId,
kind: MoveKind,
) {
debug!("add_move(lp={:?}, id={:?}, kind={:?})",
lp,
id,
kind);
debug!("add_move(lp={:?}, id={:?})", lp, id);
let path_index = self.move_path(tcx, lp);
let move_index = MoveIndex(self.moves.borrow().len());
@ -363,7 +346,6 @@ impl MoveData<'tcx> {
self.moves.borrow_mut().push(Move {
path: path_index,
id,
kind,
next_move,
});
}
@ -611,19 +593,16 @@ impl<'tcx> FlowedMoveData<'tcx> {
}
}
pub fn kind_of_move_of_path(&self,
id: hir::ItemLocalId,
loan_path: &Rc<LoanPath<'tcx>>)
-> Option<MoveKind> {
pub fn is_move_path(&self, id: hir::ItemLocalId, loan_path: &Rc<LoanPath<'tcx>>) -> bool {
//! Returns the kind of a move of `loan_path` by `id`, if one exists.
let mut ret = None;
let mut ret = false;
if let Some(loan_path_index) = self.move_data.path_map.borrow().get(&*loan_path) {
self.dfcx_moves.each_gen_bit(id, |move_index| {
let the_move = self.move_data.moves.borrow();
let the_move = (*the_move)[move_index];
if the_move.path == *loan_path_index {
ret = Some(the_move.kind);
ret = true;
false
} else {
true

View file

@ -1,186 +0,0 @@
use crate::borrowck::BorrowckCtxt;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::mem_categorization::NoteClosureEnv;
use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
use rustc::ty;
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use syntax::ast;
use syntax_pos;
use errors::{DiagnosticBuilder, Applicability};
use crate::borrowck::gather_loans::gather_moves::PatternSource;
use log::debug;
pub struct MoveErrorCollector<'tcx> {
errors: Vec<MoveError<'tcx>>
}
impl<'tcx> MoveErrorCollector<'tcx> {
pub fn new() -> MoveErrorCollector<'tcx> {
MoveErrorCollector {
errors: Vec::new()
}
}
pub fn add_error(&mut self, error: MoveError<'tcx>) {
self.errors.push(error);
}
pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
report_move_errors(bccx, &self.errors)
}
}
pub struct MoveError<'tcx> {
move_from: mc::cmt<'tcx>,
move_to: Option<MovePlace<'tcx>>
}
impl<'tcx> MoveError<'tcx> {
pub fn with_move_info(move_from: mc::cmt<'tcx>,
move_to: Option<MovePlace<'tcx>>)
-> MoveError<'tcx> {
MoveError {
move_from,
move_to,
}
}
}
#[derive(Clone)]
pub struct MovePlace<'tcx> {
pub span: syntax_pos::Span,
pub name: ast::Name,
pub pat_source: PatternSource<'tcx>,
}
pub struct GroupedMoveErrors<'tcx> {
move_from: mc::cmt<'tcx>,
move_to_places: Vec<MovePlace<'tcx>>
}
fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &[MoveError<'tcx>]) {
let grouped_errors = group_errors_with_same_origin(errors);
for error in &grouped_errors {
let mut err = report_cannot_move_out_of(bccx, error.move_from.clone());
let mut is_first_note = true;
match error.move_to_places.get(0) {
Some(&MovePlace { pat_source: PatternSource::LetDecl(ref e), .. }) => {
// ignore patterns that are found at the top-level of a `let`;
// see `get_pattern_source()` for details
let initializer =
e.init.as_ref().expect("should have an initializer to get an error");
if let Ok(snippet) = bccx.tcx.sess.source_map().span_to_snippet(initializer.span) {
err.span_suggestion(
initializer.span,
"consider using a reference instead",
format!("&{}", snippet),
Applicability::MaybeIncorrect // using a reference may not be the right fix
);
}
}
_ => {
for move_to in &error.move_to_places {
err = note_move_destination(err, move_to.span, move_to.name, is_first_note);
is_first_note = false;
}
}
}
if let NoteClosureEnv(upvar_id) = error.move_from.note {
err.span_label(bccx.tcx.hir().span(upvar_id.var_path.hir_id),
"captured outer variable");
}
err.emit();
bccx.signal_error();
}
}
fn group_errors_with_same_origin<'tcx>(errors: &[MoveError<'tcx>])
-> Vec<GroupedMoveErrors<'tcx>> {
let mut grouped_errors = Vec::new();
for error in errors {
append_to_grouped_errors(&mut grouped_errors, error)
}
return grouped_errors;
fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>,
error: &MoveError<'tcx>) {
let move_from_id = error.move_from.hir_id;
debug!("append_to_grouped_errors(move_from_id={:?})", move_from_id);
let move_to = if error.move_to.is_some() {
vec![error.move_to.clone().unwrap()]
} else {
Vec::new()
};
for ge in &mut *grouped_errors {
if move_from_id == ge.move_from.hir_id && error.move_to.is_some() {
debug!("appending move_to to list");
ge.move_to_places.extend(move_to);
return
}
}
debug!("found a new move from location");
grouped_errors.push(GroupedMoveErrors {
move_from: error.move_from.clone(),
move_to_places: move_to
})
}
}
// (keep in sync with gather_moves::check_and_get_illegal_move_origin )
fn report_cannot_move_out_of<'a, 'tcx>(bccx: &'a BorrowckCtxt<'a, 'tcx>,
move_from: mc::cmt<'tcx>)
-> DiagnosticBuilder<'a> {
match move_from.cat {
Categorization::Deref(_, mc::BorrowedPtr(..)) |
Categorization::Deref(_, mc::UnsafePtr(..)) |
Categorization::Deref(_, mc::Unique) |
Categorization::ThreadLocal(..) |
Categorization::StaticItem => {
bccx.cannot_move_out_of(
move_from.span, &move_from.descriptive_string(bccx.tcx), Origin::Ast)
}
Categorization::Interior(ref b, mc::InteriorElement(ik)) => {
bccx.cannot_move_out_of_interior_noncopy(
move_from.span, b.ty, Some(ik == Kind::Index), Origin::Ast)
}
Categorization::Downcast(ref b, _) |
Categorization::Interior(ref b, mc::InteriorField(_)) => {
match b.ty.sty {
ty::Adt(def, _) if def.has_dtor(bccx.tcx) => {
bccx.cannot_move_out_of_interior_of_drop(
move_from.span, b.ty, Origin::Ast)
}
_ => {
span_bug!(move_from.span, "this path should not cause illegal move");
}
}
}
Categorization::Rvalue(..) |
Categorization::Local(..) |
Categorization::Upvar(..) => {
span_bug!(move_from.span, "this path should not cause illegal move");
}
}
}
fn note_move_destination(mut err: DiagnosticBuilder<'_>,
move_to_span: syntax_pos::Span,
pat_name: ast::Name,
is_first_note: bool) -> DiagnosticBuilder<'_> {
if is_first_note {
err.span_label(
move_to_span,
format!("hint: to prevent move, use `ref {0}` or `ref mut {0}`",
pat_name));
err
} else {
err.span_label(move_to_span,
format!("...and here (use `ref {0}` or `ref mut {0}`)",
pat_name));
err
}
}

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
#![allow(non_snake_case)]

View file

@ -18,7 +18,7 @@ rayon = { version = "0.2.0", package = "rustc-rayon" }
rustc = { path = "../librustc" }
rustc_allocator = { path = "../librustc_allocator" }
rustc_target = { path = "../librustc_target" }
rustc_borrowck = { path = "../librustc_borrowck" }
rustc_ast_borrowck = { path = "../librustc_ast_borrowck" }
rustc_data_structures = { path = "../librustc_data_structures" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_incremental = { path = "../librustc_incremental" }

View file

@ -12,8 +12,8 @@ use rustc::session::config::Input;
use rustc::ty::{self, TyCtxt};
use rustc::util::common::ErrorReported;
use rustc_interface::util::ReplaceBodyWithLoop;
use rustc_borrowck as borrowck;
use rustc_borrowck::graphviz as borrowck_dot;
use rustc_ast_borrowck as borrowck;
use rustc_ast_borrowck::graphviz as borrowck_dot;
use rustc_mir::util::{write_mir_pretty, write_mir_graphviz};
use syntax::ast;

View file

@ -19,7 +19,7 @@ syntax_pos = { path = "../libsyntax_pos" }
serialize = { path = "../libserialize" }
rustc = { path = "../librustc" }
rustc_allocator = { path = "../librustc_allocator" }
rustc_borrowck = { path = "../librustc_borrowck" }
rustc_ast_borrowck = { path = "../librustc_ast_borrowck" }
rustc_incremental = { path = "../librustc_incremental" }
rustc_traits = { path = "../librustc_traits" }
rustc_data_structures = { path = "../librustc_data_structures" }

View file

@ -19,7 +19,7 @@ use rustc::session::{CompileResult, CrateDisambiguator, Session};
use rustc::session::config::{self, CrateType, Input, OutputFilenames, OutputType};
use rustc::session::search_paths::PathKind;
use rustc_allocator as allocator;
use rustc_borrowck as borrowck;
use rustc_ast_borrowck as borrowck;
use rustc_codegen_ssa::back::link::emit_metadata;
use rustc_codegen_utils::codegen_backend::CodegenBackend;
use rustc_codegen_utils::link::filename_for_metadata;

View file

@ -1,34 +1,13 @@
use rustc::session::config::BorrowckMode;
use rustc::ty::{self, Ty, TyCtxt};
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
use syntax_pos::{MultiSpan, Span};
use std::fmt;
// FIXME(chrisvittal) remove Origin entirely
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Origin {
Ast,
Mir,
}
impl fmt::Display for Origin {
fn fmt(&self, _w: &mut fmt::Formatter<'_>) -> fmt::Result {
// FIXME(chrisvittal) remove Origin entirely
// Print no origin info
Ok(())
}
}
impl Origin {
/// Whether we should emit errors for the origin in the given mode
pub fn should_emit_errors(self, mode: BorrowckMode) -> bool {
match self {
Origin::Ast => mode.use_ast(),
Origin::Mir => true,
}
}
}
pub trait BorrowckErrors<'cx>: Sized + Copy {
fn struct_span_err_with_code<S: Into<MultiSpan>>(
self,
@ -39,32 +18,19 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
fn struct_span_err<S: Into<MultiSpan>>(self, sp: S, msg: &str) -> DiagnosticBuilder<'cx>;
/// Cancels the given error if we shouldn't emit errors for a given
/// origin in the current mode.
///
/// Always make sure that the error gets passed through this function
/// before you return it.
fn cancel_if_wrong_origin(
self,
diag: DiagnosticBuilder<'cx>,
o: Origin,
) -> DiagnosticBuilder<'cx>;
fn cannot_move_when_borrowed(
self,
span: Span,
desc: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0505,
"cannot move out of `{}` because it is borrowed{OGN}",
"cannot move out of `{}` because it is borrowed",
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_use_when_mutably_borrowed(
@ -73,15 +39,14 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
desc: &str,
borrow_span: Span,
borrow_desc: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0503,
"cannot use `{}` because it was mutably borrowed{OGN}",
"cannot use `{}` because it was mutably borrowed",
desc,
OGN = o
);
err.span_label(
@ -89,8 +54,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
format!("borrow of `{}` occurs here", borrow_desc),
);
err.span_label(span, format!("use of borrowed `{}`", borrow_desc));
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_act_on_uninitialized_variable(
@ -98,18 +62,16 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
span: Span,
verb: &str,
desc: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0381,
"{} of possibly uninitialized variable: `{}`{OGN}",
"{} of possibly uninitialized variable: `{}`",
verb,
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_mutably_borrow_multiply(
@ -120,7 +82,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
old_loan_span: Span,
old_opt_via: &str,
old_load_end_span: Option<Span>,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let via = |msg: &str|
if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
@ -128,10 +90,9 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
self,
new_loan_span,
E0499,
"cannot borrow `{}`{} as mutable more than once at a time{OGN}",
"cannot borrow `{}`{} as mutable more than once at a time",
desc,
via(opt_via),
OGN = o
);
if old_loan_span == new_loan_span {
// Both borrows are happening in the same place
@ -160,7 +121,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
err.span_label(old_load_end_span, "first borrow ends here");
}
}
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_uniquely_borrow_by_two_closures(
@ -169,15 +130,14 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
desc: &str,
old_loan_span: Span,
old_load_end_span: Option<Span>,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
new_loan_span,
E0524,
"two closures require unique access to `{}` at the same time{OGN}",
"two closures require unique access to `{}` at the same time",
desc,
OGN = o
);
if old_loan_span == new_loan_span {
err.span_label(
@ -191,7 +151,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, "borrow from first closure ends here");
}
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_uniquely_borrow_by_one_closure(
@ -204,17 +164,16 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
noun_old: &str,
old_opt_via: &str,
previous_end_span: Option<Span>,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
new_loan_span,
E0500,
"closure requires unique access to `{}` but {} is already borrowed{}{OGN}",
"closure requires unique access to `{}` but {} is already borrowed{}",
desc_new,
noun_old,
old_opt_via,
OGN = o
);
err.span_label(
new_loan_span,
@ -224,7 +183,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
if let Some(previous_end_span) = previous_end_span {
err.span_label(previous_end_span, "borrow ends here");
}
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_reborrow_already_uniquely_borrowed(
@ -238,18 +197,17 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
old_opt_via: &str,
previous_end_span: Option<Span>,
second_borrow_desc: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
new_loan_span,
E0501,
"cannot borrow `{}`{} as {} because previous closure \
requires unique access{OGN}",
requires unique access",
desc_new,
opt_via,
kind_new,
OGN = o
);
err.span_label(
new_loan_span,
@ -262,7 +220,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
if let Some(previous_end_span) = previous_end_span {
err.span_label(previous_end_span, "borrow from closure ends here");
}
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_reborrow_already_borrowed(
@ -276,7 +234,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
kind_old: &str,
msg_old: &str,
old_load_end_span: Option<Span>,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let via = |msg: &str|
if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
@ -285,14 +243,13 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
span,
E0502,
"cannot borrow `{}`{} as {} because {} is also borrowed \
as {}{}{OGN}",
as {}{}",
desc_new,
via(msg_new),
kind_new,
noun_old,
kind_old,
via(msg_old),
OGN = o
);
if msg_new == "" {
@ -317,8 +274,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
}
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_assign_to_borrowed(
@ -326,15 +282,14 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
span: Span,
borrow_span: Span,
desc: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0506,
"cannot assign to `{}` because it is borrowed{OGN}",
"cannot assign to `{}` because it is borrowed",
desc,
OGN = o
);
err.span_label(borrow_span, format!("borrow of `{}` occurs here", desc));
@ -342,21 +297,17 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
span,
format!("assignment to borrowed `{}` occurs here", desc),
);
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_move_into_closure(self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
fn cannot_move_into_closure(self, span: Span, desc: &str, _: Origin) -> DiagnosticBuilder<'cx> {
struct_span_err!(
self,
span,
E0504,
"cannot move `{}` into closure because it is borrowed{OGN}",
"cannot move `{}` into closure because it is borrowed",
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_reassign_immutable(
@ -364,29 +315,25 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
span: Span,
desc: &str,
is_arg: bool,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let msg = if is_arg {
"to immutable argument"
} else {
"twice to immutable variable"
};
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0384,
"cannot assign {} `{}`{OGN}",
"cannot assign {} `{}`",
msg,
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_assign(self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(self, span, E0594, "cannot assign to {}{OGN}", desc, OGN = o);
self.cancel_if_wrong_origin(err, o)
fn cannot_assign(self, span: Span, desc: &str, _: Origin) -> DiagnosticBuilder<'cx> {
struct_span_err!(self, span, E0594, "cannot assign to {}", desc)
}
fn cannot_assign_static(self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder<'cx> {
@ -397,18 +344,15 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
self,
move_from_span: Span,
move_from_desc: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
move_from_span,
E0507,
"cannot move out of {}{OGN}",
"cannot move out of {}",
move_from_desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
/// Signal an error due to an attempt to move out of the interior
@ -419,7 +363,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
move_from_span: Span,
ty: Ty<'_>,
is_index: Option<bool>,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let type_name = match (&ty.sty, is_index) {
(&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array",
@ -430,33 +374,29 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
self,
move_from_span,
E0508,
"cannot move out of type `{}`, a non-copy {}{OGN}",
"cannot move out of type `{}`, a non-copy {}",
ty,
type_name,
OGN = o
);
err.span_label(move_from_span, "cannot move out of here");
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_move_out_of_interior_of_drop(
self,
move_from_span: Span,
container_ty: Ty<'_>,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
move_from_span,
E0509,
"cannot move out of type `{}`, which implements the `Drop` trait{OGN}",
"cannot move out of type `{}`, which implements the `Drop` trait",
container_ty,
OGN = o
);
err.span_label(move_from_span, "cannot move out of here");
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_act_on_moved_value(
@ -465,60 +405,51 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
verb: &str,
optional_adverb_for_moved: &str,
moved_path: Option<String>,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let moved_path = moved_path
.map(|mp| format!(": `{}`", mp))
.unwrap_or_default();
let err = struct_span_err!(
struct_span_err!(
self,
use_span,
E0382,
"{} of {}moved value{}{OGN}",
"{} of {}moved value{}",
verb,
optional_adverb_for_moved,
moved_path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_partially_reinit_an_uninit_struct(
self,
span: Span,
uninit_path: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0383,
"partial reinitialization of uninitialized structure `{}`{OGN}",
"partial reinitialization of uninitialized structure `{}`",
uninit_path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn closure_cannot_assign_to_borrowed(
self,
span: Span,
descr: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0595,
"closure cannot assign to {}{OGN}",
"closure cannot assign to {}",
descr,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_borrow_path_as_mutable_because(
@ -526,19 +457,16 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
span: Span,
path: &str,
reason: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0596,
"cannot borrow {} as mutable{}{OGN}",
"cannot borrow {} as mutable{}",
path,
reason,
OGN = o,
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_borrow_path_as_mutable(
@ -556,73 +484,63 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
match_span: Span,
match_place: &str,
action: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
mutate_span,
E0510,
"cannot {} `{}` in match guard{OGN}",
"cannot {} `{}` in match guard",
action,
match_place,
OGN = o
);
err.span_label(mutate_span, format!("cannot {}", action));
err.span_label(match_span, String::from("value is immutable in match guard"));
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_borrow_across_generator_yield(
self,
span: Span,
yield_span: Span,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0626,
"borrow may still be in use when generator yields{OGN}",
OGN = o
"borrow may still be in use when generator yields",
);
err.span_label(yield_span, "possible yield occurs here");
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_borrow_across_destructor(
self,
borrow_span: Span,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
borrow_span,
E0713,
"borrow may still be in use when destructor runs{OGN}",
OGN = o
);
self.cancel_if_wrong_origin(err, o)
"borrow may still be in use when destructor runs",
)
}
fn path_does_not_live_long_enough(
self,
span: Span,
path: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0597,
"{} does not live long enough{OGN}",
"{} does not live long enough",
path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_return_reference_to_local(
@ -631,17 +549,16 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
return_kind: &str,
reference_desc: &str,
path_desc: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0515,
"cannot {RETURN} {REFERENCE} {LOCAL}{OGN}",
"cannot {RETURN} {REFERENCE} {LOCAL}",
RETURN=return_kind,
REFERENCE=reference_desc,
LOCAL=path_desc,
OGN = o
);
err.span_label(
@ -649,26 +566,23 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
format!("{}s a {} data owned by the current function", return_kind, reference_desc),
);
self.cancel_if_wrong_origin(err, o)
err
}
fn lifetime_too_short_for_reborrow(
self,
span: Span,
path: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0598,
"lifetime of {} is too short to guarantee \
its contents can be safely reborrowed{OGN}",
its contents can be safely reborrowed",
path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn cannot_act_on_capture_in_sharable_fn(
@ -676,39 +590,35 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
span: Span,
bad_thing: &str,
help: (Span, &str),
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let (help_span, help_msg) = help;
let mut err = struct_span_err!(
self,
span,
E0387,
"{} in a captured outer variable in an `Fn` closure{OGN}",
"{} in a captured outer variable in an `Fn` closure",
bad_thing,
OGN = o
);
err.span_help(help_span, help_msg);
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_assign_into_immutable_reference(
self,
span: Span,
bad_thing: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0389,
"{} in a `&` reference{OGN}",
"{} in a `&` reference",
bad_thing,
OGN = o
);
err.span_label(span, "assignment into an immutable reference");
self.cancel_if_wrong_origin(err, o)
err
}
fn cannot_capture_in_long_lived_closure(
@ -716,7 +626,7 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
closure_span: Span,
borrowed_path: &str,
capture_span: Span,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
@ -724,67 +634,56 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
E0373,
"closure may outlive the current function, \
but it borrows {}, \
which is owned by the current function{OGN}",
which is owned by the current function",
borrowed_path,
OGN = o
);
err.span_label(capture_span, format!("{} is borrowed here", borrowed_path))
.span_label(
closure_span,
format!("may outlive borrowed value {}", borrowed_path),
);
self.cancel_if_wrong_origin(err, o)
err
}
fn borrowed_data_escapes_closure(
self,
escape_span: Span,
escapes_from: &str,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
escape_span,
E0521,
"borrowed data escapes outside of {}{OGN}",
"borrowed data escapes outside of {}",
escapes_from,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
)
}
fn thread_local_value_does_not_live_long_enough(
self,
span: Span,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0712,
"thread-local variable borrowed past end of function{OGN}",
OGN = o
);
self.cancel_if_wrong_origin(err, o)
"thread-local variable borrowed past end of function",
)
}
fn temporary_value_borrowed_for_too_long(
self,
span: Span,
o: Origin,
_: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
struct_span_err!(
self,
span,
E0716,
"temporary value dropped while borrowed{OGN}",
OGN = o
);
self.cancel_if_wrong_origin(err, o)
"temporary value dropped while borrowed",
)
}
}
@ -801,15 +700,4 @@ impl BorrowckErrors<'tcx> for TyCtxt<'tcx> {
fn struct_span_err<S: Into<MultiSpan>>(self, sp: S, msg: &str) -> DiagnosticBuilder<'tcx> {
self.sess.struct_span_err(sp, msg)
}
fn cancel_if_wrong_origin(
self,
mut diag: DiagnosticBuilder<'tcx>,
o: Origin,
) -> DiagnosticBuilder<'tcx> {
if !o.should_emit_errors(self.borrowck_mode()) {
self.sess.diagnostic().cancel(&mut diag);
}
diag
}
}