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:
commit
1b1b538843
@ -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",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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" }
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
@ -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 });
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
614
src/librustc_ast_borrowck/borrowck/mod.rs
Normal file
614
src/librustc_ast_borrowck/borrowck/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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
@ -1 +0,0 @@
|
||||
#![allow(non_snake_case)]
|
@ -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" }
|
||||
|
@ -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;
|
||||
|
@ -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" }
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user