diff --git a/Cargo.lock b/Cargo.lock index 8957e223b10..cee2b385648 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/src/librustc/middle/borrowck.rs b/src/librustc/middle/borrowck.rs index 2799f9424d9..60c24eeae7b 100644 --- a/src/librustc/middle/borrowck.rs +++ b/src/librustc/middle/borrowck.rs @@ -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, pub signalled_any_error: SignalledError, } @@ -27,10 +24,8 @@ impl<'a> HashStable> for BorrowCheckResult { hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { 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); } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 66d8a2cc111..ddad276f8a7 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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>; -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> { - 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; diff --git a/src/librustc_borrowck/Cargo.toml b/src/librustc_ast_borrowck/Cargo.toml similarity index 87% rename from src/librustc_borrowck/Cargo.toml rename to src/librustc_ast_borrowck/Cargo.toml index e9abc17202e..024b2640e1e 100644 --- a/src/librustc_borrowck/Cargo.toml +++ b/src/librustc_ast_borrowck/Cargo.toml @@ -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" } diff --git a/src/librustc_borrowck/borrowck/README.md b/src/librustc_ast_borrowck/borrowck/README.md similarity index 100% rename from src/librustc_borrowck/borrowck/README.md rename to src/librustc_ast_borrowck/borrowck/README.md diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_ast_borrowck/borrowck/check_loans.rs similarity index 60% rename from src/librustc_borrowck/borrowck/check_loans.rs rename to src/librustc_ast_borrowck/borrowck/check_loans.rs index a098cd17612..3d824ee6ce1 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_ast_borrowck/borrowck/check_loans.rs @@ -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>, /*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>) { - 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>) { 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(); - } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/gather_moves.rs similarity index 54% rename from src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs rename to src/librustc_ast_borrowck/borrowck/gather_loans/gather_moves.rs index 658e4307348..617161109b5 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc_ast_borrowck/borrowck/gather_loans/gather_moves.rs @@ -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>, -} - -/// 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 diff --git a/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/lifetime.rs similarity index 80% rename from src/librustc_borrowck/borrowck/gather_loans/lifetime.rs rename to src/librustc_ast_borrowck/borrowck/gather_loans/lifetime.rs index 3122a6060fb..ff7dd66793d 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs +++ b/src/librustc_ast_borrowck/borrowck/gather_loans/lifetime.rs @@ -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 }); - } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/mod.rs similarity index 73% rename from src/librustc_borrowck/borrowck/gather_loans/mod.rs rename to src/librustc_ast_borrowck/borrowck/gather_loans/mod.rs index 887011d3476..16fef705ec9 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_ast_borrowck/borrowck/gather_loans/mod.rs @@ -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>, /// `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 - // - // 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); - } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/restrictions.rs similarity index 91% rename from src/librustc_borrowck/borrowck/gather_loans/restrictions.rs rename to src/librustc_ast_borrowck/borrowck/gather_loans/restrictions.rs index 371e6c55a73..545c27b17bd 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs +++ b/src/librustc_ast_borrowck/borrowck/gather_loans/restrictions.rs @@ -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; } diff --git a/src/librustc_ast_borrowck/borrowck/mod.rs b/src/librustc_ast_borrowck/borrowck/mod.rs new file mode 100644 index 00000000000..f8ad8baa597 --- /dev/null +++ b/src/librustc_ast_borrowck/borrowck/mod.rs @@ -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>, + 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> + 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, +} + + +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>, + kind: ty::BorrowKind, + restricted_paths: Vec>>, + + /// 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> { + 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(&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>, DefId), // `x` downcast to particular enum variant + LpExtend(Rc>, 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, 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>>, 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>> { + 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) + } + } + } +} diff --git a/src/librustc_borrowck/borrowck/move_data.rs b/src/librustc_ast_borrowck/borrowck/move_data.rs similarity index 96% rename from src/librustc_borrowck/borrowck/move_data.rs rename to src/librustc_ast_borrowck/borrowck/move_data.rs index 9feea64f182..887a0e2f20e 100644 --- a/src/librustc_borrowck/borrowck/move_data.rs +++ b/src/librustc_ast_borrowck/borrowck/move_data.rs @@ -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>, 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>, 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>) - -> Option { + pub fn is_move_path(&self, id: hir::ItemLocalId, loan_path: &Rc>) -> 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 diff --git a/src/librustc_borrowck/dataflow.rs b/src/librustc_ast_borrowck/dataflow.rs similarity index 100% rename from src/librustc_borrowck/dataflow.rs rename to src/librustc_ast_borrowck/dataflow.rs diff --git a/src/librustc_borrowck/graphviz.rs b/src/librustc_ast_borrowck/graphviz.rs similarity index 100% rename from src/librustc_borrowck/graphviz.rs rename to src/librustc_ast_borrowck/graphviz.rs diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_ast_borrowck/lib.rs similarity index 100% rename from src/librustc_borrowck/lib.rs rename to src/librustc_ast_borrowck/lib.rs diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs deleted file mode 100644 index 58be2cf76c7..00000000000 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ /dev/null @@ -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> -} - -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> -} - -impl<'tcx> MoveError<'tcx> { - pub fn with_move_info(move_from: mc::cmt<'tcx>, - move_to: Option>) - -> 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> -} - -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> { - 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>, - 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 - } -} diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs deleted file mode 100644 index 9a581cb03ec..00000000000 --- a/src/librustc_borrowck/borrowck/mod.rs +++ /dev/null @@ -1,1573 +0,0 @@ -//! See The Book chapter on the borrow checker for more details. - -#![allow(non_camel_case_types)] - -pub use LoanPathKind::*; -pub use LoanPathElem::*; -pub use bckerr_code::*; -pub use AliasableViolationKind::*; -pub use MovedValueUseKind::*; - -use InteriorKind::*; - -use rustc::hir::HirId; -use rustc::hir::Node; -use rustc::hir::map::blocks::FnLikeNode; -use rustc::cfg; -use rustc::middle::borrowck::{BorrowCheckResult, SignalledError}; -use rustc::hir::def_id::{DefId, LocalDefId}; -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::ImmutabilityBlame; -use rustc::middle::region; -use rustc::middle::free_region::RegionRelations; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::query::Providers; -use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin}; -use rustc_mir::util::suggest_ref_mut; -use rustc::util::nodemap::FxHashSet; - -use std::borrow::Cow; -use std::cell::{Cell, RefCell}; -use std::fmt; -use std::rc::Rc; -use std::hash::{Hash, Hasher}; -use syntax::source_map::DesugaringKind; -use syntax_pos::{MultiSpan, Span}; -use errors::{Applicability, DiagnosticBuilder, DiagnosticId}; -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>, - 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 { - used_mut_nodes: Default::default(), - 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, - used_mut_nodes: Default::default(), - 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 { - used_mut_nodes: bccx.used_mut_nodes.into_inner(), - 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> -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, - used_mut_nodes: Default::default(), - 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, - - used_mut_nodes: RefCell>, - - signalled_any_error: Cell, -} - -impl BorrowckCtxt<'_, 'tcx> { - fn signal_error(&self) { - self.signalled_any_error.set(SignalledError::SawSomeError); - } -} - -impl BorrowckErrors<'a> for &'a BorrowckCtxt<'_, 'tcx> { - fn struct_span_err_with_code>(self, - sp: S, - msg: &str, - code: DiagnosticId) - -> DiagnosticBuilder<'a> - { - self.tcx.sess.struct_span_err_with_code(sp, msg, code) - } - - fn struct_span_err>(self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'a> - { - self.tcx.sess.struct_span_err(sp, msg) - } - - fn cancel_if_wrong_origin(self, - mut diag: DiagnosticBuilder<'a>, - o: Origin) - -> DiagnosticBuilder<'a> - { - if !o.should_emit_errors(self.tcx.borrowck_mode()) { - self.tcx.sess.diagnostic().cancel(&mut diag); - } - diag - } -} - -/////////////////////////////////////////////////////////////////////////// -// Loans and loan paths - -/// Record of a loan that was issued. -pub struct Loan<'tcx> { - index: usize, - loan_path: Rc>, - kind: ty::BorrowKind, - restricted_paths: Vec>>, - - /// 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, - span: Span, - cause: euv::LoanCause, -} - -impl<'tcx> Loan<'tcx> { - pub fn loan_path(&self) -> Rc> { - 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(&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>, DefId), // `x` downcast to particular enum variant - LpExtend(Rc>, 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 } - - fn has_downcast(&self) -> bool { - match self.kind { - LpDowncast(_, _) => true, - LpExtend(ref lp, _, LpInterior(_, _)) => { - lp.has_downcast() - } - _ => false, - } - } -} - -// 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, 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 LoanPath<'tcx> { - pub fn kill_scope(&self, bccx: &BorrowckCtxt<'_, '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), - } - } - - fn has_fork(&self, other: &LoanPath<'tcx>) -> bool { - match (&self.kind, &other.kind) { - (&LpExtend(ref base, _, LpInterior(opt_variant_id, id)), - &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => - if id == id2 && opt_variant_id == opt_variant_id2 { - base.has_fork(&base2) - } else { - true - }, - (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other), - (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&base), - _ => false, - } - } - - fn depth(&self) -> usize { - match self.kind { - LpExtend(ref base, _, LpDeref(_)) => base.depth(), - LpExtend(ref base, _, LpInterior(..)) => base.depth() + 1, - _ => 0, - } - } - - fn common(&self, other: &LoanPath<'tcx>) -> Option> { - match (&self.kind, &other.kind) { - (&LpExtend(ref base, a, LpInterior(opt_variant_id, id)), - &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => { - if id == id2 && opt_variant_id == opt_variant_id2 { - base.common(&base2).map(|x| { - let xd = x.depth(); - if base.depth() == xd && base2.depth() == xd { - LoanPath { - kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)), - ty: self.ty, - } - } else { - x - } - }) - } else { - base.common(&base2) - } - } - (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other), - (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&other), - (&LpVar(id), &LpVar(id2)) => { - if id == id2 { - Some(LoanPath { kind: LpVar(id), ty: self.ty }) - } else { - None - } - } - (&LpUpvar(id), &LpUpvar(id2)) => { - if id == id2 { - Some(LoanPath { kind: LpUpvar(id), ty: self.ty }) - } else { - None - } - } - _ => None, - } - } -} - -// 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>>, 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>> { - opt_loan_path_is_field(cmt).0 -} - -/////////////////////////////////////////////////////////////////////////// -// Errors - -// Errors that can occur -#[derive(Debug, PartialEq)] -pub enum bckerr_code<'tcx> { - err_mutbl, - /// superscope, subscope, loan cause - err_out_of_scope(ty::Region<'tcx>, ty::Region<'tcx>, euv::LoanCause), - err_borrowed_pointer_too_short(ty::Region<'tcx>, ty::Region<'tcx>), // loan, ptr -} - -// Combination of an error code and the categorization of the expression -// that caused it -#[derive(Debug, PartialEq)] -pub struct BckError<'c, 'tcx> { - span: Span, - cause: AliasableViolationKind, - cmt: &'c mc::cmt_<'tcx>, - code: bckerr_code<'tcx> -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum AliasableViolationKind { - MutabilityViolation, - BorrowViolation(euv::LoanCause) -} - -#[derive(Copy, Clone, Debug)] -pub enum MovedValueUseKind { - MovedInUse, - MovedInCapture, -} - -/////////////////////////////////////////////////////////////////////////// -// Misc - -impl BorrowckCtxt<'_, '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 report(&self, err: BckError<'a, 'tcx>) { - // Catch and handle some particular cases. - match (&err.code, &err.cause) { - (&err_out_of_scope(&ty::ReScope(_), &ty::ReStatic, _), - &BorrowViolation(euv::ClosureCapture(span))) | - (&err_out_of_scope(&ty::ReScope(_), &ty::ReEarlyBound(..), _), - &BorrowViolation(euv::ClosureCapture(span))) | - (&err_out_of_scope(&ty::ReScope(_), &ty::ReFree(..), _), - &BorrowViolation(euv::ClosureCapture(span))) => { - return self.report_out_of_scope_escaping_closure_capture(&err, span); - } - _ => { } - } - - self.report_bckerr(&err); - } - - pub fn report_use_of_moved_value(&self, - use_span: Span, - use_kind: MovedValueUseKind, - lp: &LoanPath<'tcx>, - the_move: &move_data::Move, - moved_lp: &LoanPath<'tcx>) { - let (verb, verb_participle) = match use_kind { - MovedInUse => ("use", "used"), - MovedInCapture => ("capture", "captured"), - }; - - let (_ol, _moved_lp_msg, mut err, need_note) = match the_move.kind { - move_data::Declared => { - // If this is an uninitialized variable, just emit a simple warning - // and return. - self.cannot_act_on_uninitialized_variable(use_span, - verb, - &self.loan_path_to_string(lp), - Origin::Ast) - .span_label(use_span, format!("use of possibly uninitialized `{}`", - self.loan_path_to_string(lp))) - .emit(); - self.signal_error(); - return; - } - _ => { - // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would - // normally generate a rather confusing message: - // - // error: use of moved value: `x.b` - // note: `x.a` moved here... - // - // What we want to do instead is get the 'common ancestor' of the two moves and - // use that for most of the message instead, giving is something like this: - // - // error: use of moved value: `x` - // note: `x` moved here (through moving `x.a`)... - - let common = moved_lp.common(lp); - let has_common = common.is_some(); - let has_fork = moved_lp.has_fork(lp); - let (nl, ol, moved_lp_msg) = - if has_fork && has_common { - let nl = self.loan_path_to_string(&common.unwrap()); - let ol = nl.clone(); - let moved_lp_msg = format!(" (through moving `{}`)", - self.loan_path_to_string(moved_lp)); - (nl, ol, moved_lp_msg) - } else { - (self.loan_path_to_string(lp), - self.loan_path_to_string(moved_lp), - String::new()) - }; - - let partial = moved_lp.depth() > lp.depth(); - let msg = if !has_fork && partial { "partially " } - else if has_fork && !has_common { "collaterally "} - else { "" }; - let mut err = self.cannot_act_on_moved_value(use_span, - verb, - msg, - Some(nl), - Origin::Ast); - let need_note = match lp.ty.sty { - ty::Closure(id, _) => { - let hir_id = self.tcx.hir().as_local_hir_id(id).unwrap(); - if let Some((span, name)) = self.tables.closure_kind_origins().get(hir_id) { - err.span_note(*span, &format!( - "closure cannot be invoked more than once because \ - it moves the variable `{}` out of its environment", - name - )); - false - } else { - true - } - } - _ => true, - }; - (ol, moved_lp_msg, err, need_note) - } - }; - - // Get type of value and span where it was previously - // moved. - let hir_id = hir::HirId { - owner: self.body.value.hir_id.owner, - local_id: the_move.id - }; - let (move_span, move_note) = match the_move.kind { - move_data::Declared => { - unreachable!(); - } - - move_data::MoveExpr | - move_data::MovePat => (self.tcx.hir().span(hir_id), ""), - - move_data::Captured => - (match self.tcx.hir().expect_expr(hir_id).node { - hir::ExprKind::Closure(.., fn_decl_span, _) => fn_decl_span, - ref r => bug!("Captured({:?}) maps to non-closure: {:?}", - the_move.id, r), - }, " (into closure)"), - }; - - // Annotate the use and the move in the span. Watch out for - // the case where the use and the move are the same. This - // means the use is in a loop. - err = if use_span == move_span { - err.span_label( - use_span, - format!("value moved{} here in previous iteration of loop", - move_note)); - err - } else { - err.span_label(use_span, format!("value {} here after move", verb_participle)); - err.span_label(move_span, format!("value moved{} here", move_note)); - err - }; - - if need_note { - err.note(&format!( - "move occurs because {} has type `{}`, which does not implement the `Copy` trait", - if moved_lp.has_downcast() { - "the value".to_string() - } else { - format!("`{}`", self.loan_path_to_string(moved_lp)) - }, - moved_lp.ty)); - } - if let (Some(DesugaringKind::ForLoop), Ok(snippet)) = ( - move_span.desugaring_kind(), - self.tcx.sess.source_map().span_to_snippet(move_span), - ) { - if !snippet.starts_with("&") { - err.span_suggestion( - move_span, - "consider borrowing this to avoid moving it into the for loop", - format!("&{}", snippet), - Applicability::MaybeIncorrect, - ); - } - } - - // Note: we used to suggest adding a `ref binding` or calling - // `clone` but those suggestions have been removed because - // they are often not what you actually want to do, and were - // not considered particularly helpful. - - err.emit(); - self.signal_error(); - } - - pub fn report_partial_reinitialization_of_uninitialized_structure( - &self, - span: Span, - lp: &LoanPath<'tcx>) { - self.cannot_partially_reinit_an_uninit_struct(span, - &self.loan_path_to_string(lp), - Origin::Ast) - .emit(); - self.signal_error(); - } - - pub fn report_reassigned_immutable_variable(&self, - span: Span, - lp: &LoanPath<'tcx>, - assign: - &move_data::Assignment) { - let mut err = self.cannot_reassign_immutable(span, - &self.loan_path_to_string(lp), - false, - Origin::Ast); - err.span_label(span, "cannot assign twice to immutable variable"); - if span != assign.span { - err.span_label(assign.span, format!("first assignment to `{}`", - self.loan_path_to_string(lp))); - } - err.emit(); - self.signal_error(); - } - - fn report_bckerr(&self, err: &BckError<'a, 'tcx>) { - let error_span = err.span.clone(); - - match err.code { - err_mutbl => { - let descr: Cow<'static, str> = match err.cmt.note { - mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => { - self.cmt_to_cow_str(&err.cmt) - } - _ => match opt_loan_path_is_field(&err.cmt) { - (None, true) => { - format!("{} of {} binding", - self.cmt_to_cow_str(&err.cmt), - err.cmt.mutbl.to_user_str()).into() - - } - (None, false) => { - format!("{} {}", - err.cmt.mutbl.to_user_str(), - self.cmt_to_cow_str(&err.cmt)).into() - - } - (Some(lp), true) => { - format!("{} `{}` of {} binding", - self.cmt_to_cow_str(&err.cmt), - self.loan_path_to_string(&lp), - err.cmt.mutbl.to_user_str()).into() - } - (Some(lp), false) => { - format!("{} {} `{}`", - err.cmt.mutbl.to_user_str(), - self.cmt_to_cow_str(&err.cmt), - self.loan_path_to_string(&lp)).into() - } - } - }; - - let mut db = match err.cause { - MutabilityViolation => { - let mut db = self.cannot_assign(error_span, &descr, Origin::Ast); - if let mc::NoteClosureEnv(upvar_id) = err.cmt.note { - let hir_id = upvar_id.var_path.hir_id; - let sp = self.tcx.hir().span(hir_id); - let fn_closure_msg = "`Fn` closures cannot capture their enclosing \ - environment for modifications"; - match (self.tcx.sess.source_map().span_to_snippet(sp), &err.cmt.cat) { - (_, &Categorization::Upvar(mc::Upvar { - kind: ty::ClosureKind::Fn, .. - })) => { - db.note(fn_closure_msg); - // we should point at the cause for this closure being - // identified as `Fn` (like in signature of method this - // closure was passed into) - } - (Ok(ref snippet), ref cat) => { - let msg = &format!("consider making `{}` mutable", snippet); - let suggestion = format!("mut {}", snippet); - - if let &Categorization::Deref(ref cmt, _) = cat { - if let Categorization::Upvar(mc::Upvar { - kind: ty::ClosureKind::Fn, .. - }) = cmt.cat { - db.note(fn_closure_msg); - } else { - db.span_suggestion( - sp, - msg, - suggestion, - Applicability::Unspecified, - ); - } - } else { - db.span_suggestion( - sp, - msg, - suggestion, - Applicability::Unspecified, - ); - } - } - _ => { - db.span_help(sp, "consider making this binding mutable"); - } - } - } - - db - } - BorrowViolation(euv::ClosureCapture(_)) => { - self.closure_cannot_assign_to_borrowed(error_span, &descr, Origin::Ast) - } - BorrowViolation(euv::OverloadedOperator) | - BorrowViolation(euv::AddrOf) | - BorrowViolation(euv::RefBinding) | - BorrowViolation(euv::AutoRef) | - BorrowViolation(euv::AutoUnsafe) | - BorrowViolation(euv::ForLoop) | - BorrowViolation(euv::MatchDiscriminant) => { - self.cannot_borrow_path_as_mutable(error_span, &descr, Origin::Ast) - } - BorrowViolation(euv::ClosureInvocation) => { - span_bug!(err.span, "err_mutbl with a closure invocation"); - } - }; - - // We add a special note about `IndexMut`, if the source of this error - // is the fact that `Index` is implemented, but `IndexMut` is not. Needing - // to implement two traits for "one operator" is not very intuitive for - // many programmers. - if err.cmt.note == mc::NoteIndex { - let node = self.tcx.hir().get(err.cmt.hir_id); - - // This pattern probably always matches. - if let Node::Expr( - hir::Expr { node: hir::ExprKind::Index(lhs, _), ..} - ) = node { - let ty = self.tables.expr_ty(lhs); - - db.help(&format!( - "trait `IndexMut` is required to modify indexed content, but \ - it is not implemented for `{}`", - ty - )); - } - } - - self.note_and_explain_mutbl_error(&mut db, &err, &error_span); - self.note_immutability_blame( - &mut db, - err.cmt.immutability_blame(), - err.cmt.hir_id - ); - db.emit(); - self.signal_error(); - } - err_out_of_scope(super_scope, sub_scope, cause) => { - let msg = match opt_loan_path(&err.cmt) { - None => "borrowed value".to_string(), - Some(lp) => { - format!("`{}`", self.loan_path_to_string(&lp)) - } - }; - - let mut db = self.path_does_not_live_long_enough(error_span, &msg, Origin::Ast); - let value_kind = match err.cmt.cat { - mc::Categorization::Rvalue(..) => "temporary value", - _ => "borrowed value", - }; - - let is_closure = match cause { - euv::ClosureCapture(s) => { - // The primary span starts out as the closure creation point. - // Change the primary span here to highlight the use of the variable - // in the closure, because it seems more natural. Highlight - // closure creation point as a secondary span. - match db.span.primary_span() { - Some(primary) => { - db.span = MultiSpan::from_span(s); - db.span_label(primary, "capture occurs here"); - db.span_label(s, format!("{} does not live long enough", - value_kind)); - true - } - None => false - } - } - _ => { - db.span_label(error_span, format!("{} does not live long enough", - value_kind)); - false - } - }; - - let sub_span = self.region_end_span(sub_scope); - let super_span = self.region_end_span(super_scope); - - match (sub_span, super_span) { - (Some(s1), Some(s2)) if s1 == s2 => { - if !is_closure { - let msg = match opt_loan_path(&err.cmt) { - None => value_kind.to_string(), - Some(lp) => { - format!("`{}`", self.loan_path_to_string(&lp)) - } - }; - db.span_label(s1, - format!("{} dropped here while still borrowed", msg)); - } else { - db.span_label(s1, format!("{} dropped before borrower", value_kind)); - } - db.note("values in a scope are dropped in the opposite order \ - they are created"); - } - (Some(s1), Some(s2)) if !is_closure => { - let msg = match opt_loan_path(&err.cmt) { - None => value_kind.to_string(), - Some(lp) => { - format!("`{}`", self.loan_path_to_string(&lp)) - } - }; - db.span_label(s2, format!("{} dropped here while still borrowed", msg)); - db.span_label(s1, format!("{} needs to live until here", value_kind)); - } - _ => { - match sub_span { - Some(s) => { - db.span_label(s, format!("{} needs to live until here", - value_kind)); - } - None => { - self.tcx.note_and_explain_region( - &self.region_scope_tree, - &mut db, - "borrowed value must be valid for ", - sub_scope, - "..."); - } - } - match super_span { - Some(s) => { - db.span_label(s, format!("{} only lives until here", value_kind)); - } - None => { - self.tcx.note_and_explain_region( - &self.region_scope_tree, - &mut db, - "...but borrowed value is only valid for ", - super_scope, - ""); - } - } - } - } - - if let ty::ReScope(scope) = *super_scope { - let hir_id = scope.hir_id(&self.region_scope_tree); - match self.tcx.hir().find(hir_id) { - Some(Node::Stmt(_)) => { - if *sub_scope != ty::ReStatic { - db.note("consider using a `let` binding to increase its lifetime"); - } - - } - _ => {} - } - } - - db.emit(); - self.signal_error(); - } - err_borrowed_pointer_too_short(loan_scope, ptr_scope) => { - let descr = self.cmt_to_path_or_string(err.cmt); - let mut db = self.lifetime_too_short_for_reborrow(error_span, &descr, Origin::Ast); - let descr: Cow<'static, str> = match opt_loan_path(&err.cmt) { - Some(lp) => { - format!("`{}`", self.loan_path_to_string(&lp)).into() - } - None => self.cmt_to_cow_str(&err.cmt) - }; - self.tcx.note_and_explain_region( - &self.region_scope_tree, - &mut db, - &format!("{} would have to be valid for ", - descr), - loan_scope, - "..."); - self.tcx.note_and_explain_region( - &self.region_scope_tree, - &mut db, - &format!("...but {} is only valid for ", descr), - ptr_scope, - ""); - - db.emit(); - self.signal_error(); - } - } - } - - pub fn report_aliasability_violation(&self, - span: Span, - kind: AliasableViolationKind, - cause: mc::AliasableReason, - cmt: &mc::cmt_<'tcx>) { - let mut is_closure = false; - let prefix = match kind { - MutabilityViolation => { - "cannot assign to data" - } - BorrowViolation(euv::ClosureCapture(_)) | - BorrowViolation(euv::OverloadedOperator) | - BorrowViolation(euv::AddrOf) | - BorrowViolation(euv::AutoRef) | - BorrowViolation(euv::AutoUnsafe) | - BorrowViolation(euv::RefBinding) | - BorrowViolation(euv::MatchDiscriminant) => { - "cannot borrow data mutably" - } - BorrowViolation(euv::ClosureInvocation) => { - is_closure = true; - "closure invocation" - } - - BorrowViolation(euv::ForLoop) => { - "`for` loop" - } - }; - - match cause { - mc::AliasableStaticMut => { - // This path cannot occur. `static mut X` is not checked - // for aliasability violations. - span_bug!(span, "aliasability violation for static mut `{}`", prefix) - } - mc::AliasableStatic | mc::AliasableBorrowed => {} - }; - let blame = cmt.immutability_blame(); - let mut err = match blame { - Some(ImmutabilityBlame::ClosureEnv(id)) => { - // FIXME: the distinction between these 2 messages looks wrong. - let help_msg = if let BorrowViolation(euv::ClosureCapture(_)) = kind { - // The aliasability violation with closure captures can - // happen for nested closures, so we know the enclosing - // closure incorrectly accepts an `Fn` while it needs to - // be `FnMut`. - "consider changing this to accept closures that implement `FnMut`" - - } else { - "consider changing this closure to take self by mutable reference" - }; - let hir_id = self.tcx.hir().local_def_id_to_hir_id(id); - let help_span = self.tcx.hir().span(hir_id); - self.cannot_act_on_capture_in_sharable_fn(span, - prefix, - (help_span, help_msg), - Origin::Ast) - } - _ => { - self.cannot_assign_into_immutable_reference(span, prefix, - Origin::Ast) - } - }; - self.note_immutability_blame( - &mut err, - blame, - cmt.hir_id - ); - - if is_closure { - err.help("closures behind references must be called via `&mut`"); - } - err.emit(); - self.signal_error(); - } - - /// Given a type, if it is an immutable reference, return a suggestion to make it mutable - fn suggest_mut_for_immutable(&self, pty: &hir::Ty, is_implicit_self: bool) -> Option { - // Check whether the argument is an immutable reference - debug!("suggest_mut_for_immutable({:?}, {:?})", pty, is_implicit_self); - if let hir::TyKind::Rptr(lifetime, hir::MutTy { - mutbl: hir::Mutability::MutImmutable, - ref ty - }) = pty.node { - // Account for existing lifetimes when generating the message - let pointee_snippet = match self.tcx.sess.source_map().span_to_snippet(ty.span) { - Ok(snippet) => snippet, - _ => return None - }; - - let lifetime_snippet = if !lifetime.is_elided() { - format!("{} ", match self.tcx.sess.source_map().span_to_snippet(lifetime.span) { - Ok(lifetime_snippet) => lifetime_snippet, - _ => return None - }) - } else { - String::new() - }; - Some(format!("use `&{}mut {}` here to make mutable", - lifetime_snippet, - if is_implicit_self { "self" } else { &*pointee_snippet })) - } else { - None - } - } - - fn local_binding_mode(&self, hir_id: hir::HirId) -> ty::BindingMode { - let pat = match self.tcx.hir().get(hir_id) { - Node::Binding(pat) => pat, - node => bug!("bad node for local: {:?}", node) - }; - - match pat.node { - hir::PatKind::Binding(..) => { - *self.tables - .pat_binding_modes() - .get(pat.hir_id) - .expect("missing binding mode") - } - _ => bug!("local is not a binding: {:?}", pat) - } - } - - fn local_ty(&self, hir_id: hir::HirId) -> (Option<&hir::Ty>, bool) { - let parent = self.tcx.hir().get_parent_node(hir_id); - let parent_node = self.tcx.hir().get(parent); - - // The parent node is like a fn - if let Some(fn_like) = FnLikeNode::from_node(parent_node) { - // `nid`'s parent's `Body` - let fn_body = self.tcx.hir().body(fn_like.body()); - // Get the position of `node_id` in the arguments list - let arg_pos = fn_body.arguments.iter().position(|arg| arg.pat.hir_id == hir_id); - if let Some(i) = arg_pos { - // The argument's `Ty` - (Some(&fn_like.decl().inputs[i]), - i == 0 && fn_like.decl().implicit_self.has_implicit_self()) - } else { - (None, false) - } - } else { - (None, false) - } - } - - fn note_immutability_blame(&self, - db: &mut DiagnosticBuilder<'_>, - blame: Option>, - error_hir_id: hir::HirId) { - match blame { - None => {} - Some(ImmutabilityBlame::ClosureEnv(_)) => {} - Some(ImmutabilityBlame::ImmLocal(hir_id)) => { - self.note_immutable_local(db, error_hir_id, hir_id) - } - Some(ImmutabilityBlame::LocalDeref(hir_id)) => { - match self.local_binding_mode(hir_id) { - ty::BindByReference(..) => { - let let_span = self.tcx.hir().span(hir_id); - let suggestion = suggest_ref_mut(self.tcx, let_span); - if let Some(replace_str) = suggestion { - db.span_suggestion( - let_span, - "use a mutable reference instead", - replace_str, - // I believe this can be machine applicable, - // but if there are multiple attempted uses of an immutable - // reference, I don't know how rustfix handles it, it might - // attempt fixing them multiple times. - // @estebank - Applicability::Unspecified, - ); - } - } - ty::BindByValue(..) => { - if let (Some(local_ty), is_implicit_self) = self.local_ty(hir_id) { - if let Some(msg) = - self.suggest_mut_for_immutable(local_ty, is_implicit_self) { - db.span_label(local_ty.span, msg); - } - } - } - } - } - Some(ImmutabilityBlame::AdtFieldDeref(_, field)) => { - let hir_id = match self.tcx.hir().as_local_hir_id(field.did) { - Some(hir_id) => hir_id, - None => return - }; - - if let Node::Field(ref field) = self.tcx.hir().get(hir_id) { - if let Some(msg) = self.suggest_mut_for_immutable(&field.ty, false) { - db.span_label(field.ty.span, msg); - } - } - } - } - } - - // Suggest a fix when trying to mutably borrow an immutable local - // binding: either to make the binding mutable (if its type is - // not a mutable reference) or to avoid borrowing altogether - fn note_immutable_local(&self, - db: &mut DiagnosticBuilder<'_>, - borrowed_hir_id: hir::HirId, - binding_hir_id: hir::HirId) { - let let_span = self.tcx.hir().span(binding_hir_id); - if let ty::BindByValue(..) = self.local_binding_mode(binding_hir_id) { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(let_span) { - let (ty, is_implicit_self) = self.local_ty(binding_hir_id); - if is_implicit_self && snippet != "self" { - // avoid suggesting `mut &self`. - return - } - if let Some(&hir::TyKind::Rptr( - _, - hir::MutTy { - mutbl: hir::MutMutable, - .. - }, - )) = ty.map(|t| &t.node) - { - let borrow_expr_id = self.tcx.hir().get_parent_node(borrowed_hir_id); - db.span_suggestion( - self.tcx.hir().span(borrow_expr_id), - "consider removing the `&mut`, as it is an \ - immutable binding to a mutable reference", - snippet, - Applicability::MachineApplicable, - ); - } else { - db.span_suggestion( - let_span, - "make this binding mutable", - format!("mut {}", snippet), - Applicability::MachineApplicable, - ); - } - } - } - } - - fn report_out_of_scope_escaping_closure_capture(&self, - err: &BckError<'a, 'tcx>, - capture_span: Span) - { - let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt); - - let suggestion = - match self.tcx.sess.source_map().span_to_snippet(err.span) { - Ok(string) => format!("move {}", string), - Err(_) => "move || ".to_string() - }; - - self.cannot_capture_in_long_lived_closure(err.span, - &cmt_path_or_string, - capture_span, - Origin::Ast) - .span_suggestion( - err.span, - &format!("to force the closure to take ownership of {} \ - (and any other referenced variables), \ - use the `move` keyword", - cmt_path_or_string), - suggestion, - Applicability::MachineApplicable, - ) - .emit(); - self.signal_error(); - } - - fn region_end_span(&self, region: ty::Region<'tcx>) -> Option { - match *region { - ty::ReScope(scope) => { - Some(self.tcx.sess.source_map().end_point( - scope.span(self.tcx, &self.region_scope_tree))) - } - _ => None - } - } - - fn note_and_explain_mutbl_error(&self, db: &mut DiagnosticBuilder<'_>, err: &BckError<'a, 'tcx>, - error_span: &Span) { - match err.cmt.note { - mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => { - // If this is an `Fn` closure, it simply can't mutate upvars. - // If it's an `FnMut` closure, the original variable was declared immutable. - // We need to determine which is the case here. - let kind = match err.cmt.upvar_cat().unwrap() { - Categorization::Upvar(mc::Upvar { kind, .. }) => kind, - _ => bug!() - }; - if *kind == ty::ClosureKind::Fn { - let closure_hir_id = - self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id); - db.span_help(self.tcx.hir().span(closure_hir_id), - "consider changing this closure to take \ - self by mutable reference"); - } - } - _ => { - if let Categorization::Deref(..) = err.cmt.cat { - db.span_label(*error_span, "cannot borrow as mutable"); - } else if let Categorization::Local(local_id) = err.cmt.cat { - let span = self.tcx.hir().span(local_id); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - if snippet.starts_with("ref mut ") || snippet.starts_with("&mut ") { - db.span_label(*error_span, "cannot reborrow mutably"); - db.span_label(*error_span, "try removing `&mut` here"); - } else { - db.span_label(*error_span, "cannot borrow mutably"); - } - } else { - db.span_label(*error_span, "cannot borrow mutably"); - } - } else if let Categorization::Interior(ref cmt, _) = err.cmt.cat { - if let mc::MutabilityCategory::McImmutable = cmt.mutbl { - db.span_label(*error_span, - "cannot mutably borrow field of immutable binding"); - } - } - } - } - } - 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) - } - } - } -} diff --git a/src/librustc_borrowck/error_codes.rs b/src/librustc_borrowck/error_codes.rs deleted file mode 100644 index 44d8a23fcb9..00000000000 --- a/src/librustc_borrowck/error_codes.rs +++ /dev/null @@ -1 +0,0 @@ -#![allow(non_snake_case)] diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 9a8473e1409..d4c30dc6c45 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -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" } diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 51b161c3768..af000376044 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -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; diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index 2712355d537..a0efec5ee7a 100644 --- a/src/librustc_interface/Cargo.toml +++ b/src/librustc_interface/Cargo.toml @@ -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" } diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 9a5eb2b93d5..bb58d134989 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -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; diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index f1aaa857dd3..bce6761e2ca 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -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>( self, @@ -39,32 +18,19 @@ pub trait BorrowckErrors<'cx>: Sized + Copy { fn struct_span_err>(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, - 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, - 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, - 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, 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, - 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, - 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, - 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>(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 - } }