fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
check_loans_in_pat(self, p);
}
- fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl,
- b: &ast::Block, s: Span, n: ast::NodeId, _: ()) {
- check_loans_in_fn(self, fk, fd, b, s, n);
+ fn visit_fn(&mut self, _fk: &visit::FnKind, _fd: &ast::FnDecl,
+ _b: &ast::Block, _s: Span, _n: ast::NodeId, _: ()) {
+ // Don't process nested items or closures here,
+ // the outer loop will take care of it.
+ return;
}
// FIXME(#10894) should continue recursing
loan2.repr(self.tcx()));
// Restrictions that would cause the new loan to be illegal:
- let illegal_if = match loan2.mutbl {
- MutableMutability => RESTR_FREEZE | RESTR_CLAIM,
- ImmutableMutability => RESTR_FREEZE,
+ let illegal_if = match loan2.kind {
+ // Look for restrictions against mutation. These are
+ // generated by all other borrows.
+ ty::MutBorrow => RESTR_MUTATE,
+
+ // Look for restrictions against freezing (immutable borrows).
+ // These are generated by `&mut` borrows.
+ ty::ImmBorrow => RESTR_FREEZE,
+
+ // No matter how the data is borrowed (as `&`, as `&mut`,
+ // or as `&unique imm`) it will always generate a
+ // restriction against mutating the data. So look for those.
+ ty::UniqueImmBorrow => RESTR_MUTATE,
};
debug!("illegal_if={:?}", illegal_if);
if !restr.set.intersects(illegal_if) { continue; }
if restr.loan_path != loan2.loan_path { continue; }
- match (new_loan.mutbl, old_loan.mutbl) {
- (_, MutableMutability) => {
- let var = self.bccx.loan_path_to_str(new_loan.loan_path);
+ let old_pronoun = if new_loan.loan_path == old_loan.loan_path {
+ ~"it"
+ } else {
+ format!("`{}`",
+ self.bccx.loan_path_to_str(old_loan.loan_path))
+ };
+
+ match (new_loan.kind, old_loan.kind) {
+ (ty::MutBorrow, ty::MutBorrow) => {
self.bccx.span_err(
new_loan.span,
- format!("cannot borrow `{}` because it is already \
- borrowed as mutable", var));
- self.bccx.span_note(
- old_loan.span,
- format!("previous borrow of `{0}` as mutable occurs \
- here; the mutable borrow prevents subsequent \
- moves, borrows, or modification of `{0}` \
- until the borrow ends", var));
+ format!("cannot borrow `{}` as mutable \
+ more than once at a time",
+ self.bccx.loan_path_to_str(new_loan.loan_path)));
+ }
+
+ (ty::UniqueImmBorrow, _) => {
+ self.bccx.span_err(
+ new_loan.span,
+ format!("closure requires unique access to `{}` \
+ but {} is already borrowed",
+ self.bccx.loan_path_to_str(new_loan.loan_path),
+ old_pronoun));
}
- (_, mutability) => {
+ (_, ty::UniqueImmBorrow) => {
self.bccx.span_err(
new_loan.span,
format!("cannot borrow `{}` as {} because \
- it is already borrowed as {}",
- self.bccx.loan_path_to_str(new_loan.loan_path),
- self.bccx.mut_to_str(new_loan.mutbl),
- self.bccx.mut_to_str(old_loan.mutbl)));
-
- let var = self.bccx.loan_path_to_str(new_loan.loan_path);
- let mut note = format!("previous borrow of `{}` occurs \
- here", var);
- if mutability == ImmutableMutability {
- note.push_str(format!("; the immutable borrow prevents \
- subsequent moves or mutable
- borrows of `{}` until the
- borrow ends", var));
- }
- self.bccx.span_note(old_loan.span, note);
+ previous closure requires unique access",
+ self.bccx.loan_path_to_str(new_loan.loan_path),
+ new_loan.kind.to_user_str()));
+ }
+
+ (_, _) => {
+ self.bccx.span_err(
+ new_loan.span,
+ format!("cannot borrow `{}` as {} because \
+ {} is also borrowed as {}",
+ self.bccx.loan_path_to_str(new_loan.loan_path),
+ new_loan.kind.to_user_str(),
+ old_pronoun,
+ old_loan.kind.to_user_str()));
}
}
+ match new_loan.cause {
+ ClosureCapture(span) => {
+ self.bccx.span_note(
+ span,
+ format!("borrow occurs due to use of `{}` in closure",
+ self.bccx.loan_path_to_str(new_loan.loan_path)));
+ }
+ _ => { }
+ }
+
+ let rule_summary = match old_loan.kind {
+ ty::MutBorrow => {
+ format!("the mutable borrow prevents subsequent \
+ moves, borrows, or modification of `{0}` \
+ until the borrow ends",
+ self.bccx.loan_path_to_str(old_loan.loan_path))
+ }
+
+ ty::ImmBorrow => {
+ format!("the immutable borrow prevents subsequent \
+ moves or mutable borrows of `{0}` \
+ until the borrow ends",
+ self.bccx.loan_path_to_str(old_loan.loan_path))
+ }
+
+ ty::UniqueImmBorrow => {
+ format!("the unique capture prevents subsequent \
+ moves or borrows of `{0}` \
+ until the borrow ends",
+ self.bccx.loan_path_to_str(old_loan.loan_path))
+ }
+ };
+
+ let borrow_summary = match old_loan.cause {
+ ClosureCapture(_) => {
+ format!("previous borrow of `{}` occurs here due to \
+ use in closure",
+ self.bccx.loan_path_to_str(old_loan.loan_path))
+ }
+
+ AddrOf | AutoRef | RefBinding => {
+ format!("previous borrow of `{}` occurs here",
+ self.bccx.loan_path_to_str(old_loan.loan_path))
+ }
+ };
+
+ self.bccx.span_note(
+ old_loan.span,
+ format!("{}; {}", borrow_summary, rule_summary));
+
let old_loan_span = ast_map::node_span(self.tcx().items,
old_loan.kill_scope);
self.bccx.span_end_note(old_loan_span,
"previous borrow ends here");
+
return false;
}
}
// Otherwise, just a plain error.
- self.bccx.span_err(
- expr.span,
- format!("cannot assign to {} {}",
- cmt.mutbl.to_user_str(),
- self.bccx.cmt_to_str(cmt)));
+ match opt_loan_path(cmt) {
+ Some(lp) => {
+ self.bccx.span_err(
+ expr.span,
+ format!("cannot assign to {} {} `{}`",
+ cmt.mutbl.to_user_str(),
+ self.bccx.cmt_to_str(cmt),
+ self.bccx.loan_path_to_str(lp)));
+ }
+ None => {
+ self.bccx.span_err(
+ expr.span,
+ format!("cannot assign to {} {}",
+ cmt.mutbl.to_user_str(),
+ self.bccx.cmt_to_str(cmt)));
+ }
+ }
return;
fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
return;
}
- mc::cat_stack_upvar(b) => {
- cmt = b;
+ mc::cat_upvar(..) => {
+ return;
}
- mc::cat_deref(_, _, mc::gc_ptr) => {
+ mc::cat_deref(_, _, mc::GcPtr) => {
assert_eq!(cmt.mutbl, mc::McImmutable);
return;
}
mc::cat_rvalue(..) |
mc::cat_static_item |
mc::cat_copied_upvar(..) |
- mc::cat_deref(_, _, mc::unsafe_ptr(..)) |
- mc::cat_deref(_, _, mc::region_ptr(..)) => {
+ mc::cat_deref(_, _, mc::UnsafePtr(..)) |
+ mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
assert_eq!(cmt.mutbl, mc::McDeclared);
return;
}
mc::cat_discr(b, _) |
- mc::cat_deref(b, _, mc::uniq_ptr) => {
+ mc::cat_deref(b, _, mc::OwnedPtr) => {
assert_eq!(cmt.mutbl, mc::McInherited);
cmt = b;
}
mc::cat_downcast(b) |
mc::cat_interior(b, _) => {
- if cmt.mutbl == mc::McInherited {
- cmt = b;
- } else {
- return; // field declared as mutable or some such
- }
+ assert_eq!(cmt.mutbl, mc::McInherited);
+ cmt = b;
}
}
}
debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
match guarantor.cat {
- mc::cat_deref(b, _, mc::region_ptr(ast::MutMutable, _)) => {
+ mc::cat_deref(b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
// Statically prohibit writes to `&mut` when aliasable
check_for_aliasability_violation(this, expr, b);
// with inherited mutability and with `&mut`
// pointers.
LpExtend(lp_base, mc::McInherited, _) |
- LpExtend(lp_base, _, LpDeref(mc::region_ptr(ast::MutMutable, _))) => {
+ LpExtend(lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => {
loan_path = lp_base;
}
// Check for a non-const loan of `loan_path`
let cont = this.each_in_scope_loan(expr.id, |loan| {
if loan.loan_path == loan_path {
- this.report_illegal_mutation(expr,
- full_loan_path,
- loan);
+ this.report_illegal_mutation(expr, full_loan_path, loan);
false
} else {
true
fn check_move_out_from_expr(&self, expr: &ast::Expr) {
match expr.node {
ast::ExprFnBlock(..) | ast::ExprProc(..) => {
- // moves due to capture clauses are checked
- // in `check_loans_in_fn`, so that we can
- // give a better error message
+ // Moves due to captures are checked in
+ // check_captured_variables() because it allows
+ // us to give a more precise error message with
+ // a more precise span.
}
_ => {
self.check_move_out_from_id(expr.id, expr.span)
self.bccx.span_err(
span,
format!("cannot move out of `{}` \
- because it is borrowed",
+ because it is borrowed",
self.bccx.loan_path_to_str(move_path)));
self.bccx.span_note(
loan_span,
format!("borrow of `{}` occurs here",
- self.bccx.loan_path_to_str(loan_path)));
+ self.bccx.loan_path_to_str(loan_path)));
}
}
true
});
}
+ fn check_captured_variables(&self,
+ closure_id: ast::NodeId,
+ span: Span) {
+ let capture_map = self.bccx.capture_map.borrow();
+ let cap_vars = capture_map.get().get(&closure_id);
+ for cap_var in cap_vars.borrow().iter() {
+ let var_id = ast_util::def_id_of_def(cap_var.def).node;
+ let var_path = @LpVar(var_id);
+ self.check_if_path_is_moved(closure_id, span,
+ MovedInCapture, var_path);
+ match cap_var.mode {
+ moves::CapRef | moves::CapCopy => {}
+ moves::CapMove => {
+ check_by_move_capture(self, closure_id, cap_var, var_path);
+ }
+ }
+ }
+ return;
+
+ fn check_by_move_capture(this: &CheckLoanCtxt,
+ closure_id: ast::NodeId,
+ cap_var: &moves::CaptureVar,
+ move_path: @LoanPath) {
+ let move_err = this.analyze_move_out_from(closure_id, move_path);
+ match move_err {
+ MoveOk => {}
+ MoveWhileBorrowed(loan_path, loan_span) => {
+ this.bccx.span_err(
+ cap_var.span,
+ format!("cannot move `{}` into closure \
+ because it is borrowed",
+ this.bccx.loan_path_to_str(move_path)));
+ this.bccx.span_note(
+ loan_span,
+ format!("borrow of `{}` occurs here",
+ this.bccx.loan_path_to_str(loan_path)));
+ }
+ }
+ }
+ }
+
pub fn analyze_move_out_from(&self,
expr_id: ast::NodeId,
mut move_path: @LoanPath)
}
}
-fn check_loans_in_fn<'a>(this: &mut CheckLoanCtxt<'a>,
- fk: &visit::FnKind,
- decl: &ast::FnDecl,
- body: &ast::Block,
- sp: Span,
- id: ast::NodeId) {
- match *fk {
- visit::FkItemFn(..) | visit::FkMethod(..) => {
- // Don't process nested items.
- return;
- }
-
- visit::FkFnBlock(..) => {
- check_captured_variables(this, id, sp);
- }
- }
-
- visit::walk_fn(this, fk, decl, body, sp, id, ());
-
- fn check_captured_variables(this: &CheckLoanCtxt,
- closure_id: ast::NodeId,
- span: Span) {
- let capture_map = this.bccx.capture_map.borrow();
- let cap_vars = capture_map.get().get(&closure_id);
- for cap_var in cap_vars.borrow().iter() {
- let var_id = ast_util::def_id_of_def(cap_var.def).node;
- let var_path = @LpVar(var_id);
- this.check_if_path_is_moved(closure_id, span,
- MovedInCapture, var_path);
- match cap_var.mode {
- moves::CapRef | moves::CapCopy => {}
- moves::CapMove => {
- check_by_move_capture(this, closure_id, cap_var, var_path);
- }
- }
- }
- return;
-
- fn check_by_move_capture(this: &CheckLoanCtxt,
- closure_id: ast::NodeId,
- cap_var: &moves::CaptureVar,
- move_path: @LoanPath) {
- let move_err = this.analyze_move_out_from(closure_id, move_path);
- match move_err {
- MoveOk => {}
- MoveWhileBorrowed(loan_path, loan_span) => {
- this.bccx.span_err(
- cap_var.span,
- format!("cannot move `{}` into closure \
- because it is borrowed",
- this.bccx.loan_path_to_str(move_path)));
- this.bccx.span_note(
- loan_span,
- format!("borrow of `{}` occurs here",
- this.bccx.loan_path_to_str(loan_path)));
- }
- }
- }
- }
-}
-
fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>,
local: &ast::Local) {
visit::walk_local(this, local, ());
}
}
}
+ ast::ExprFnBlock(..) | ast::ExprProc(..) => {
+ this.check_captured_variables(expr.id, expr.span)
+ }
ast::ExprAssign(dest, _) |
ast::ExprAssignOp(_, _, dest, _) => {
this.check_assignment(dest);
use middle::moves;
use middle::ty;
use syntax::ast;
-use syntax::ast_util;
use syntax::codemap::Span;
-use util::ppaux::{UserString};
+use util::ppaux::{Repr, UserString};
pub fn gather_decl(bccx: &BorrowckCtxt,
move_data: &MoveData,
move_data: &MoveData,
move_expr: &ast::Expr,
cmt: mc::cmt) {
- gather_move_from_expr_or_pat(bccx, move_data, move_expr.id, MoveExpr, cmt);
+ gather_move(bccx, move_data, move_expr.id, MoveExpr, cmt);
}
pub fn gather_move_from_pat(bccx: &BorrowckCtxt,
move_data: &MoveData,
move_pat: &ast::Pat,
cmt: mc::cmt) {
- gather_move_from_expr_or_pat(bccx, move_data, move_pat.id, MovePat, cmt);
-}
-
-fn gather_move_from_expr_or_pat(bccx: &BorrowckCtxt,
- move_data: &MoveData,
- move_id: ast::NodeId,
- move_kind: MoveKind,
- cmt: mc::cmt) {
- if !check_is_legal_to_move_from(bccx, cmt, cmt) {
- return;
- }
-
- match opt_loan_path(cmt) {
- Some(loan_path) => {
- move_data.add_move(bccx.tcx, loan_path, move_id, move_kind);
- }
- None => {
- // move from rvalue or unsafe pointer, hence ok
- }
- }
+ gather_move(bccx, move_data, move_pat.id, MovePat, cmt);
}
pub fn gather_captures(bccx: &BorrowckCtxt,
for captured_var in captured_vars.borrow().iter() {
match captured_var.mode {
moves::CapMove => {
- let fvar_id = ast_util::def_id_of_def(captured_var.def).node;
- let loan_path = @LpVar(fvar_id);
- move_data.add_move(bccx.tcx, loan_path, closure_expr.id,
- Captured);
+ let cmt = bccx.cat_captured_var(closure_expr.id,
+ closure_expr.span,
+ captured_var);
+ gather_move(bccx, move_data, closure_expr.id, Captured, cmt);
}
moves::CapCopy | moves::CapRef => {}
}
}
}
+fn gather_move(bccx: &BorrowckCtxt,
+ move_data: &MoveData,
+ move_id: ast::NodeId,
+ move_kind: MoveKind,
+ cmt: mc::cmt) {
+ debug!("gather_move(move_id={}, cmt={})",
+ move_id, cmt.repr(bccx.tcx));
+
+ if !check_is_legal_to_move_from(bccx, cmt, cmt) {
+ return;
+ }
+
+ match opt_loan_path(cmt) {
+ Some(loan_path) => {
+ move_data.add_move(bccx.tcx, loan_path, move_id, move_kind);
+ }
+ None => {
+ // move from rvalue or unsafe pointer, hence ok
+ }
+ }
+}
+
pub fn gather_assignment(bccx: &BorrowckCtxt,
move_data: &MoveData,
assignment_id: ast::NodeId,
cmt0: mc::cmt,
cmt: mc::cmt) -> bool {
match cmt.cat {
- mc::cat_deref(_, _, mc::region_ptr(..)) |
- mc::cat_deref(_, _, mc::gc_ptr) |
- mc::cat_deref(_, _, mc::unsafe_ptr(..)) |
- mc::cat_stack_upvar(..) |
+ mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
+ mc::cat_deref(_, _, mc::GcPtr) |
+ mc::cat_deref(_, _, mc::UnsafePtr(..)) |
+ mc::cat_upvar(..) |
mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => {
bccx.span_err(
cmt0.span,
format!("cannot move out of {}",
- bccx.cmt_to_str(cmt)));
+ bccx.cmt_to_str(cmt)));
false
}
}
}
- mc::cat_deref(b, _, mc::uniq_ptr) |
+ mc::cat_deref(b, _, mc::OwnedPtr) |
mc::cat_discr(b, _) => {
check_is_legal_to_move_from(bccx, cmt0, b)
}
item_scope_id: ast::NodeId,
root_scope_id: ast::NodeId,
span: Span,
+ cause: LoanCause,
cmt: mc::cmt,
loan_region: ty::Region,
- loan_mutbl: LoanMutability) -> R {
+ loan_kind: ty::BorrowKind)
+ -> Result<(),()> {
debug!("guarantee_lifetime(cmt={}, loan_region={})",
cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx));
let ctxt = GuaranteeLifetimeContext {bccx: bccx,
item_scope_id: item_scope_id,
span: span,
+ cause: cause,
loan_region: loan_region,
- loan_mutbl: loan_mutbl,
+ loan_kind: loan_kind,
cmt_original: cmt,
root_scope_id: root_scope_id};
ctxt.check(cmt, None)
root_scope_id: ast::NodeId,
span: Span,
+ cause: LoanCause,
loan_region: ty::Region,
- loan_mutbl: LoanMutability,
+ loan_kind: ty::BorrowKind,
cmt_original: mc::cmt
}
mc::cat_copied_upvar(..) | // L-Local
mc::cat_local(..) | // L-Local
mc::cat_arg(..) | // L-Local
- mc::cat_deref(_, _, mc::region_ptr(..)) | // L-Deref-Borrowed
- mc::cat_deref(_, _, mc::unsafe_ptr(..)) => {
+ mc::cat_upvar(..) |
+ mc::cat_deref(_, _, mc::BorrowedPtr(..)) | // L-Deref-Borrowed
+ mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
let scope = self.scope(cmt);
self.check_scope(scope)
}
- mc::cat_stack_upvar(cmt) => {
- self.check(cmt, discr_scope)
- }
-
mc::cat_static_item => {
Ok(())
}
- mc::cat_deref(base, derefs, mc::gc_ptr) => {
+ mc::cat_deref(base, derefs, mc::GcPtr) => {
let base_scope = self.scope(base);
// L-Deref-Managed-Imm-User-Root
}
mc::cat_downcast(base) |
- mc::cat_deref(base, _, mc::uniq_ptr) | // L-Deref-Send
+ mc::cat_deref(base, _, mc::OwnedPtr) | // L-Deref-Send
mc::cat_interior(base, _) => { // L-Field
self.check(base, discr_scope)
}
mc::cat_rvalue(..) |
mc::cat_static_item |
mc::cat_copied_upvar(..) |
- mc::cat_deref(..) => {
+ mc::cat_deref(..) |
+ mc::cat_upvar(..) => {
false
}
r @ mc::cat_downcast(..) |
r @ mc::cat_interior(..) |
- r @ mc::cat_stack_upvar(..) |
r @ mc::cat_discr(..) => {
self.tcx().sess.span_bug(
cmt.span,
mc::cat_rvalue(temp_scope) => {
temp_scope
}
+ mc::cat_upvar(..) |
mc::cat_copied_upvar(_) => {
ty::ReScope(self.item_scope_id)
}
mc::cat_arg(local_id) => {
ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id))
}
- mc::cat_deref(_, _, mc::unsafe_ptr(..)) => {
+ mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
ty::ReStatic
}
- mc::cat_deref(_, _, mc::region_ptr(_, r)) => {
+ mc::cat_deref(_, _, mc::BorrowedPtr(_, r)) => {
r
}
mc::cat_downcast(cmt) |
- mc::cat_deref(cmt, _, mc::uniq_ptr) |
- mc::cat_deref(cmt, _, mc::gc_ptr) |
+ mc::cat_deref(cmt, _, mc::OwnedPtr) |
+ mc::cat_deref(cmt, _, mc::GcPtr) |
mc::cat_interior(cmt, _) |
- mc::cat_stack_upvar(cmt) |
mc::cat_discr(cmt, _) => {
self.scope(cmt)
}
}
fn report_error(&self, code: bckerr_code) {
- self.bccx.report(BckError {
- cmt: self.cmt_original,
- span: self.span,
- code: code
- });
+ self.bccx.report(BckError { cmt: self.cmt_original,
+ span: self.span,
+ cause: self.cause,
+ code: code });
}
}
// their associated scopes. In phase two, checking loans, we will then make
// sure that all of these loans are honored.
-
use middle::borrowck::*;
use middle::borrowck::move_data::MoveData;
use mc = middle::mem_categorization;
+use middle::moves;
use middle::pat_util;
use middle::ty::{ty_region};
use middle::ty;
use std::cell::RefCell;
use syntax::ast;
+use syntax::ast_util;
use syntax::ast_util::IdRange;
use syntax::codemap::Span;
use syntax::print::pprust;
visit::walk_pat(this, p, ());
}
-fn gather_loans_in_fn(this: &mut GatherLoanCtxt, fk: &FnKind,
- decl: &ast::FnDecl, body: &ast::Block,
- sp: Span, id: ast::NodeId) {
- match fk {
- &visit::FkItemFn(..) | &visit::FkMethod(..) => {
- fail!("cannot occur, due to visit_item override");
- }
-
- // Visit closures as part of the containing item.
- &visit::FkFnBlock(..) => {
- this.push_repeating_id(body.id);
- visit::walk_fn(this, fk, decl, body, sp, id, ());
- this.pop_repeating_id(body.id);
- this.gather_fn_arg_patterns(decl, body);
- }
- }
+fn gather_loans_in_fn(_v: &mut GatherLoanCtxt,
+ _fk: &FnKind,
+ _decl: &ast::FnDecl,
+ _body: &ast::Block,
+ _sp: Span,
+ _id: ast::NodeId) {
+ // Do not visit closures or fn items here, the outer loop in
+ // borrowck/mod will visit them for us in turn.
+ return;
}
fn gather_loans_in_block(this: &mut GatherLoanCtxt,
this.guarantee_valid(ex.id,
ex.span,
base_cmt,
- LoanMutability::from_ast_mutability(mutbl),
- scope_r);
+ mutbl,
+ scope_r,
+ AddrOf);
}
visit::walk_expr(this, ex, ());
}
this.guarantee_valid(arg.id,
arg.span,
arg_cmt,
- ImmutableMutability,
- scope_r);
+ ast::MutImmutable,
+ scope_r,
+ AutoRef);
visit::walk_expr(this, ex, ());
}
ast::ExprFnBlock(..) | ast::ExprProc(..) => {
gather_moves::gather_captures(this.bccx, &this.move_data, ex);
+ this.guarantee_captures(ex);
visit::walk_expr(this, ex, ());
}
ty::AutoDerefRef {
autoref: Some(ref autoref),
autoderefs: autoderefs}) => {
- let mcx = &mc::mem_categorization_ctxt {
- tcx: self.tcx(),
- method_map: self.bccx.method_map};
- let cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
+ let mut mc = self.bccx.mc();
+ let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) {
+ Ok(v) => v,
+ Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
+ };
debug!("after autoderef, cmt={}", cmt.repr(self.tcx()));
match *autoref {
ty::AutoPtr(r, m) => {
- let loan_mutability =
- LoanMutability::from_ast_mutability(m);
self.guarantee_valid(expr.id,
expr.span,
cmt,
- loan_mutability,
- r)
+ m,
+ r,
+ AutoRef)
}
ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
- let cmt_index = mcx.cat_index(expr, cmt, autoderefs+1);
- let loan_mutability =
- LoanMutability::from_ast_mutability(m);
+ let cmt_index = mc.cat_index(expr, cmt, autoderefs+1);
self.guarantee_valid(expr.id,
expr.span,
cmt_index,
- loan_mutability,
- r)
+ m,
+ r,
+ AutoRef)
}
ty::AutoBorrowFn(r) => {
- let cmt_deref = mcx.cat_deref_fn_or_obj(expr, cmt, 0);
+ let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0);
self.guarantee_valid(expr.id,
expr.span,
cmt_deref,
- ImmutableMutability,
- r)
+ ast::MutImmutable,
+ r,
+ AutoRef)
}
ty::AutoBorrowObj(r, m) => {
- let cmt_deref = mcx.cat_deref_fn_or_obj(expr, cmt, 0);
- let loan_mutability =
- LoanMutability::from_ast_mutability(m);
+ let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0);
self.guarantee_valid(expr.id,
expr.span,
cmt_deref,
- loan_mutability,
- r)
+ m,
+ r,
+ AutoRef)
}
ty::AutoUnsafe(_) => {}
}
}
}
- // Guarantees that addr_of(cmt) will be valid for the duration of
- // `static_scope_r`, or reports an error. This may entail taking
- // out loans, which will be added to the `req_loan_map`. This can
- // also entail "rooting" GC'd pointers, which means ensuring
- // dynamically that they are not freed.
+ fn guarantee_captures(&mut self,
+ closure_expr: &ast::Expr) {
+ let capture_map = self.bccx.capture_map.borrow();
+ let captured_vars = capture_map.get().get(&closure_expr.id);
+ for captured_var in captured_vars.borrow().iter() {
+ match captured_var.mode {
+ moves::CapCopy | moves::CapMove => { continue; }
+ moves::CapRef => { }
+ }
+
+ let var_id = ast_util::def_id_of_def(captured_var.def).node;
+ let var_cmt = self.bccx.cat_captured_var(closure_expr.id,
+ closure_expr.span,
+ captured_var);
+
+ // Lookup the kind of borrow the callee requires
+ let upvar_id = ty::UpvarId { var_id: var_id,
+ closure_expr_id: closure_expr.id };
+ let upvar_borrow_map = self.tcx().upvar_borrow_map.borrow();
+ let upvar_borrow = upvar_borrow_map.get().get_copy(&upvar_id);
+
+ self.guarantee_valid_kind(closure_expr.id,
+ closure_expr.span,
+ var_cmt,
+ upvar_borrow.kind,
+ upvar_borrow.region,
+ ClosureCapture(captured_var.span));
+ }
+ }
+
pub fn guarantee_valid(&mut self,
borrow_id: ast::NodeId,
borrow_span: Span,
cmt: mc::cmt,
- req_mutbl: LoanMutability,
- loan_region: ty::Region) {
+ req_mutbl: ast::Mutability,
+ loan_region: ty::Region,
+ cause: LoanCause) {
+ self.guarantee_valid_kind(borrow_id,
+ borrow_span,
+ cmt,
+ ty::BorrowKind::from_mutbl(req_mutbl),
+ loan_region,
+ cause);
+ }
+
+ fn guarantee_valid_kind(&mut self,
+ borrow_id: ast::NodeId,
+ borrow_span: Span,
+ cmt: mc::cmt,
+ req_kind: ty::BorrowKind,
+ loan_region: ty::Region,
+ cause: LoanCause) {
+ /*!
+ * Guarantees that `addr_of(cmt)` will be valid for the duration of
+ * `static_scope_r`, or reports an error. This may entail taking
+ * out loans, which will be added to the `req_loan_map`. This can
+ * also entail "rooting" GC'd pointers, which means ensuring
+ * dynamically that they are not freed.
+ */
+
debug!("guarantee_valid(borrow_id={:?}, cmt={}, \
req_mutbl={:?}, loan_region={:?})",
borrow_id,
cmt.repr(self.tcx()),
- req_mutbl,
+ req_kind,
loan_region);
// a loan for the empty region can never be dereferenced, so
// 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, root_ub,
- borrow_span, cmt, loan_region,
- req_mutbl).is_err() {
+ borrow_span, cause, cmt, loan_region,
+ req_kind).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, cmt, req_mutbl).is_err() {
+ if check_mutability(self.bccx, borrow_span, cause,
+ 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, cmt, req_mutbl).is_err() {
+ if check_aliasability(self.bccx, borrow_span, cause,
+ 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,
- cmt, loan_region, self.restriction_set(req_mutbl));
+ self.bccx, borrow_span, cause,
+ cmt, loan_region, self.restriction_set(req_kind));
// Create the loan record (if needed).
let loan = match restr {
let kill_scope = self.compute_kill_scope(loan_scope, loan_path);
debug!("kill_scope = {:?}", kill_scope);
- if req_mutbl == MutableMutability {
+ if req_kind == ty::MutBorrow {
self.mark_loan_path_as_mutated(loan_path);
}
index: all_loans.get().len(),
loan_path: loan_path,
cmt: cmt,
- mutbl: req_mutbl,
+ kind: req_kind,
gen_scope: gen_scope,
kill_scope: kill_scope,
span: borrow_span,
- restrictions: restrictions
+ restrictions: restrictions,
+ cause: cause,
}
}
};
fn check_mutability(bccx: &BorrowckCtxt,
borrow_span: Span,
+ cause: LoanCause,
cmt: mc::cmt,
- req_mutbl: LoanMutability) -> Result<(),()> {
+ req_kind: ty::BorrowKind)
+ -> Result<(),()> {
//! Implements the M-* rules in doc.rs.
- match req_mutbl {
- ImmutableMutability => {
- // both imm and mut data can be lent as imm;
- // for mutable data, this is a freeze
- Ok(())
+ match req_kind {
+ ty::UniqueImmBorrow | ty::ImmBorrow => {
+ match cmt.mutbl {
+ // I am intentionally leaving this here to help
+ // refactoring if, in the future, we should add new
+ // kinds of mutability.
+ mc::McImmutable | mc::McDeclared | mc::McInherited => {
+ // both imm and mut data can be lent as imm;
+ // for mutable data, this is a freeze
+ Ok(())
+ }
+ }
}
- MutableMutability => {
+ ty::MutBorrow => {
// Only mutable data can be lent as mutable.
if !cmt.mutbl.is_mutable() {
- Err(bccx.report(BckError {span: borrow_span,
- cmt: cmt,
- code: err_mutbl(req_mutbl)}))
+ Err(bccx.report(BckError { span: borrow_span,
+ cause: cause,
+ cmt: cmt,
+ code: err_mutbl }))
} else {
Ok(())
}
fn check_aliasability(bccx: &BorrowckCtxt,
borrow_span: Span,
+ loan_cause: LoanCause,
cmt: mc::cmt,
- req_mutbl: LoanMutability) -> Result<(),()> {
+ req_kind: ty::BorrowKind)
+ -> Result<(),()> {
//! Implements the A-* rules in doc.rs.
- match req_mutbl {
- ImmutableMutability => {
- // both imm and mut data can be lent as imm;
- // for mutable data, this is a freeze
+ match req_kind {
+ ty::ImmBorrow => {
Ok(())
}
- MutableMutability => {
+ ty::UniqueImmBorrow | ty::MutBorrow => {
// Check for those cases where we cannot control
// the aliasing and make sure that we are not
// being asked to.
// unsafe. At your own peril and all that.
Ok(())
}
- Some(cause) => {
+ Some(alias_cause) => {
bccx.report_aliasability_violation(
borrow_span,
- BorrowViolation,
- cause);
+ BorrowViolation(loan_cause),
+ alias_cause);
Err(())
}
}
}
}
- pub fn restriction_set(&self, req_mutbl: LoanMutability)
- -> RestrictionSet {
- match req_mutbl {
- ImmutableMutability => RESTR_MUTATE | RESTR_CLAIM,
- MutableMutability => RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE,
+ fn restriction_set(&self, req_kind: ty::BorrowKind) -> RestrictionSet {
+ match req_kind {
+ // If borrowing data as immutable, no mutation allowed:
+ ty::ImmBorrow => RESTR_MUTATE,
+
+ // If borrowing data as mutable, no mutation nor other
+ // borrows allowed:
+ ty::MutBorrow => RESTR_MUTATE | RESTR_FREEZE,
+
+ // If borrowing data as unique imm, no mutation nor other
+ // borrows allowed:
+ ty::UniqueImmBorrow => RESTR_MUTATE | RESTR_FREEZE,
}
}
* `gather_pat()`.
*/
- let mc_ctxt = self.bccx.mc_ctxt();
+ let mut mc = self.bccx.mc();
for arg in decl.inputs.iter() {
let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
- let arg_cmt = mc_ctxt.cat_rvalue(
+ let arg_cmt = mc.cat_rvalue(
arg.id,
arg.pat.span,
ty::ReScope(body.id), // Args live only as long as the fn body.
fn gather_pat(&mut self,
discr_cmt: mc::cmt,
- root_pat: &ast::Pat,
+ root_pat: @ast::Pat,
arm_match_ids: Option<(ast::NodeId, ast::NodeId)>) {
/*!
* Walks patterns, examining the bindings to determine if they
}
}
};
- let loan_mutability =
- LoanMutability::from_ast_mutability(mutbl);
self.guarantee_valid(pat.id,
pat.span,
cmt_discr,
- loan_mutability,
- scope_r);
+ mutbl,
+ scope_r,
+ RefBinding);
}
ast::BindByValue(_) => {
// No borrows here, but there may be moves
// original vector. This is effectively a borrow of
// the elements of the vector being matched.
- let slice_ty = ty::node_id_to_type(self.tcx(),
- slice_pat.id);
- let (slice_mutbl, slice_r) =
- self.vec_slice_info(slice_pat, slice_ty);
- let mcx = self.bccx.mc_ctxt();
- let cmt_index = mcx.cat_index(slice_pat, cmt, 0);
- let slice_loan_mutability =
- LoanMutability::from_ast_mutability(slice_mutbl);
+ let (slice_cmt, slice_borrow_kind, slice_r) = {
+ match self.bccx.mc().cat_slice_pattern(cmt, slice_pat) {
+ Ok(v) => v,
+ Err(()) => {
+ self.tcx().sess.span_bug(slice_pat.span,
+ "Err from mc")
+ }
+ }
+ };
// Note: We declare here that the borrow occurs upon
// entering the `[...]` pattern. This implies that
// trans do the right thing, and it would only work
// for `~` vectors. It seems simpler to just require
// that people call `vec.pop()` or `vec.unshift()`.
- self.guarantee_valid(pat.id,
- pat.span,
- cmt_index,
- slice_loan_mutability,
- slice_r);
+ self.guarantee_valid(pat.id, pat.span,
+ slice_cmt, slice_borrow_kind, slice_r,
+ RefBinding);
}
_ => {}
})
}
- pub fn vec_slice_info(&self, pat: &ast::Pat, slice_ty: ty::t)
- -> (ast::Mutability, ty::Region) {
- /*!
- *
- * In a pattern like [a, b, ..c], normally `c` has slice type,
- * but if you have [a, b, ..ref c], then the type of `ref c`
- * will be `&&[]`, so to extract the slice details we have
- * to recurse through rptrs.
- */
-
- match ty::get(slice_ty).sty {
- ty::ty_vec(slice_mt, ty::vstore_slice(slice_r)) => {
- (slice_mt.mutbl, slice_r)
- }
-
- ty::ty_rptr(_, ref mt) => {
- self.vec_slice_info(pat, mt.ty)
- }
-
- _ => {
- self.tcx().sess.span_bug(
- pat.span,
- format!("type of slice pattern is not a slice"));
- }
- }
- }
-
pub fn pat_is_binding(&self, pat: &ast::Pat) -> bool {
pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
}
use middle::borrowck::*;
use mc = middle::mem_categorization;
use middle::ty;
-use syntax::ast::{MutImmutable, MutMutable};
use syntax::codemap::Span;
+use util::ppaux::Repr;
pub enum RestrictionResult {
Safe,
pub fn compute_restrictions(bccx: &BorrowckCtxt,
span: Span,
+ cause: LoanCause,
cmt: mc::cmt,
loan_region: ty::Region,
restr: RestrictionSet) -> RestrictionResult {
let ctxt = RestrictionsContext {
bccx: bccx,
span: span,
+ cause: cause,
cmt_original: cmt,
loan_region: loan_region,
};
span: Span,
cmt_original: mc::cmt,
loan_region: ty::Region,
+ cause: LoanCause,
}
impl<'a> RestrictionsContext<'a> {
fn restrict(&self,
cmt: mc::cmt,
restrictions: RestrictionSet) -> RestrictionResult {
+ debug!("restrict(cmt={}, restrictions={})",
+ cmt.repr(self.bccx.tcx),
+ restrictions.repr(self.bccx.tcx));
+
match cmt.cat {
mc::cat_rvalue(..) => {
// Effectively, rvalues are stored into a
}
mc::cat_local(local_id) |
- mc::cat_arg(local_id) => {
+ mc::cat_arg(local_id) |
+ mc::cat_upvar(ty::UpvarId {var_id: local_id, ..}, _) => {
// R-Variable
let lp = @LpVar(local_id);
SafeIf(lp, ~[Restriction {loan_path: lp,
// could cause the type of the memory to change.
self.restrict(
cmt_base,
- restrictions | RESTR_MUTATE | RESTR_CLAIM)
+ restrictions | RESTR_MUTATE)
}
mc::cat_interior(cmt_base, i) => {
self.extend(result, cmt.mutbl, LpInterior(i), restrictions)
}
- mc::cat_deref(cmt_base, _, pk @ mc::uniq_ptr) => {
+ mc::cat_deref(cmt_base, _, pk @ mc::OwnedPtr) => {
// R-Deref-Send-Pointer
//
// When we borrow the interior of an owned pointer, we
// would cause the unique pointer to be freed.
let result = self.restrict(
cmt_base,
- restrictions | RESTR_MUTATE | RESTR_CLAIM);
+ restrictions | RESTR_MUTATE);
self.extend(result, cmt.mutbl, LpDeref(pk), restrictions)
}
Safe
}
- mc::cat_deref(cmt_base, _, mc::region_ptr(MutImmutable, lt)) => {
+ mc::cat_deref(cmt_base, _, mc::BorrowedPtr(ty::ImmBorrow, lt)) |
+ mc::cat_deref(cmt_base, _, mc::BorrowedPtr(ty::UniqueImmBorrow, lt)) => {
// R-Deref-Imm-Borrowed
if !self.bccx.is_subregion_of(self.loan_region, lt) {
self.bccx.report(
BckError {
span: self.span,
+ cause: self.cause,
cmt: cmt_base,
code: err_borrowed_pointer_too_short(
self.loan_region, lt, restrictions)});
Safe
}
- mc::cat_deref(_, _, mc::gc_ptr) => {
+ mc::cat_deref(_, _, mc::GcPtr) => {
// R-Deref-Imm-Managed
Safe
}
- mc::cat_deref(cmt_base, _, pk @ mc::region_ptr(MutMutable, lt)) => {
+ mc::cat_deref(cmt_base, _, pk @ mc::BorrowedPtr(ty::MutBorrow, lt)) => {
// R-Deref-Mut-Borrowed
if !self.bccx.is_subregion_of(self.loan_region, lt) {
self.bccx.report(
BckError {
span: self.span,
+ cause: self.cause,
cmt: cmt_base,
code: err_borrowed_pointer_too_short(
self.loan_region, lt, restrictions)});
self.extend(result, cmt.mutbl, LpDeref(pk), restrictions)
}
- mc::cat_deref(_, _, mc::unsafe_ptr(..)) => {
+ mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
// We are very trusting when working with unsafe pointers.
Safe
}
- mc::cat_stack_upvar(cmt_base) |
mc::cat_discr(cmt_base, _) => {
self.restrict(cmt_base, restrictions)
}
body: &ast::Block,
sp: Span,
id: ast::NodeId) {
- match fk {
- &visit::FkFnBlock(..) => {
- // Closures are checked as part of their containing fn item.
- }
-
- &visit::FkItemFn(..) | &visit::FkMethod(..) => {
- debug!("borrowck_fn(id={:?})", id);
-
- // Check the body of fn items.
- let (id_range, all_loans, move_data) =
- gather_loans::gather_loans(this, decl, body);
-
- let all_loans = all_loans.borrow();
- let mut loan_dfcx = DataFlowContext::new(this.tcx,
- this.method_map,
- LoanDataFlowOperator,
- id_range,
- all_loans.get().len());
- for (loan_idx, loan) in all_loans.get().iter().enumerate() {
- loan_dfcx.add_gen(loan.gen_scope, loan_idx);
- loan_dfcx.add_kill(loan.kill_scope, loan_idx);
- }
-
- loan_dfcx.propagate(body);
-
- let flowed_moves = move_data::FlowedMoveData::new(move_data,
- this.tcx,
- this.method_map,
- id_range,
- body);
-
- check_loans::check_loans(this, &loan_dfcx, flowed_moves,
- *all_loans.get(), body);
- }
- }
+ debug!("borrowck_fn(id={})", id);
+
+ // Check the body of fn items.
+ let (id_range, all_loans, move_data) =
+ gather_loans::gather_loans(this, decl, body);
+ let all_loans = all_loans.borrow();
+ let mut loan_dfcx =
+ DataFlowContext::new(this.tcx,
+ this.method_map,
+ LoanDataFlowOperator,
+ id_range,
+ all_loans.get().len());
+ for (loan_idx, loan) in all_loans.get().iter().enumerate() {
+ loan_dfcx.add_gen(loan.gen_scope, loan_idx);
+ loan_dfcx.add_kill(loan.kill_scope, loan_idx);
+ }
+ loan_dfcx.propagate(body);
+
+ let flowed_moves = move_data::FlowedMoveData::new(move_data,
+ this.tcx,
+ this.method_map,
+ id_range,
+ body);
+
+ check_loans::check_loans(this, &loan_dfcx, flowed_moves,
+ *all_loans.get(), body);
visit::walk_fn(this, fk, decl, body, sp, id, ());
}
///////////////////////////////////////////////////////////////////////////
// Loans and loan paths
-#[deriving(Clone, Eq)]
-pub enum LoanMutability {
- ImmutableMutability,
- MutableMutability,
-}
-
-impl LoanMutability {
- pub fn from_ast_mutability(ast_mutability: ast::Mutability)
- -> LoanMutability {
- match ast_mutability {
- ast::MutImmutable => ImmutableMutability,
- ast::MutMutable => MutableMutability,
- }
- }
-}
-
-impl ToStr for LoanMutability {
- fn to_str(&self) -> ~str {
- match *self {
- ImmutableMutability => ~"immutable",
- MutableMutability => ~"mutable",
- }
- }
-}
-
/// Record of a loan that was issued.
pub struct Loan {
index: uint,
loan_path: @LoanPath,
cmt: mc::cmt,
- mutbl: LoanMutability,
+ kind: ty::BorrowKind,
restrictions: ~[Restriction],
gen_scope: ast::NodeId,
kill_scope: ast::NodeId,
span: Span,
+ cause: LoanCause,
+}
+
+#[deriving(Eq)]
+pub enum LoanCause {
+ ClosureCapture(Span),
+ AddrOf,
+ AutoRef,
+ RefBinding,
}
#[deriving(Eq, IterBytes)]
None
}
- mc::cat_local(id) | mc::cat_arg(id) => {
+ mc::cat_local(id) |
+ mc::cat_arg(id) |
+ mc::cat_upvar(ty::UpvarId {var_id: id, ..}, _) => {
Some(@LpVar(id))
}
}
mc::cat_downcast(cmt_base) |
- mc::cat_stack_upvar(cmt_base) |
mc::cat_discr(cmt_base, _) => {
opt_loan_path(cmt_base)
}
// Borrowing an lvalue often results in *restrictions* that limit what
// can be done with this lvalue during the scope of the loan:
//
-// - `RESTR_MUTATE`: The lvalue may not be modified.
-// - `RESTR_CLAIM`: `&mut` borrows of the lvalue are forbidden.
+// - `RESTR_MUTATE`: The lvalue may not be modified or `&mut` borrowed.
// - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
//
// In addition, no value which is restricted may be moved. Therefore,
pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000};
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
-pub static RESTR_CLAIM: RestrictionSet = RestrictionSet {bits: 0b0010};
-pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0100};
+pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0010};
impl RestrictionSet {
pub fn intersects(&self, restr: RestrictionSet) -> bool {
}
}
+impl Repr for RestrictionSet {
+ fn repr(&self, _tcx: ty::ctxt) -> ~str {
+ format!("RestrictionSet(0x{:x})", self.bits as uint)
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// Rooting of managed boxes
//
// Errors that can occur
#[deriving(Eq)]
pub enum bckerr_code {
- err_mutbl(LoanMutability),
+ err_mutbl,
err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
- err_freeze_aliasable_const,
err_borrowed_pointer_too_short(
ty::Region, ty::Region, RestrictionSet), // loan, ptr
}
#[deriving(Eq)]
pub struct BckError {
span: Span,
+ cause: LoanCause,
cmt: mc::cmt,
code: bckerr_code
}
pub enum AliasableViolationKind {
MutabilityViolation,
- BorrowViolation
+ BorrowViolation(LoanCause)
}
pub enum MovedValueUseKind {
moves_map.get().contains(&id)
}
+ pub fn mc(&self) -> mc::MemCategorizationContext<TcxTyper> {
+ mc::MemCategorizationContext {
+ typer: TcxTyper {
+ tcx: self.tcx,
+ method_map: self.method_map
+ }
+ }
+ }
+
pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt {
- mc::cat_expr(self.tcx, self.method_map, expr)
+ match self.mc().cat_expr(expr) {
+ Ok(c) => c,
+ Err(()) => {
+ self.tcx.sess.span_bug(expr.span, "error in mem categorization");
+ }
+ }
}
pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> mc::cmt {
- mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
+ match self.mc().cat_expr_unadjusted(expr) {
+ Ok(c) => c,
+ Err(()) => {
+ self.tcx.sess.span_bug(expr.span, "error in mem categorization");
+ }
+ }
}
pub fn cat_expr_autoderefd(&self,
expr: &ast::Expr,
adj: &ty::AutoAdjustment)
-> mc::cmt {
- match *adj {
+ let r = match *adj {
ty::AutoAddEnv(..) | ty::AutoObject(..) => {
// no autoderefs
- mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
+ self.mc().cat_expr_unadjusted(expr)
}
ty::AutoDerefRef(
ty::AutoDerefRef {
autoderefs: autoderefs, ..}) => {
- mc::cat_expr_autoderefd(self.tcx, self.method_map, expr,
- autoderefs)
+ self.mc().cat_expr_autoderefd(expr, autoderefs)
+ }
+ };
+
+ match r {
+ Ok(c) => c,
+ Err(()) => {
+ self.tcx.sess.span_bug(expr.span,
+ "error in mem categorization");
}
}
}
ty: ty::t,
def: ast::Def)
-> mc::cmt {
- mc::cat_def(self.tcx, self.method_map, id, span, ty, def)
+ match self.mc().cat_def(id, span, ty, def) {
+ Ok(c) => c,
+ Err(()) => {
+ self.tcx.sess.span_bug(span, "error in mem categorization");
+ }
+ }
+ }
+
+ pub fn cat_captured_var(&self,
+ id: ast::NodeId,
+ span: Span,
+ captured_var: &moves::CaptureVar) -> mc::cmt {
+ // Create the cmt for the variable being borrowed, from the
+ // caller's perspective
+ let var_id = ast_util::def_id_of_def(captured_var.def).node;
+ let var_ty = ty::node_id_to_type(self.tcx, var_id);
+ self.cat_def(id, span, var_ty, captured_var.def)
}
pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt {
..*cmt}
}
- pub fn mc_ctxt(&self) -> mc::mem_categorization_ctxt {
- mc::mem_categorization_ctxt {tcx: self.tcx,
- method_map: self.method_map}
- }
-
pub fn cat_pattern(&self,
cmt: mc::cmt,
- pat: &ast::Pat,
+ pat: @ast::Pat,
op: |mc::cmt, &ast::Pat|) {
- let mc = self.mc_ctxt();
- mc.cat_pattern(cmt, pat, op);
+ let r = self.mc().cat_pattern(cmt, pat, |_,x,y| op(x,y));
+ assert!(r.is_ok());
}
pub fn report(&self, err: BckError) {
pub fn bckerr_to_str(&self, err: BckError) -> ~str {
match err.code {
- err_mutbl(lk) => {
- format!("cannot borrow {} {} as {}",
- err.cmt.mutbl.to_user_str(),
- self.cmt_to_str(err.cmt),
- self.mut_to_str(lk))
+ err_mutbl => {
+ let descr = match opt_loan_path(err.cmt) {
+ None => format!("{} {}",
+ err.cmt.mutbl.to_user_str(),
+ self.cmt_to_str(err.cmt)),
+ Some(lp) => format!("{} {} `{}`",
+ err.cmt.mutbl.to_user_str(),
+ self.cmt_to_str(err.cmt),
+ self.loan_path_to_str(lp)),
+ };
+
+ match err.cause {
+ ClosureCapture(_) => {
+ format!("closure cannot assign to {}", descr)
+ }
+ AddrOf | RefBinding | AutoRef => {
+ format!("cannot borrow {} as mutable", descr)
+ }
+ }
}
err_out_of_root_scope(..) => {
format!("cannot root managed value long enough")
}
err_out_of_scope(..) => {
- format!("borrowed value does not live long enough")
- }
- err_freeze_aliasable_const => {
- // Means that the user borrowed a ~T or enum value
- // residing in &const or @const pointer. Terrible
- // error message, but then &const and @const are
- // supposed to be going away.
- format!("unsafe borrow of aliasable, const value")
+ let msg = match opt_loan_path(err.cmt) {
+ None => format!("borrowed value"),
+ Some(lp) => format!("`{}`", self.loan_path_to_str(lp)),
+ };
+ format!("{} does not live long enough", msg)
}
err_borrowed_pointer_too_short(..) => {
let descr = match opt_loan_path(err.cmt) {
kind: AliasableViolationKind,
cause: mc::AliasableReason) {
let prefix = match kind {
- MutabilityViolation => "cannot assign to data",
- BorrowViolation => "cannot borrow data mutably"
+ MutabilityViolation => {
+ "cannot assign to data"
+ }
+ BorrowViolation(ClosureCapture(_)) => {
+ // I don't think we can get aliasability violations
+ // with closure captures, so no need to come up with a
+ // good error message. The reason this cannot happen
+ // is because we only capture local variables in
+ // closures, and those are never aliasable.
+ self.tcx.sess.span_bug(
+ span,
+ "aliasability violation with closure");
+ }
+ BorrowViolation(AddrOf) |
+ BorrowViolation(AutoRef) |
+ BorrowViolation(RefBinding) => {
+ "cannot borrow data mutably"
+ }
};
match cause {
span,
format!("{} in a `@` pointer", prefix));
}
- mc::AliasableBorrowed(_) => {
+ mc::AliasableBorrowed => {
self.tcx.sess.span_err(
span,
format!("{} in a `&` reference", prefix));
pub fn note_and_explain_bckerr(&self, err: BckError) {
let code = err.code;
match code {
- err_mutbl(..) | err_freeze_aliasable_const(..) => {}
+ err_mutbl(..) => { }
err_out_of_root_scope(super_scope, sub_scope) => {
note_and_explain_region(
}
}
- pub fn append_loan_path_to_str_from_interior(&self,
- loan_path: &LoanPath,
- out: &mut ~str) {
- match *loan_path {
- LpExtend(_, _, LpDeref(_)) => {
- out.push_char('(');
- self.append_loan_path_to_str(loan_path, out);
- out.push_char(')');
- }
- LpExtend(_, _, LpInterior(_)) |
- LpVar(_) => {
- self.append_loan_path_to_str(loan_path, out);
- }
- }
- }
-
pub fn append_loan_path_to_str(&self,
loan_path: &LoanPath,
out: &mut ~str) {
match *loan_path {
LpVar(id) => {
- match self.tcx.items.find(id) {
- Some(ast_map::NodeLocal(pat)) => {
- match pat.node {
- ast::PatIdent(_, ref path, _) => {
- let ident = ast_util::path_to_ident(path);
- let string = token::get_ident(ident.name);
- out.push_str(string.get());
- }
- _ => {
- self.tcx.sess.bug(
- format!("loan path LpVar({:?}) maps to {:?}, not local",
- id, pat));
- }
- }
- }
- r => {
- self.tcx.sess.bug(
- format!("loan path LpVar({:?}) maps to {:?}, not local",
- id, r));
- }
- }
+ out.push_str(ty::local_var_name_str(self.tcx, id).get());
}
LpExtend(lp_base, _, LpInterior(mc::InteriorField(fname))) => {
- self.append_loan_path_to_str_from_interior(lp_base, out);
+ self.append_autoderefd_loan_path_to_str(lp_base, out);
match fname {
mc::NamedField(ref fname) => {
let string = token::get_ident(*fname);
}
LpExtend(lp_base, _, LpInterior(mc::InteriorElement(_))) => {
- self.append_loan_path_to_str_from_interior(lp_base, out);
- out.push_str("[]");
+ self.append_autoderefd_loan_path_to_str(lp_base, out);
+ out.push_str("[..]");
}
LpExtend(lp_base, _, LpDeref(_)) => {
}
}
+ pub fn append_autoderefd_loan_path_to_str(&self,
+ loan_path: &LoanPath,
+ out: &mut ~str) {
+ match *loan_path {
+ LpExtend(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_str(lp_base, out)
+ }
+
+ LpVar(..) | LpExtend(_, _, LpInterior(..)) => {
+ self.append_loan_path_to_str(loan_path, out)
+ }
+ }
+ }
+
pub fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str {
let mut result = ~"";
self.append_loan_path_to_str(loan_path, &mut result);
}
pub fn cmt_to_str(&self, cmt: mc::cmt) -> ~str {
- let mc = &mc::mem_categorization_ctxt {tcx: self.tcx,
- method_map: self.method_map};
- mc.cmt_to_str(cmt)
+ self.mc().cmt_to_str(cmt)
}
- pub fn mut_to_str(&self, mutbl: LoanMutability) -> ~str {
- mutbl.to_str()
+ pub fn mut_to_str(&self, mutbl: ast::Mutability) -> ~str {
+ self.mc().mut_to_str(mutbl)
}
pub fn mut_to_keyword(&self, mutbl: ast::Mutability) -> &'static str {
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // loans from both preds are in scope
}
-
- #[inline]
- fn walk_closures(&self) -> bool {
- true
- }
}
impl Repr for Loan {
format!("Loan_{:?}({}, {:?}, {:?}-{:?}, {})",
self.index,
self.loan_path.repr(tcx),
- self.mutbl,
+ self.kind,
self.gen_scope,
self.kill_scope,
self.restrictions.repr(tcx))
}
}
}
+
+///////////////////////////////////////////////////////////////////////////
+
+struct TcxTyper {
+ tcx: ty::ctxt,
+ method_map: typeck::method_map,
+}
+
+impl mc::Typer for TcxTyper {
+ fn tcx(&self) -> ty::ctxt {
+ self.tcx
+ }
+
+ fn node_ty(&mut self, id: ast::NodeId) -> mc::McResult<ty::t> {
+ Ok(ty::node_id_to_type(self.tcx, id))
+ }
+
+ fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> {
+ let adjustments = self.tcx.adjustments.borrow();
+ adjustments.get().find_copy(&id)
+ }
+
+ fn is_method_call(&mut self, id: ast::NodeId) -> bool {
+ let method_map = self.method_map.borrow();
+ method_map.get().contains_key(&id)
+ }
+
+ fn temporary_scope(&mut self, id: ast::NodeId) -> Option<ast::NodeId> {
+ self.tcx.region_maps.temporary_scope(id)
+ }
+
+ fn upvar_borrow(&mut self, id: ty::UpvarId) -> ty::UpvarBorrow {
+ let upvar_borrow_map = self.tcx.upvar_borrow_map.borrow();
+ upvar_borrow_map.get().get_copy(&id)
+ }
+}
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // moves from both preds are in scope
}
-
- #[inline]
- fn walk_closures(&self) -> bool {
- true
- }
}
impl DataFlowOperator for AssignDataFlowOperator {
fn join(&self, succ: uint, pred: uint) -> uint {
succ | pred // moves from both preds are in scope
}
-
- #[inline]
- fn walk_closures(&self) -> bool {
- true
- }
}