use rustc::middle::free_region::RegionRelations;
use rustc::ty::{self, TyCtxt};
use rustc::ty::maps::Providers;
-
+use rustc::util::nodemap::FxHashMap;
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use std::fmt;
let body_id = tcx.hir.body_owned_by(owner_id);
let tables = tcx.typeck_tables_of(owner_def_id);
let region_maps = tcx.region_maps(owner_def_id);
- let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
-
- let body = bccx.tcx.hir.body(body_id);
+ let body = tcx.hir.body(body_id);
+ let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
// Eventually, borrowck will always read the MIR, but at the
// moment we do not. So, for now, we always force MIR to be
{
// Check the body of fn items.
let tcx = this.tcx;
- let body = tcx.hir.body(body_id);
let id_range = {
let mut visitor = intravisit::IdRangeComputingVisitor::new(&tcx.hir);
- visitor.visit_body(body);
+ visitor.visit_body(this.body);
visitor.result()
};
let (all_loans, move_data) =
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
"borrowck",
- Some(body),
+ Some(this.body),
cfg,
LoanDataFlowOperator,
id_range,
loan.kill_scope.node_id(), loan_idx);
}
loan_dfcx.add_kills_from_flow_exits(cfg);
- loan_dfcx.propagate(cfg, body);
+ loan_dfcx.propagate(cfg, this.body);
let flowed_moves = move_data::FlowedMoveData::new(move_data,
this,
cfg,
id_range,
- body);
+ this.body);
Some(AnalysisData { all_loans,
loans: loan_dfcx,
let owner_def_id = tcx.hir.local_def_id(owner_id);
let tables = tcx.typeck_tables_of(owner_def_id);
let region_maps = tcx.region_maps(owner_def_id);
- let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
+ let body = tcx.hir.body(body_id);
+ let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
(bccx, dataflow_data.unwrap())
region_maps: Rc<RegionMaps>,
owner_def_id: DefId,
+
+ body: &'tcx hir::Body,
}
impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> {
let closure_id = tcx.hir.def_index_to_node_id(closure_id);
match tcx.hir.get(closure_id) {
hir_map::NodeExpr(expr) => match expr.node {
- hir::ExprClosure(.., body_id, _) => {
+ hir::ExprClosure(.., body_id, _, _) => {
body_id.node_id
}
_ => {
_ => { }
}
- let mut db = self.bckerr_to_diag(&err);
- self.note_and_explain_bckerr(&mut db, err);
- db.emit();
+ self.report_bckerr(&err);
}
pub fn report_use_of_moved_value(&self,
move_data::Captured =>
(match self.tcx.hir.expect_expr(the_move.id).node {
- hir::ExprClosure(.., fn_decl_span) => fn_decl_span,
+ hir::ExprClosure(.., fn_decl_span, _) => fn_decl_span,
ref r => bug!("Captured({}) maps to non-closure: {:?}",
the_move.id, r),
}, " (into closure)"),
self.tcx.sess.span_err_with_code(s, msg, code);
}
- fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
- let span = err.span.clone();
+ fn report_bckerr(&self, err: &BckError<'tcx>) {
+ let error_span = err.span.clone();
match err.code {
err_mutbl => {
}
};
- match err.cause {
+ let mut db = match err.cause {
MutabilityViolation => {
- struct_span_err!(self.tcx.sess, span, E0594, "cannot assign to {}", descr)
+ struct_span_err!(self.tcx.sess,
+ error_span,
+ E0594,
+ "cannot assign to {}",
+ descr)
}
BorrowViolation(euv::ClosureCapture(_)) => {
- struct_span_err!(self.tcx.sess, span, E0595,
+ struct_span_err!(self.tcx.sess, error_span, E0595,
"closure cannot assign to {}", descr)
}
BorrowViolation(euv::OverloadedOperator) |
BorrowViolation(euv::AutoUnsafe) |
BorrowViolation(euv::ForLoop) |
BorrowViolation(euv::MatchDiscriminant) => {
- struct_span_err!(self.tcx.sess, span, E0596,
+ struct_span_err!(self.tcx.sess, error_span, E0596,
"cannot borrow {} as mutable", descr)
}
BorrowViolation(euv::ClosureInvocation) => {
span_bug!(err.span,
"err_mutbl with a closure invocation");
}
- }
+ };
+
+ self.note_and_explain_mutbl_error(&mut db, &err, &error_span);
+ self.note_immutability_blame(&mut db, err.cmt.immutability_blame());
+ db.emit();
}
- err_out_of_scope(..) => {
+ 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))
}
};
- struct_span_err!(self.tcx.sess, span, E0597, "{} does not live long enough", msg)
+
+ // When you have a borrow that lives across a yield,
+ // that reference winds up captured in the generator
+ // type. Regionck then constraints it to live as long
+ // as the generator itself. If that borrow is borrowing
+ // data owned by the generator, this winds up resulting in
+ // an `err_out_of_scope` error:
+ //
+ // ```
+ // {
+ // let g = || {
+ // let a = &3; // this borrow is forced to ... -+
+ // yield (); // |
+ // println!("{}", a); // |
+ // }; // |
+ // } <----------------------... live until here --------+
+ // ```
+ //
+ // To detect this case, we look for cases where the
+ // `super_scope` (lifetime of the value) is within the
+ // body, but the `sub_scope` is not.
+ debug!("err_out_of_scope: self.body.is_generator = {:?}",
+ self.body.is_generator);
+ let maybe_borrow_across_yield = if self.body.is_generator {
+ let body_extent = region::CodeExtent::Misc(self.body.id().node_id);
+ debug!("err_out_of_scope: body_extent = {:?}", body_extent);
+ debug!("err_out_of_scope: super_scope = {:?}", super_scope);
+ debug!("err_out_of_scope: sub_scope = {:?}", sub_scope);
+ match (super_scope, sub_scope) {
+ (&ty::RegionKind::ReScope(value_extent),
+ &ty::RegionKind::ReScope(loan_extent)) => {
+ if {
+ // value_extent <= body_extent &&
+ self.region_maps.is_subscope_of(value_extent, body_extent) &&
+ // body_extent <= loan_extent
+ self.region_maps.is_subscope_of(body_extent, loan_extent)
+ } {
+ // We now know that this is a case
+ // that fits the bill described above:
+ // a borrow of something whose scope
+ // is within the generator, but the
+ // borrow is for a scope outside the
+ // generator.
+ //
+ // Now look within the scope of the of
+ // the value being borrowed (in the
+ // example above, that would be the
+ // block remainder that starts with
+ // `let a`) for a yield. We can cite
+ // that for the user.
+ self.tcx.yield_in_extent(value_extent, &mut FxHashMap())
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ } else {
+ None
+ };
+
+ if let Some(yield_span) = maybe_borrow_across_yield {
+ debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span);
+ struct_span_err!(self.tcx.sess,
+ error_span,
+ E0626,
+ "borrow may still be in use when generator yields")
+ .span_label(yield_span, "possible yield occurs here")
+ .emit();
+ return;
+ }
+
+ let mut db = struct_span_err!(self.tcx.sess,
+ error_span,
+ E0597,
+ "{} does not live long enough",
+ msg);
+
+ let (value_kind, value_msg) = match err.cmt.cat {
+ mc::Categorization::Rvalue(..) =>
+ ("temporary value", "temporary value created here"),
+ _ =>
+ ("borrowed value", "borrow occurs here")
+ };
+
+ 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, "does not live long enough");
+ true
+ }
+ None => false
+ }
+ }
+ _ => {
+ db.span_label(error_span, "does not live long enough");
+ 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 {
+ db.span = MultiSpan::from_span(s1);
+ db.span_label(error_span, value_msg);
+ 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 => {
+ db.span = MultiSpan::from_span(s2);
+ db.span_label(error_span, value_msg);
+ 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(
+ &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(
+ &mut db,
+ "...but borrowed value is only valid for ",
+ super_scope,
+ "");
+ }
+ }
+ }
+ }
+
+ if let Some(_) = statement_scope_span(self.tcx, super_scope) {
+ db.note("consider using a `let` binding to increase its lifetime");
+ }
+
+ db.emit();
}
- err_borrowed_pointer_too_short(..) => {
+ err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
let descr = self.cmt_to_path_or_string(&err.cmt);
- struct_span_err!(self.tcx.sess, span, E0598,
- "lifetime of {} is too short to guarantee \
- its contents can be safely reborrowed",
- descr)
+ let mut db = struct_span_err!(self.tcx.sess, error_span, E0598,
+ "lifetime of {} is too short to guarantee \
+ its contents can be safely reborrowed",
+ descr);
+
+ let descr = match opt_loan_path(&err.cmt) {
+ Some(lp) => {
+ format!("`{}`", self.loan_path_to_string(&lp))
+ }
+ None => self.cmt_to_string(&err.cmt),
+ };
+ self.tcx.note_and_explain_region(
+ &mut db,
+ &format!("{} would have to be valid for ",
+ descr),
+ loan_scope,
+ "...");
+ self.tcx.note_and_explain_region(
+ &mut db,
+ &format!("...but {} is only valid for ", descr),
+ ptr_scope,
+ "");
+
+ db.emit();
}
}
}
}
}
- fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) {
- let error_span = err.span.clone();
- match err.code {
- err_mutbl => {
- self.note_and_explain_mutbl_error(db, &err, &error_span);
- self.note_immutability_blame(db, err.cmt.immutability_blame());
- }
- err_out_of_scope(super_scope, sub_scope, cause) => {
- let (value_kind, value_msg) = match err.cmt.cat {
- mc::Categorization::Rvalue(..) =>
- ("temporary value", "temporary value created here"),
- _ =>
- ("borrowed value", "borrow occurs here")
- };
-
- 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, "does not live long enough");
- true
- }
- None => false
- }
- }
- _ => {
- db.span_label(error_span, "does not live long enough");
- 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 {
- db.span = MultiSpan::from_span(s1);
- db.span_label(error_span, value_msg);
- 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 => {
- db.span = MultiSpan::from_span(s2);
- db.span_label(error_span, value_msg);
- 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(
- 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(
- db,
- "...but borrowed value is only valid for ",
- super_scope,
- "");
- }
- }
- }
- }
-
- if let Some(_) = statement_scope_span(self.tcx, super_scope) {
- db.note("consider using a `let` binding to increase its lifetime");
- }
- }
-
- err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
- let descr = match opt_loan_path(&err.cmt) {
- Some(lp) => {
- format!("`{}`", self.loan_path_to_string(&lp))
- }
- None => self.cmt_to_string(&err.cmt),
- };
- self.tcx.note_and_explain_region(
- db,
- &format!("{} would have to be valid for ",
- descr),
- loan_scope,
- "...");
- self.tcx.note_and_explain_region(
- db,
- &format!("...but {} is only valid for ", descr),
- ptr_scope,
- "");
- }
- }
- }
-
fn note_and_explain_mutbl_error(&self, db: &mut DiagnosticBuilder, err: &BckError<'tcx>,
error_span: &Span) {
match err.cmt.note {