Modify borrow checker to visit irrefutable patterns that appear in
let and function arguments; modify type checker to store type information for all patterns and handles some missing cases.
This commit is contained in:
parent
2d3262ca7b
commit
729b07f83c
@ -65,7 +65,7 @@ pub fn check_loans(bccx: @BorrowckCtxt,
|
||||
|
||||
enum MoveError {
|
||||
MoveOk,
|
||||
MoveWhileBorrowed(/*move*/@LoanPath, /*loan*/@LoanPath, /*loan*/span)
|
||||
MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span)
|
||||
}
|
||||
|
||||
impl<'self> CheckLoanCtxt<'self> {
|
||||
@ -348,7 +348,7 @@ impl<'self> CheckLoanCtxt<'self> {
|
||||
cmt = b;
|
||||
}
|
||||
|
||||
mc::cat_rvalue |
|
||||
mc::cat_rvalue(*) |
|
||||
mc::cat_static_item |
|
||||
mc::cat_implicit_self |
|
||||
mc::cat_copied_upvar(*) |
|
||||
@ -547,45 +547,50 @@ impl<'self> CheckLoanCtxt<'self> {
|
||||
self.bccx.loan_path_to_str(loan_path)));
|
||||
}
|
||||
|
||||
pub fn check_move_out_from_expr(&self, ex: @ast::expr) {
|
||||
match ex.node {
|
||||
ast::expr_paren(*) => {
|
||||
/* In the case of an expr_paren(), the expression inside
|
||||
* the parens will also be marked as being moved. Ignore
|
||||
* the parents then so as not to report duplicate errors. */
|
||||
fn check_move_out_from_expr(&self, expr: @ast::expr) {
|
||||
match expr.node {
|
||||
ast::expr_fn_block(*) => {
|
||||
// moves due to capture clauses are checked
|
||||
// in `check_loans_in_fn`, so that we can
|
||||
// give a better error message
|
||||
}
|
||||
_ => {
|
||||
let cmt = self.bccx.cat_expr(ex);
|
||||
match self.analyze_move_out_from_cmt(cmt) {
|
||||
MoveOk => {}
|
||||
MoveWhileBorrowed(move_path, loan_path, loan_span) => {
|
||||
self.bccx.span_err(
|
||||
cmt.span,
|
||||
fmt!("cannot move out of `%s` \
|
||||
because it is borrowed",
|
||||
self.bccx.loan_path_to_str(move_path)));
|
||||
self.bccx.span_note(
|
||||
loan_span,
|
||||
fmt!("borrow of `%s` occurs here",
|
||||
self.bccx.loan_path_to_str(loan_path)));
|
||||
}
|
||||
self.check_move_out_from_id(expr.id, expr.span)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_move_out_from_id(&self, id: ast::node_id, span: span) {
|
||||
for self.move_data.each_path_moved_by(id) |_, move_path| {
|
||||
match self.analyze_move_out_from(id, move_path) {
|
||||
MoveOk => {}
|
||||
MoveWhileBorrowed(loan_path, loan_span) => {
|
||||
self.bccx.span_err(
|
||||
span,
|
||||
fmt!("cannot move out of `%s` \
|
||||
because it is borrowed",
|
||||
self.bccx.loan_path_to_str(move_path)));
|
||||
self.bccx.span_note(
|
||||
loan_span,
|
||||
fmt!("borrow of `%s` occurs here",
|
||||
self.bccx.loan_path_to_str(loan_path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
|
||||
debug!("analyze_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
|
||||
pub fn analyze_move_out_from(&self,
|
||||
expr_id: ast::node_id,
|
||||
move_path: @LoanPath) -> MoveError {
|
||||
debug!("analyze_move_out_from(expr_id=%?, move_path=%s)",
|
||||
expr_id, move_path.repr(self.tcx()));
|
||||
|
||||
// FIXME(#4384) inadequare if/when we permit `move a.b`
|
||||
|
||||
// check for a conflicting loan:
|
||||
let r = opt_loan_path(cmt);
|
||||
for r.iter().advance |&lp| {
|
||||
for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
|
||||
// Any restriction prevents moves.
|
||||
return MoveWhileBorrowed(lp, loan.loan_path, loan.span);
|
||||
}
|
||||
for self.each_in_scope_restriction(expr_id, move_path) |loan, _| {
|
||||
// Any restriction prevents moves.
|
||||
return MoveWhileBorrowed(loan.loan_path, loan.span);
|
||||
}
|
||||
|
||||
MoveOk
|
||||
@ -652,13 +657,11 @@ fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
|
||||
closure_id: ast::node_id,
|
||||
cap_var: &moves::CaptureVar) {
|
||||
let var_id = ast_util::def_id_of_def(cap_var.def).node;
|
||||
let ty = ty::node_id_to_type(this.tcx(), var_id);
|
||||
let cmt = this.bccx.cat_def(closure_id, cap_var.span,
|
||||
ty, cap_var.def);
|
||||
let move_err = this.analyze_move_out_from_cmt(cmt);
|
||||
let move_path = @LpVar(var_id);
|
||||
let move_err = this.analyze_move_out_from(closure_id, move_path);
|
||||
match move_err {
|
||||
MoveOk => {}
|
||||
MoveWhileBorrowed(move_path, loan_path, loan_span) => {
|
||||
MoveWhileBorrowed(loan_path, loan_span) => {
|
||||
this.bccx.span_err(
|
||||
cap_var.span,
|
||||
fmt!("cannot move `%s` into closure \
|
||||
@ -689,10 +692,7 @@ fn check_loans_in_expr<'a>(expr: @ast::expr,
|
||||
expr.repr(this.tcx()));
|
||||
|
||||
this.check_for_conflicting_loans(expr.id);
|
||||
|
||||
if this.bccx.moves_map.contains(&expr.id) {
|
||||
this.check_move_out_from_expr(expr);
|
||||
}
|
||||
this.check_move_out_from_expr(expr);
|
||||
|
||||
match expr.node {
|
||||
ast::expr_self |
|
||||
@ -742,18 +742,7 @@ fn check_loans_in_pat<'a>(pat: @ast::pat,
|
||||
visit::vt<@mut CheckLoanCtxt<'a>>))
|
||||
{
|
||||
this.check_for_conflicting_loans(pat.id);
|
||||
|
||||
// Note: moves out of pattern bindings are not checked by
|
||||
// the borrow checker, at least not directly. What happens
|
||||
// is that if there are any moved bindings, the discriminant
|
||||
// will be considered a move, and this will be checked as
|
||||
// normal. Then, in `middle::check_match`, we will check
|
||||
// that no move occurs in a binding that is underneath an
|
||||
// `@` or `&`. Together these give the same guarantees as
|
||||
// `check_move_out_from_expr()` without requiring us to
|
||||
// rewalk the patterns and rebuild the pattern
|
||||
// categorizations.
|
||||
|
||||
this.check_move_out_from_id(pat.id, pat.span);
|
||||
visit::visit_pat(pat, (this, vt));
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ impl GuaranteeLifetimeContext {
|
||||
//! Main routine. Walks down `cmt` until we find the "guarantor".
|
||||
|
||||
match cmt.cat {
|
||||
mc::cat_rvalue |
|
||||
mc::cat_rvalue(*) |
|
||||
mc::cat_implicit_self |
|
||||
mc::cat_copied_upvar(*) | // L-Local
|
||||
mc::cat_local(*) | // L-Local
|
||||
@ -179,7 +179,7 @@ impl GuaranteeLifetimeContext {
|
||||
//! lvalue.
|
||||
|
||||
cmt.mutbl.is_immutable() || match cmt.guarantor().cat {
|
||||
mc::cat_rvalue => true,
|
||||
mc::cat_rvalue(*) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
@ -299,7 +299,7 @@ impl GuaranteeLifetimeContext {
|
||||
mc::cat_arg(id) => {
|
||||
self.bccx.moved_variables_set.contains(&id)
|
||||
}
|
||||
mc::cat_rvalue |
|
||||
mc::cat_rvalue(*) |
|
||||
mc::cat_static_item |
|
||||
mc::cat_implicit_self |
|
||||
mc::cat_copied_upvar(*) |
|
||||
@ -325,8 +325,8 @@ impl GuaranteeLifetimeContext {
|
||||
// See the SCOPE(LV) function in doc.rs
|
||||
|
||||
match cmt.cat {
|
||||
mc::cat_rvalue => {
|
||||
ty::re_scope(self.bccx.tcx.region_maps.cleanup_scope(cmt.id))
|
||||
mc::cat_rvalue(cleanup_scope_id) => {
|
||||
ty::re_scope(cleanup_scope_id)
|
||||
}
|
||||
mc::cat_implicit_self |
|
||||
mc::cat_copied_upvar(_) => {
|
||||
|
@ -73,6 +73,7 @@ struct GatherLoanCtxt {
|
||||
}
|
||||
|
||||
pub fn gather_loans(bccx: @BorrowckCtxt,
|
||||
decl: &ast::fn_decl,
|
||||
body: &ast::blk)
|
||||
-> (id_range, @mut ~[Loan], @mut move_data::MoveData) {
|
||||
let glcx = @mut GatherLoanCtxt {
|
||||
@ -83,6 +84,7 @@ pub fn gather_loans(bccx: @BorrowckCtxt,
|
||||
repeating_ids: ~[body.node.id],
|
||||
move_data: @mut MoveData::new()
|
||||
};
|
||||
glcx.gather_fn_arg_patterns(decl, body);
|
||||
let v = visit::mk_vt(@visit::Visitor {visit_expr: gather_loans_in_expr,
|
||||
visit_block: gather_loans_in_block,
|
||||
visit_fn: gather_loans_in_fn,
|
||||
@ -124,6 +126,7 @@ fn gather_loans_in_fn(fk: &visit::fn_kind,
|
||||
this.push_repeating_id(body.node.id);
|
||||
visit::visit_fn(fk, decl, body, sp, id, (this, v));
|
||||
this.pop_repeating_id(body.node.id);
|
||||
this.gather_fn_arg_patterns(decl, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,26 +141,33 @@ fn gather_loans_in_block(blk: &ast::blk,
|
||||
fn gather_loans_in_local(local: @ast::local,
|
||||
(this, vt): (@mut GatherLoanCtxt,
|
||||
visit::vt<@mut GatherLoanCtxt>)) {
|
||||
if local.node.init.is_none() {
|
||||
// Variable declarations without initializers are considered "moves":
|
||||
let tcx = this.bccx.tcx;
|
||||
do pat_util::pat_bindings(tcx.def_map, local.node.pat) |_, id, span, _| {
|
||||
gather_moves::gather_decl(this.bccx,
|
||||
this.move_data,
|
||||
id,
|
||||
span,
|
||||
id);
|
||||
match local.node.init {
|
||||
None => {
|
||||
// Variable declarations without initializers are considered "moves":
|
||||
let tcx = this.bccx.tcx;
|
||||
do pat_util::pat_bindings(tcx.def_map, local.node.pat)
|
||||
|_, id, span, _| {
|
||||
gather_moves::gather_decl(this.bccx,
|
||||
this.move_data,
|
||||
id,
|
||||
span,
|
||||
id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Variable declarations with initializers are considered "assigns":
|
||||
let tcx = this.bccx.tcx;
|
||||
do pat_util::pat_bindings(tcx.def_map, local.node.pat) |_, id, span, _| {
|
||||
gather_moves::gather_assignment(this.bccx,
|
||||
this.move_data,
|
||||
id,
|
||||
span,
|
||||
@LpVar(id),
|
||||
id);
|
||||
Some(init) => {
|
||||
// Variable declarations with initializers are considered "assigns":
|
||||
let tcx = this.bccx.tcx;
|
||||
do pat_util::pat_bindings(tcx.def_map, local.node.pat)
|
||||
|_, id, span, _| {
|
||||
gather_moves::gather_assignment(this.bccx,
|
||||
this.move_data,
|
||||
id,
|
||||
span,
|
||||
@LpVar(id),
|
||||
id);
|
||||
}
|
||||
let init_cmt = this.bccx.cat_expr(init);
|
||||
this.gather_pat(init_cmt, local.node.pat, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +240,7 @@ fn gather_loans_in_expr(ex: @ast::expr,
|
||||
let cmt = this.bccx.cat_expr(ex_v);
|
||||
for arms.iter().advance |arm| {
|
||||
for arm.pats.iter().advance |pat| {
|
||||
this.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
|
||||
this.gather_pat(cmt, *pat, Some((arm.body.node.id, ex.id)));
|
||||
}
|
||||
}
|
||||
visit::visit_expr(ex, (this, vt));
|
||||
@ -596,11 +606,40 @@ impl GatherLoanCtxt {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gather_pat(&mut self,
|
||||
discr_cmt: mc::cmt,
|
||||
root_pat: @ast::pat,
|
||||
arm_body_id: ast::node_id,
|
||||
match_id: ast::node_id) {
|
||||
fn gather_fn_arg_patterns(&mut self,
|
||||
decl: &ast::fn_decl,
|
||||
body: &ast::blk) {
|
||||
/*!
|
||||
* Walks the patterns for fn arguments, checking that they
|
||||
* do not attempt illegal moves or create refs that outlive
|
||||
* the arguments themselves. Just a shallow wrapper around
|
||||
* `gather_pat()`.
|
||||
*/
|
||||
|
||||
let mc_ctxt = self.bccx.mc_ctxt();
|
||||
for decl.inputs.each |arg| {
|
||||
let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
|
||||
|
||||
let arg_cmt = mc_ctxt.cat_rvalue(
|
||||
arg.id,
|
||||
arg.pat.span,
|
||||
body.node.id, // Arguments live only as long as the fn body.
|
||||
arg_ty);
|
||||
|
||||
self.gather_pat(arg_cmt, arg.pat, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_pat(&mut self,
|
||||
discr_cmt: mc::cmt,
|
||||
root_pat: @ast::pat,
|
||||
arm_match_ids: Option<(ast::node_id, ast::node_id)>) {
|
||||
/*!
|
||||
* Walks patterns, examining the bindings to determine if they
|
||||
* cause borrows (`ref` bindings, vector patterns) or
|
||||
* moves (non-`ref` bindings with linear type).
|
||||
*/
|
||||
|
||||
do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
|
||||
match pat.node {
|
||||
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
|
||||
@ -621,15 +660,19 @@ impl GatherLoanCtxt {
|
||||
// with a cat_discr() node. There is a detailed
|
||||
// discussion of the function of this node in
|
||||
// `lifetime.rs`:
|
||||
let arm_scope = ty::re_scope(arm_body_id);
|
||||
if self.bccx.is_subregion_of(scope_r, arm_scope) {
|
||||
let cmt_discr = self.bccx.cat_discr(cmt, match_id);
|
||||
self.guarantee_valid(pat.id, pat.span,
|
||||
cmt_discr, mutbl, scope_r);
|
||||
} else {
|
||||
self.guarantee_valid(pat.id, pat.span,
|
||||
cmt, mutbl, scope_r);
|
||||
}
|
||||
let cmt_discr = match arm_match_ids {
|
||||
None => cmt,
|
||||
Some((arm_id, match_id)) => {
|
||||
let arm_scope = ty::re_scope(arm_id);
|
||||
if self.bccx.is_subregion_of(scope_r, arm_scope) {
|
||||
self.bccx.cat_discr(cmt, match_id)
|
||||
} else {
|
||||
cmt
|
||||
}
|
||||
}
|
||||
};
|
||||
self.guarantee_valid(pat.id, pat.span,
|
||||
cmt_discr, mutbl, scope_r);
|
||||
}
|
||||
ast::bind_infer => {
|
||||
// No borrows here, but there may be moves
|
||||
@ -652,6 +695,24 @@ impl GatherLoanCtxt {
|
||||
self.vec_slice_info(slice_pat, slice_ty);
|
||||
let mcx = self.bccx.mc_ctxt();
|
||||
let cmt_index = mcx.cat_index(slice_pat, cmt, 0);
|
||||
|
||||
// Note: We declare here that the borrow occurs upon
|
||||
// entering the `[...]` pattern. This implies that
|
||||
// something like `[a, ..b]` where `a` is a move is
|
||||
// illegal, because the borrow is already in effect.
|
||||
// In fact such a move would be safe-ish, but it
|
||||
// effectively *requires* that we use the nulling
|
||||
// out semantics to indicate when a value has been
|
||||
// moved, which we are trying to move away from.
|
||||
// Otherwise, how can we indicate that the first
|
||||
// element in the vector has been moved?
|
||||
// Eventually, we could perhaps modify this rule to
|
||||
// permit `[..a, b]` where `b` is a move, because in
|
||||
// that case we can adjust the length of the
|
||||
// original vec accordingly, but we'd have to make
|
||||
// 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_mutbl, slice_r);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ impl RestrictionsContext {
|
||||
}
|
||||
|
||||
match cmt.cat {
|
||||
mc::cat_rvalue => {
|
||||
mc::cat_rvalue(*) => {
|
||||
// Effectively, rvalues are stored into a
|
||||
// non-aliasable temporary on the stack. Since they
|
||||
// are inherently non-aliasable, they can only be
|
||||
|
@ -124,7 +124,7 @@ fn borrowck_fn(fk: &visit::fn_kind,
|
||||
|
||||
// Check the body of fn items.
|
||||
let (id_range, all_loans, move_data) =
|
||||
gather_loans::gather_loans(this, body);
|
||||
gather_loans::gather_loans(this, decl, body);
|
||||
let mut loan_dfcx =
|
||||
DataFlowContext::new(this.tcx,
|
||||
this.method_map,
|
||||
@ -264,7 +264,7 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
|
||||
//! traverses the CMT.
|
||||
|
||||
match cmt.cat {
|
||||
mc::cat_rvalue |
|
||||
mc::cat_rvalue(*) |
|
||||
mc::cat_static_item |
|
||||
mc::cat_copied_upvar(_) |
|
||||
mc::cat_implicit_self => {
|
||||
@ -485,7 +485,7 @@ impl BorrowckCtxt {
|
||||
|
||||
pub fn mc_ctxt(&self) -> mc::mem_categorization_ctxt {
|
||||
mc::mem_categorization_ctxt {tcx: self.tcx,
|
||||
method_map: self.method_map}
|
||||
method_map: self.method_map}
|
||||
}
|
||||
|
||||
pub fn cat_pattern(&self,
|
||||
|
@ -474,6 +474,24 @@ impl FlowedMoveData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn each_path_moved_by(&self,
|
||||
id: ast::node_id,
|
||||
f: &fn(&Move, @LoanPath) -> bool)
|
||||
-> bool {
|
||||
/*!
|
||||
* Iterates through each path moved by `id`
|
||||
*/
|
||||
|
||||
for self.dfcx_moves.each_gen_bit_frozen(id) |index| {
|
||||
let move = &self.move_data.moves[index];
|
||||
let moved_path = move.path;
|
||||
if !f(move, self.move_data.path(moved_path).loan_path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn each_move_of(&self,
|
||||
id: ast::node_id,
|
||||
loan_path: @LoanPath,
|
||||
|
@ -49,23 +49,13 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
pub fn expr_is_non_moving_lvalue(cx: &MatchCheckCtxt, expr: &expr) -> bool {
|
||||
if !ty::expr_is_lval(cx.tcx, cx.method_map, expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
!cx.moves_map.contains(&expr.id)
|
||||
}
|
||||
|
||||
pub fn check_expr(cx: @MatchCheckCtxt, ex: @expr, (s, v): ((), visit::vt<()>)) {
|
||||
visit::visit_expr(ex, (s, v));
|
||||
match ex.node {
|
||||
expr_match(scrut, ref arms) => {
|
||||
// First, check legality of move bindings.
|
||||
let is_non_moving_lvalue = expr_is_non_moving_lvalue(cx, ex);
|
||||
for arms.iter().advance |arm| {
|
||||
check_legality_of_move_bindings(cx,
|
||||
is_non_moving_lvalue,
|
||||
arm.guard.is_some(),
|
||||
arm.pats);
|
||||
}
|
||||
@ -758,11 +748,7 @@ pub fn check_local(cx: &MatchCheckCtxt,
|
||||
}
|
||||
|
||||
// Check legality of move bindings.
|
||||
let is_lvalue = match loc.node.init {
|
||||
Some(init) => expr_is_non_moving_lvalue(cx, init),
|
||||
None => true
|
||||
};
|
||||
check_legality_of_move_bindings(cx, is_lvalue, false, [ loc.node.pat ]);
|
||||
check_legality_of_move_bindings(cx, false, [ loc.node.pat ]);
|
||||
}
|
||||
|
||||
pub fn check_fn(cx: &MatchCheckCtxt,
|
||||
@ -821,7 +807,6 @@ pub fn is_refutable(cx: &MatchCheckCtxt, pat: &pat) -> bool {
|
||||
// Legality of move bindings checking
|
||||
|
||||
pub fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
|
||||
is_lvalue: bool,
|
||||
has_guard: bool,
|
||||
pats: &[@pat]) {
|
||||
let tcx = cx.tcx;
|
||||
@ -861,11 +846,6 @@ pub fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
|
||||
tcx.sess.span_note(
|
||||
by_ref_span.get(),
|
||||
"by-ref binding occurs here");
|
||||
} else if is_lvalue {
|
||||
tcx.sess.span_err(
|
||||
p.span,
|
||||
"cannot bind by-move when \
|
||||
matching an lvalue");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -422,8 +422,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
|
||||
loop_scopes: &mut ~[LoopScope]) {
|
||||
match decl.node {
|
||||
ast::decl_local(local) => {
|
||||
self.walk_pat(local.node.pat, in_out, loop_scopes);
|
||||
self.walk_opt_expr(local.node.init, in_out, loop_scopes);
|
||||
self.walk_pat(local.node.pat, in_out, loop_scopes);
|
||||
}
|
||||
|
||||
ast::decl_item(_) => {}
|
||||
|
@ -60,7 +60,7 @@ use syntax::print::pprust;
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum categorization {
|
||||
cat_rvalue, // result of eval'ing some misc expr
|
||||
cat_rvalue(ast::node_id), // temporary val, argument is its scope
|
||||
cat_static_item,
|
||||
cat_implicit_self,
|
||||
cat_copied_upvar(CopiedUpvar), // upvar copied into @fn or ~fn env
|
||||
@ -350,7 +350,7 @@ impl mem_categorization_ctxt {
|
||||
// Convert a bare fn to a closure by adding NULL env.
|
||||
// Result is an rvalue.
|
||||
let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
|
||||
self.cat_rvalue(expr, expr_ty)
|
||||
self.cat_rvalue_node(expr, expr_ty)
|
||||
}
|
||||
|
||||
Some(
|
||||
@ -360,7 +360,7 @@ impl mem_categorization_ctxt {
|
||||
// Equivalent to &*expr or something similar.
|
||||
// Result is an rvalue.
|
||||
let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
|
||||
self.cat_rvalue(expr, expr_ty)
|
||||
self.cat_rvalue_node(expr, expr_ty)
|
||||
}
|
||||
|
||||
Some(
|
||||
@ -390,7 +390,7 @@ impl mem_categorization_ctxt {
|
||||
match expr.node {
|
||||
ast::expr_unary(_, ast::deref, e_base) => {
|
||||
if self.method_map.contains_key(&expr.id) {
|
||||
return self.cat_rvalue(expr, expr_ty);
|
||||
return self.cat_rvalue_node(expr, expr_ty);
|
||||
}
|
||||
|
||||
let base_cmt = self.cat_expr(e_base);
|
||||
@ -408,7 +408,7 @@ impl mem_categorization_ctxt {
|
||||
|
||||
ast::expr_index(_, base, _) => {
|
||||
if self.method_map.contains_key(&expr.id) {
|
||||
return self.cat_rvalue(expr, expr_ty);
|
||||
return self.cat_rvalue_node(expr, expr_ty);
|
||||
}
|
||||
|
||||
let base_cmt = self.cat_expr(base);
|
||||
@ -433,7 +433,7 @@ impl mem_categorization_ctxt {
|
||||
ast::expr_match(*) | ast::expr_lit(*) | ast::expr_break(*) |
|
||||
ast::expr_mac(*) | ast::expr_again(*) | ast::expr_struct(*) |
|
||||
ast::expr_repeat(*) | ast::expr_inline_asm(*) => {
|
||||
return self.cat_rvalue(expr, expr_ty);
|
||||
return self.cat_rvalue_node(expr, expr_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -577,11 +577,24 @@ impl mem_categorization_ctxt {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cat_rvalue<N:ast_node>(&self, elt: N, expr_ty: ty::t) -> cmt {
|
||||
pub fn cat_rvalue_node<N:ast_node>(&self,
|
||||
node: N,
|
||||
expr_ty: ty::t) -> cmt {
|
||||
self.cat_rvalue(node.id(),
|
||||
node.span(),
|
||||
self.tcx.region_maps.cleanup_scope(node.id()),
|
||||
expr_ty)
|
||||
}
|
||||
|
||||
pub fn cat_rvalue(&self,
|
||||
cmt_id: ast::node_id,
|
||||
span: span,
|
||||
cleanup_scope_id: ast::node_id,
|
||||
expr_ty: ty::t) -> cmt {
|
||||
@cmt_ {
|
||||
id:elt.id(),
|
||||
span:elt.span(),
|
||||
cat:cat_rvalue,
|
||||
id:cmt_id,
|
||||
span:span,
|
||||
cat:cat_rvalue(cleanup_scope_id),
|
||||
mutbl:McDeclared,
|
||||
ty:expr_ty
|
||||
}
|
||||
@ -970,7 +983,7 @@ impl mem_categorization_ctxt {
|
||||
}
|
||||
for slice.iter().advance |&slice_pat| {
|
||||
let slice_ty = self.pat_ty(slice_pat);
|
||||
let slice_cmt = self.cat_rvalue(pat, slice_ty);
|
||||
let slice_cmt = self.cat_rvalue_node(pat, slice_ty);
|
||||
self.cat_pattern(slice_cmt, slice_pat, |x,y| op(x,y));
|
||||
}
|
||||
for after.iter().advance |&after_pat| {
|
||||
@ -1003,7 +1016,7 @@ impl mem_categorization_ctxt {
|
||||
cat_copied_upvar(_) => {
|
||||
~"captured outer variable in a heap closure"
|
||||
}
|
||||
cat_rvalue => {
|
||||
cat_rvalue(*) => {
|
||||
~"non-lvalue"
|
||||
}
|
||||
cat_local(_) => {
|
||||
@ -1100,7 +1113,7 @@ impl cmt_ {
|
||||
//! determines how long the value in `self` remains live.
|
||||
|
||||
match self.cat {
|
||||
cat_rvalue |
|
||||
cat_rvalue(*) |
|
||||
cat_static_item |
|
||||
cat_implicit_self |
|
||||
cat_copied_upvar(*) |
|
||||
@ -1187,11 +1200,13 @@ impl Repr for categorization {
|
||||
match *self {
|
||||
cat_static_item |
|
||||
cat_implicit_self |
|
||||
cat_rvalue |
|
||||
cat_rvalue(*) |
|
||||
cat_copied_upvar(*) |
|
||||
cat_local(*) |
|
||||
cat_self(*) |
|
||||
cat_arg(*) => fmt!("%?", *self),
|
||||
cat_arg(*) => {
|
||||
fmt!("%?", *self)
|
||||
}
|
||||
cat_deref(cmt, derefs, ptr) => {
|
||||
fmt!("%s->(%s, %u)", cmt.cat.repr(tcx),
|
||||
ptr_sigil(ptr), derefs)
|
||||
@ -1205,7 +1220,9 @@ impl Repr for categorization {
|
||||
fmt!("%s->(enum)", cmt.cat.repr(tcx))
|
||||
}
|
||||
cat_stack_upvar(cmt) |
|
||||
cat_discr(cmt, _) => cmt.cat.repr(tcx)
|
||||
cat_discr(cmt, _) => {
|
||||
cmt.cat.repr(tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,10 +190,19 @@ enum UseMode {
|
||||
|
||||
pub fn compute_moves(tcx: ty::ctxt,
|
||||
method_map: method_map,
|
||||
<<<<<<< HEAD
|
||||
crate: &crate) -> MoveMaps
|
||||
{
|
||||
||||||| merged common ancestors
|
||||
crate: @crate) -> MoveMaps
|
||||
{
|
||||
=======
|
||||
crate: @crate) -> MoveMaps {
|
||||
>>>>>>> Modify borrow checker to visit irrefutable patterns that appear in
|
||||
let visitor = visit::mk_vt(@visit::Visitor {
|
||||
visit_fn: compute_modes_for_fn,
|
||||
visit_expr: compute_modes_for_expr,
|
||||
visit_local: compute_modes_for_local,
|
||||
.. *visit::default_visitor()
|
||||
});
|
||||
let visit_cx = VisitContext {
|
||||
@ -220,9 +229,31 @@ pub fn moved_variable_node_id_from_def(def: def) -> Option<node_id> {
|
||||
}
|
||||
}
|
||||
|
||||
// ______________________________________________________________________
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Expressions
|
||||
|
||||
fn compute_modes_for_local<'a>(local: @local,
|
||||
(cx, v): (VisitContext,
|
||||
vt<VisitContext>)) {
|
||||
cx.use_pat(local.node.pat);
|
||||
for local.node.init.iter().advance |&init| {
|
||||
cx.use_expr(init, Read, v);
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_modes_for_fn(fk: &visit::fn_kind,
|
||||
decl: &fn_decl,
|
||||
body: &blk,
|
||||
span: span,
|
||||
id: node_id,
|
||||
(cx, v): (VisitContext,
|
||||
vt<VisitContext>)) {
|
||||
for decl.inputs.each |a| {
|
||||
cx.use_pat(a.pat);
|
||||
}
|
||||
visit::visit_fn(fk, decl, body, span, id, (cx, v));
|
||||
}
|
||||
|
||||
fn compute_modes_for_expr(expr: @expr,
|
||||
(cx, v): (VisitContext,
|
||||
vt<VisitContext>))
|
||||
@ -522,7 +553,10 @@ impl VisitContext {
|
||||
self.use_expr(base, comp_mode, visitor);
|
||||
}
|
||||
|
||||
expr_fn_block(_, ref body) => {
|
||||
expr_fn_block(ref decl, ref body) => {
|
||||
for decl.inputs.each |a| {
|
||||
self.use_pat(a.pat);
|
||||
}
|
||||
let cap_vars = self.compute_captures(expr.id);
|
||||
self.move_maps.capture_map.insert(expr.id, cap_vars);
|
||||
self.consume_block(body, visitor);
|
||||
@ -580,13 +614,15 @@ impl VisitContext {
|
||||
* into itself or not based on its type and annotation.
|
||||
*/
|
||||
|
||||
do pat_bindings(self.tcx.def_map, pat) |bm, id, _span, _path| {
|
||||
do pat_bindings(self.tcx.def_map, pat) |bm, id, _span, path| {
|
||||
let binding_moves = match bm {
|
||||
bind_by_ref(_) => false,
|
||||
bind_infer => {
|
||||
let pat_ty = ty::node_id_to_type(self.tcx, id);
|
||||
debug!("pattern %? type is %s",
|
||||
id, pat_ty.repr(self.tcx));
|
||||
debug!("pattern %? %s type is %s",
|
||||
id,
|
||||
ast_util::path_to_ident(path).repr(self.tcx),
|
||||
pat_ty.repr(self.tcx));
|
||||
ty::type_moves_by_default(self.tcx, pat_ty)
|
||||
}
|
||||
};
|
||||
|
@ -158,9 +158,9 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: &ast::Path,
|
||||
None => {
|
||||
fcx.infcx().type_error_message_str_with_expected(pat.span,
|
||||
|expected, actual| {
|
||||
expected.map_default(~"", |&e| {
|
||||
expected.map_default(~"", |e| {
|
||||
fmt!("mismatched types: expected `%s` but found %s",
|
||||
e, actual)})},
|
||||
*e, actual)})},
|
||||
Some(expected), ~"a structure pattern",
|
||||
None);
|
||||
fcx.write_error(pat.id);
|
||||
@ -200,9 +200,9 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: &ast::Path,
|
||||
_ => {
|
||||
fcx.infcx().type_error_message_str_with_expected(pat.span,
|
||||
|expected, actual| {
|
||||
expected.map_default(~"", |&e| {
|
||||
expected.map_default(~"", |e| {
|
||||
fmt!("mismatched types: expected `%s` but found %s",
|
||||
e, actual)})},
|
||||
*e, actual)})},
|
||||
Some(expected), ~"an enum or structure pattern",
|
||||
None);
|
||||
fcx.write_error(pat.id);
|
||||
@ -534,9 +534,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
|
||||
_ => ty::terr_mismatch
|
||||
};
|
||||
fcx.infcx().type_error_message_str_with_expected(pat.span, |expected, actual| {
|
||||
expected.map_default(~"", |&e| {
|
||||
expected.map_default(~"", |e| {
|
||||
fmt!("mismatched types: expected `%s` but found %s",
|
||||
e, actual)})}, Some(expected), ~"tuple", Some(&type_error));
|
||||
*e, actual)})}, Some(expected), ~"tuple", Some(&type_error));
|
||||
fcx.write_error(pat.id);
|
||||
}
|
||||
}
|
||||
@ -583,9 +583,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
|
||||
fcx.infcx().type_error_message_str_with_expected(
|
||||
pat.span,
|
||||
|expected, actual| {
|
||||
expected.map_default(~"", |&e| {
|
||||
expected.map_default(~"", |e| {
|
||||
fmt!("mismatched types: expected `%s` but found %s",
|
||||
e, actual)})},
|
||||
*e, actual)})},
|
||||
Some(expected),
|
||||
~"a vector pattern",
|
||||
None);
|
||||
@ -641,9 +641,9 @@ pub fn check_pointer_pat(pcx: &pat_ctxt,
|
||||
fcx.infcx().type_error_message_str_with_expected(
|
||||
span,
|
||||
|expected, actual| {
|
||||
expected.map_default(~"", |&e| {
|
||||
expected.map_default(~"", |e| {
|
||||
fmt!("mismatched types: expected `%s` but found %s",
|
||||
e, actual)})},
|
||||
*e, actual)})},
|
||||
Some(expected),
|
||||
fmt!("%s pattern", match pointer_kind {
|
||||
Managed => "an @-box",
|
||||
|
@ -350,10 +350,7 @@ pub fn resolve_type_vars_in_fn(fcx: @mut FnCtxt,
|
||||
self_info.self_id);
|
||||
}
|
||||
for decl.inputs.iter().advance |arg| {
|
||||
do pat_util::pat_bindings(fcx.tcx().def_map, arg.pat)
|
||||
|_bm, pat_id, span, _path| {
|
||||
resolve_type_vars_for_node(wbcx, span, pat_id);
|
||||
}
|
||||
(visit.visit_pat)(arg.pat, (wbcx, visit));
|
||||
// Privacy needs the type for the whole pattern, not just each binding
|
||||
if !pat_util::pat_is_binding(fcx.tcx().def_map, arg.pat) {
|
||||
resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id);
|
||||
|
Loading…
x
Reference in New Issue
Block a user