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:
Niko Matsakis 2013-06-20 15:25:52 -04:00
parent 2d3262ca7b
commit 729b07f83c
12 changed files with 249 additions and 151 deletions

View File

@ -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));
}

View File

@ -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(_) => {

View File

@ -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);
}

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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");
}
};

View File

@ -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(_) => {}

View File

@ -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)
}
}
}
}

View File

@ -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)
}
};

View File

@ -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",

View File

@ -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);