check_loans.rs 26.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12 13 14 15 16 17
// ----------------------------------------------------------------------
// Checking loans
//
// Phase 2 of check: we walk down the tree and check that:
// 1. assignments are always made to mutable locations;
// 2. loans made in overlapping scopes do not conflict
// 3. assignments do not affect things loaned out as immutable
18
// 4. moves do not affect things loaned out in any way
19

20
use core::prelude::*;
21

22 23
use middle::moves;
use middle::borrowck::{Loan, bckerr, BorrowckCtxt, inherent_mutability};
24
use middle::borrowck::{req_maps, root_map_key, save_and_restore};
25 26
use middle::borrowck::{MoveError, MoveOk, MoveFromIllegalCmt};
use middle::borrowck::{MoveWhileBorrowed};
27
use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
28
use middle::mem_categorization::{cat_local, cat_rvalue, cat_self};
29
use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg};
30
use middle::mem_categorization::{lp_comp, lp_deref, lp_local};
31
use middle::ty;
32
use util::ppaux::ty_to_str;
33

34 35 36 37
use core::cmp;
use core::dvec::DVec;
use core::uint;
use core::vec;
38
use std::oldmap::HashMap;
39
use syntax::ast::{m_const, m_imm, m_mutbl};
40 41
use syntax::ast;
use syntax::ast_util;
42
use syntax::codemap::span;
43 44
use syntax::print::pprust;
use syntax::visit;
45 46

enum check_loan_ctxt = @{
47
    bccx: @BorrowckCtxt,
48 49
    req_maps: req_maps,

B
Brian Anderson 已提交
50
    reported: HashMap<ast::node_id, ()>,
51 52

    mut declared_purity: ast::purity,
53
    mut fn_args: @~[ast::node_id]
54 55 56
};

// if we are enforcing purity, why are we doing so?
57
#[deriving_eq]
58 59 60 61 62 63 64 65 66 67
enum purity_cause {
    // enforcing purity because fn was declared pure:
    pc_pure_fn,

    // enforce purity because we need to guarantee the
    // validity of some alias; `bckerr` describes the
    // reason we needed to enforce purity.
    pc_cmt(bckerr)
}

68 69 70
pub fn check_loans(bccx: @BorrowckCtxt,
               req_maps: req_maps,
               crate: @ast::crate) {
71 72
    let clcx = check_loan_ctxt(@{bccx: bccx,
                                 req_maps: req_maps,
73
                                 reported: HashMap(),
74
                                 mut declared_purity: ast::impure_fn,
75
                                 mut fn_args: @~[]});
76 77 78 79 80
    let vt = visit::mk_vt(@visit::Visitor {visit_expr: check_loans_in_expr,
                                           visit_local: check_loans_in_local,
                                           visit_block: check_loans_in_block,
                                           visit_fn: check_loans_in_fn,
                                           .. *visit::default_visitor()});
81 82 83
    visit::visit_crate(*crate, clcx, vt);
}

84
#[deriving_eq]
85 86
enum assignment_type {
    at_straight_up,
87
    at_swap
88 89
}

B
Brian Anderson 已提交
90
impl assignment_type {
91 92 93
    fn checked_by_liveness() -> bool {
        // the liveness pass guarantees that immutable local variables
        // are only assigned once; but it doesn't consider &mut
94
        match self {
B
Brian Anderson 已提交
95
          at_straight_up => true,
96
          at_swap => true
97 98
        }
    }
99
    fn ing_form(desc: ~str) -> ~str {
100
        match self {
B
Brian Anderson 已提交
101
          at_straight_up => ~"assigning to " + desc,
102
          at_swap => ~"swapping to and from " + desc
103 104 105 106
        }
    }
}

B
Brian Anderson 已提交
107
impl check_loan_ctxt {
108 109
    fn tcx() -> ty::ctxt { self.bccx.tcx }

B
Brian Anderson 已提交
110
    fn purity(scope_id: ast::node_id) -> Option<purity_cause> {
111
        let default_purity = match self.declared_purity {
112
          // an unsafe declaration overrides all
B
Brian Anderson 已提交
113
          ast::unsafe_fn => return None,
114 115 116 117

          // otherwise, remember what was declared as the
          // default, but we must scan for requirements
          // imposed by the borrow check
B
Brian Anderson 已提交
118 119
          ast::pure_fn => Some(pc_pure_fn),
          ast::extern_fn | ast::impure_fn => None
120 121 122 123 124 125 126 127 128
        };

        // scan to see if this scope or any enclosing scope requires
        // purity.  if so, that overrides the declaration.

        let mut scope_id = scope_id;
        let region_map = self.tcx().region_map;
        let pure_map = self.req_maps.pure_map;
        loop {
129
            match pure_map.find(&scope_id) {
B
Brian Anderson 已提交
130
              None => (),
131
              Some(ref e) => return Some(pc_cmt((*e)))
132 133
            }

134
            match region_map.find(&scope_id) {
B
Brian Anderson 已提交
135 136
              None => return default_purity,
              Some(next_scope_id) => scope_id = next_scope_id
137 138 139 140
            }
        }
    }

141
    fn walk_loans(scope_id: ast::node_id, f: fn(v: &Loan) -> bool) {
142 143 144 145 146
        let mut scope_id = scope_id;
        let region_map = self.tcx().region_map;
        let req_loan_map = self.req_maps.req_loan_map;

        loop {
147
            for req_loan_map.find(&scope_id).each |loans| {
148 149
                for loans.each |loan| {
                    if !f(loan) { return; }
150 151 152
                }
            }

153
            match region_map.find(&scope_id) {
B
Brian Anderson 已提交
154 155
              None => return,
              Some(next_scope_id) => scope_id = next_scope_id,
156 157 158 159 160 161
            }
        }
    }

    fn walk_loans_of(scope_id: ast::node_id,
                     lp: @loan_path,
162
                     f: fn(v: &Loan) -> bool) {
B
Brian Anderson 已提交
163
        for self.walk_loans(scope_id) |loan| {
164
            if loan.lp == lp {
B
Brian Anderson 已提交
165
                if !f(loan) { return; }
166 167 168 169 170 171
            }
        }
    }

    // when we are in a pure context, we check each call to ensure
    // that the function which is invoked is itself pure.
172 173 174 175 176
    //
    // note: we take opt_expr and expr_id separately because for
    // overloaded operators the callee has an id but no expr.
    // annoying.
    fn check_pure_callee_or_arg(pc: purity_cause,
B
Brian Anderson 已提交
177
                                opt_expr: Option<@ast::expr>,
178 179
                                callee_id: ast::node_id,
                                callee_span: span) {
180 181
        let tcx = self.tcx();

P
Paul Stansifer 已提交
182
        debug!("check_pure_callee_or_arg(pc=%?, expr=%?, \
183 184
                callee_id=%d, ty=%s)",
               pc,
185
               opt_expr.map(|e| pprust::expr_to_str(*e, tcx.sess.intr()) ),
186
               callee_id,
P
Paul Stansifer 已提交
187
               ty_to_str(self.tcx(), ty::node_id_to_type(tcx, callee_id)));
188 189 190 191 192 193 194 195 196 197

        // Purity rules: an expr B is a legal callee or argument to a
        // call within a pure function A if at least one of the
        // following holds:
        //
        // (a) A was declared pure and B is one of its arguments;
        // (b) B is a stack closure;
        // (c) B is a pure fn;
        // (d) B is not a fn.

198
        match opt_expr {
B
Brian Anderson 已提交
199
          Some(expr) => {
200
            match expr.node {
B
Brian Anderson 已提交
201
              ast::expr_path(_) if pc == pc_pure_fn => {
202
                let def = self.tcx().def_map.get(&expr.id);
203 204 205
                let did = ast_util::def_id_of_def(def);
                let is_fn_arg =
                    did.crate == ast::local_crate &&
T
Tim Chevalier 已提交
206
                    (*self.fn_args).contains(&(did.node));
B
Brian Anderson 已提交
207
                if is_fn_arg { return; } // case (a) above
208 209
              }
              ast::expr_fn_block(*) | ast::expr_fn(*) |
B
Brian Anderson 已提交
210
              ast::expr_loop_body(*) | ast::expr_do_body(*) => {
B
Brian Anderson 已提交
211 212 213 214
                if self.is_stack_closure(expr.id) {
                    // case (b) above
                    return;
                }
215
              }
B
Brian Anderson 已提交
216
              _ => ()
217
            }
218
          }
B
Brian Anderson 已提交
219
          None => ()
220 221
        }

222
        let callee_ty = ty::node_id_to_type(tcx, callee_id);
223
        match ty::get(callee_ty).sty {
224 225 226 227 228 229 230 231 232 233 234
            ty::ty_bare_fn(ty::BareFnTy {purity: purity, _}) |
            ty::ty_closure(ty::ClosureTy {purity: purity, _}) => {
                match purity {
                    ast::pure_fn => return, // case (c) above
                    ast::impure_fn | ast::unsafe_fn | ast::extern_fn => {
                        self.report_purity_error(
                            pc, callee_span,
                            fmt!("access to %s function",
                                 purity.to_str()));
                    }
                }
235
            }
236
            _ => return, // case (d) above
237 238 239 240 241 242 243
        }
    }

    // True if the expression with the given `id` is a stack closure.
    // The expression must be an expr_fn(*) or expr_fn_block(*)
    fn is_stack_closure(id: ast::node_id) -> bool {
        let fn_ty = ty::node_id_to_type(self.tcx(), id);
244 245 246 247 248
        match ty::get(fn_ty).sty {
            ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil,
                                          _}) => true,
            _ => false
        }
249 250 251
    }

    fn is_allowed_pure_arg(expr: @ast::expr) -> bool {
252
        return match expr.node {
B
Brian Anderson 已提交
253
          ast::expr_path(_) => {
254
            let def = self.tcx().def_map.get(&expr.id);
255
            let did = ast_util::def_id_of_def(def);
256
            did.crate == ast::local_crate &&
T
Tim Chevalier 已提交
257
                (*self.fn_args).contains(&(did.node))
258
          }
B
Brian Anderson 已提交
259
          ast::expr_fn_block(*) | ast::expr_fn(*) => {
260 261
            self.is_stack_closure(expr.id)
          }
B
Brian Anderson 已提交
262
          _ => false
263 264 265 266
        };
    }

    fn check_for_conflicting_loans(scope_id: ast::node_id) {
267 268
        debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);

269
        let new_loans = match self.req_maps.req_loan_map.find(&scope_id) {
B
Brian Anderson 已提交
270
            None => return,
271
            Some(loans) => loans
272 273
        };

274 275
        debug!("new_loans has length %?", new_loans.len());

276
        let par_scope_id = self.tcx().region_map.get(&scope_id);
B
Brian Anderson 已提交
277
        for self.walk_loans(par_scope_id) |old_loan| {
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
            debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan));

            for new_loans.each |new_loan| {
                self.report_error_if_loans_conflict(old_loan, new_loan);
            }
        }

        let len = new_loans.len();
        for uint::range(0, len) |i| {
            let loan_i = new_loans[i];
            for uint::range(i+1, len) |j| {
                let loan_j = new_loans[j];
                self.report_error_if_loans_conflict(&loan_i, &loan_j);
            }
        }
    }

    fn report_error_if_loans_conflict(&self,
                                      old_loan: &Loan,
                                      new_loan: &Loan) {
        if old_loan.lp != new_loan.lp {
            return;
        }

        match (old_loan.mutbl, new_loan.mutbl) {
303
            (m_const, _) | (_, m_const) | (m_imm, m_imm) => {
304 305 306
                /*ok*/
            }

307
            (m_mutbl, m_mutbl) | (m_mutbl, m_imm) | (m_imm, m_mutbl) => {
308 309 310 311 312 313 314 315 316 317
                self.bccx.span_err(
                    new_loan.cmt.span,
                    fmt!("loan of %s as %s \
                          conflicts with prior loan",
                         self.bccx.cmt_to_str(new_loan.cmt),
                         self.bccx.mut_to_str(new_loan.mutbl)));
                self.bccx.span_note(
                    old_loan.cmt.span,
                    fmt!("prior loan as %s granted here",
                         self.bccx.mut_to_str(old_loan.mutbl)));
318 319 320 321 322
            }
        }
    }

    fn is_local_variable(cmt: cmt) -> bool {
323
        match cmt.cat {
B
Brian Anderson 已提交
324 325
          cat_local(_) => true,
          _ => false
326 327 328 329
        }
    }

    fn check_assignment(at: assignment_type, ex: @ast::expr) {
330 331
        // We don't use cat_expr() here because we don't want to treat
        // auto-ref'd parameters in overloaded operators as rvalues.
332
        let cmt = match self.bccx.tcx.adjustments.find(&ex.id) {
333 334 335
            None => self.bccx.cat_expr_unadjusted(ex),
            Some(adj) => self.bccx.cat_expr_autoderefd(ex, adj)
        };
336

P
Paul Stansifer 已提交
337 338
        debug!("check_assignment(cmt=%s)",
               self.bccx.cmt_to_repr(cmt));
339

340
        if self.is_local_variable(cmt) && at.checked_by_liveness() {
341 342 343
            // liveness guarantees that immutable local variables
            // are only assigned once
        } else {
344
            match cmt.mutbl {
B
Brian Anderson 已提交
345 346
              m_mutbl => { /*ok*/ }
              m_const | m_imm => {
347 348 349
                self.bccx.span_err(
                    ex.span,
                    at.ing_form(self.bccx.cmt_to_str(cmt)));
B
Brian Anderson 已提交
350
                return;
351 352 353 354 355 356 357
              }
            }
        }

        // if this is a pure function, only loan-able state can be
        // assigned, because it is uniquely tied to this function and
        // is not visible from the outside
358
        match self.purity(ex.id) {
B
Brian Anderson 已提交
359
          None => (),
360
          Some(pc_cmt(_)) => {
361 362 363 364 365 366
            // Subtle: Issue #3162.  If we are enforcing purity
            // because there is a reference to aliasable, mutable data
            // that we require to be immutable, we can't allow writes
            // even to data owned by the current stack frame.  This is
            // because that aliasable data might have been located on
            // the current stack frame, we don't know.
367
            self.report_purity_error(
368 369 370
                self.purity(ex.id).get(),
                ex.span,
                at.ing_form(self.bccx.cmt_to_str(cmt)));
371
          }
B
Brian Anderson 已提交
372
          Some(pc_pure_fn) => {
373 374 375 376
            if cmt.lp.is_none() {
                self.report_purity_error(
                    pc_pure_fn, ex.span,
                    at.ing_form(self.bccx.cmt_to_str(cmt)));
377 378 379 380 381 382 383 384
            }
          }
        }

        // check for a conflicting loan as well, except in the case of
        // taking a mutable ref.  that will create a loan of its own
        // which will be checked for compat separately in
        // check_for_conflicting_loans()
385 386
        for cmt.lp.each |lp| {
            self.check_for_loan_conflicting_with_assignment(
387
                at, ex, cmt, *lp);
388 389 390
        }

        self.bccx.add_to_mutbl_map(cmt);
391 392 393 394 395 396 397 398 399 400 401 402

        // Check for and insert write guards as necessary.
        self.add_write_guards_if_necessary(cmt);
    }

    fn add_write_guards_if_necessary(cmt: cmt) {
        match cmt.cat {
            cat_deref(base, deref_count, ptr_kind) => {
                self.add_write_guards_if_necessary(base);

                match ptr_kind {
                    gc_ptr(ast::m_mutbl) => {
403 404 405 406
                        let key = root_map_key {
                            id: base.id,
                            derefs: deref_count
                        };
407 408 409 410 411 412 413 414 415 416
                        self.bccx.write_guard_map.insert(key, ());
                    }
                    _ => {}
                }
            }
            cat_comp(base, _) => {
                self.add_write_guards_if_necessary(base);
            }
            _ => {}
        }
417 418
    }

419 420 421 422 423 424
    fn check_for_loan_conflicting_with_assignment(
        at: assignment_type,
        ex: @ast::expr,
        cmt: cmt,
        lp: @loan_path) {

B
Brian Anderson 已提交
425
        for self.walk_loans_of(ex.id, lp) |loan| {
426
            match loan.mutbl {
427 428
              m_const => { /*ok*/ }
              m_mutbl | m_imm => {
429 430
                self.bccx.span_err(
                    ex.span,
P
Paul Stansifer 已提交
431 432
                    fmt!("%s prohibited due to outstanding loan",
                         at.ing_form(self.bccx.cmt_to_str(cmt))));
433 434
                self.bccx.span_note(
                    loan.cmt.span,
P
Paul Stansifer 已提交
435 436
                    fmt!("loan of %s granted here",
                         self.bccx.cmt_to_str(loan.cmt)));
B
Brian Anderson 已提交
437
                return;
438 439 440 441 442 443 444 445
              }
            }
        }

        // Subtle: if the mutability of the component being assigned
        // is inherited from the thing that the component is embedded
        // within, then we have to check whether that thing has been
        // loaned out as immutable!  An example:
B
Brian Anderson 已提交
446
        //    let mut x = {f: Some(3)};
447 448
        //    let y = &x; // x loaned out as immutable
        //    x.f = none; // changes type of y.f, which appears to be imm
449
        match *lp {
B
Brian Anderson 已提交
450
          lp_comp(lp_base, ck) if inherent_mutability(ck) != m_mutbl => {
451 452 453
            self.check_for_loan_conflicting_with_assignment(
                at, ex, cmt, lp_base);
          }
454
          lp_comp(*) | lp_self | lp_local(*) | lp_arg(*) | lp_deref(*) => ()
455 456 457
        }
    }

458
    fn report_purity_error(pc: purity_cause, sp: span, msg: ~str) {
459
        match pc {
B
Brian Anderson 已提交
460
          pc_pure_fn => {
461 462
            self.tcx().sess.span_err(
                sp,
P
Paul Stansifer 已提交
463
                fmt!("%s prohibited in pure context", msg));
464
          }
465 466
          pc_cmt(ref e) => {
            if self.reported.insert((*e).cmt.id, ()) {
467
                self.tcx().sess.span_err(
468
                    (*e).cmt.span,
P
Paul Stansifer 已提交
469
                    fmt!("illegal borrow unless pure: %s",
470 471
                         self.bccx.bckerr_to_str((*e))));
                self.bccx.note_and_explain_bckerr((*e));
472 473
                self.tcx().sess.span_note(
                    sp,
P
Paul Stansifer 已提交
474
                    fmt!("impure due to %s", msg));
475 476 477 478 479
            }
          }
        }
    }

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
    fn check_move_out_from_expr(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. */
            }
            _ => {
                let cmt = self.bccx.cat_expr(ex);
                match self.analyze_move_out_from_cmt(cmt) {
                    MoveOk => {}
                    MoveFromIllegalCmt(_) => {
                        self.bccx.span_err(
                            cmt.span,
                            fmt!("moving out of %s",
                                 self.bccx.cmt_to_str(cmt)));
                    }
                    MoveWhileBorrowed(_, loan_cmt) => {
                        self.bccx.span_err(
                            cmt.span,
                            fmt!("moving out of %s prohibited \
                                  due to outstanding loan",
                                 self.bccx.cmt_to_str(cmt)));
                        self.bccx.span_note(
                            loan_cmt.span,
                            fmt!("loan of %s granted here",
                                 self.bccx.cmt_to_str(loan_cmt)));
                    }
                }
            }
        }
511 512
    }

513
    fn analyze_move_out_from_cmt(cmt: cmt) -> MoveError {
P
Paul Stansifer 已提交
514 515
        debug!("check_move_out_from_cmt(cmt=%s)",
               self.bccx.cmt_to_repr(cmt));
516

517
        match cmt.cat {
518
          // Rvalues, locals, and arguments can be moved:
519
          cat_rvalue | cat_local(_) | cat_arg(_) | cat_self(_) => {}
520 521 522 523

          // We allow moving out of static items because the old code
          // did.  This seems consistent with permitting moves out of
          // rvalues, I guess.
B
Brian Anderson 已提交
524
          cat_special(sk_static_item) => {}
525

B
Brian Anderson 已提交
526
          cat_deref(_, _, unsafe_ptr) => {}
527

528
          // Nothing else.
B
Brian Anderson 已提交
529
          _ => {
530
              return MoveFromIllegalCmt(cmt);
531 532 533 534 535 536
          }
        }

        self.bccx.add_to_mutbl_map(cmt);

        // check for a conflicting loan:
537 538 539
        for cmt.lp.each |lp| {
            for self.walk_loans_of(cmt.id, *lp) |loan| {
                return MoveWhileBorrowed(cmt, loan.cmt);
540
            }
541
        }
542 543

        return MoveOk;
544 545
    }

546
    fn check_call(expr: @ast::expr,
B
Brian Anderson 已提交
547
                  callee: Option<@ast::expr>,
548 549
                  callee_id: ast::node_id,
                  callee_span: span,
550
                  args: &[@ast::expr]) {
551
        match self.purity(expr.id) {
B
Brian Anderson 已提交
552
          None => {}
553
          Some(ref pc) => {
554
            self.check_pure_callee_or_arg(
555
                (*pc), callee, callee_id, callee_span);
B
Brian Anderson 已提交
556
            for args.each |arg| {
557
                self.check_pure_callee_or_arg(
558
                    (*pc), Some(*arg), arg.id, arg.span);
559 560 561 562
            }
          }
        }
    }
563 564 565 566
}

fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
                     sp: span, id: ast::node_id, &&self: check_loan_ctxt,
567 568 569 570 571
                     visitor: visit::vt<check_loan_ctxt>)
{
    let is_stack_closure = self.is_stack_closure(id);
    let fty = ty::node_id_to_type(self.tcx(), id);

572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
    let declared_purity;
    match fk {
        visit::fk_item_fn(*) | visit::fk_method(*) |
        visit::fk_dtor(*) => {
            declared_purity = ty::ty_fn_purity(fty);
        }

        visit::fk_anon(*) | visit::fk_fn_block(*) => {
            let fty_sigil = ty::ty_closure_sigil(fty);
            check_moves_from_captured_variables(self, id, fty_sigil);
            declared_purity = ty::determine_inherited_purity(
                copy self.declared_purity, ty::ty_fn_purity(fty),
                fty_sigil);
        }
    }
587

P
Paul Stansifer 已提交
588
    debug!("purity on entry=%?", copy self.declared_purity);
589 590
    do save_and_restore(&mut(self.declared_purity)) {
        do save_and_restore(&mut(self.fn_args)) {
591
            self.declared_purity = declared_purity;
592 593 594 595

            match fk {
                visit::fk_anon(*) |
                visit::fk_fn_block(*) if is_stack_closure => {
596
                    // inherits the fn_args from enclosing ctxt
597 598 599 600
                }
                visit::fk_anon(*) | visit::fk_fn_block(*) |
                visit::fk_method(*) | visit::fk_item_fn(*) |
                visit::fk_dtor(*) => {
601 602 603 604 605 606 607 608 609 610 611 612 613 614
                    let mut fn_args = ~[];
                    for decl.inputs.each |input| {
                        // For the purposes of purity, only consider function-
                        // typed bindings in trivial patterns to be function
                        // arguments. For example, do not allow `f` and `g` in
                        // (f, g): (&fn(), &fn()) to be called.
                        match input.pat.node {
                            ast::pat_ident(_, _, None) => {
                                fn_args.push(input.pat.id);
                            }
                            _ => {} // Ignore this argument.
                        }
                    }
                    self.fn_args = @move fn_args;
615
                }
T
Tim Chevalier 已提交
616
            }
617 618

            visit::visit_fn(fk, decl, body, sp, id, self, visitor);
619 620
        }
    }
P
Paul Stansifer 已提交
621
    debug!("purity on exit=%?", copy self.declared_purity);
622 623 624

    fn check_moves_from_captured_variables(&&self: check_loan_ctxt,
                                           id: ast::node_id,
625
                                           fty_sigil: ast::Sigil)
626
    {
627 628
        match fty_sigil {
            ast::ManagedSigil | ast::OwnedSigil => {
629
                let cap_vars = self.bccx.capture_map.get(&id);
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
                for cap_vars.each |cap_var| {
                    match cap_var.mode {
                        moves::CapRef | moves::CapCopy => { loop; }
                        moves::CapMove => { }
                    }
                    let def_id = ast_util::def_id_of_def(cap_var.def).node;
                    let ty = ty::node_id_to_type(self.tcx(), def_id);
                    let cmt = self.bccx.cat_def(id, cap_var.span,
                                                ty, cap_var.def);
                    let move_err = self.analyze_move_out_from_cmt(cmt);
                    match move_err {
                        MoveOk => {}
                        MoveFromIllegalCmt(move_cmt) => {
                            self.bccx.span_err(
                                cap_var.span,
                                fmt!("illegal by-move capture of %s",
                                     self.bccx.cmt_to_str(move_cmt)));
                        }
                        MoveWhileBorrowed(move_cmt, loan_cmt) => {
                            self.bccx.span_err(
                                cap_var.span,
                                fmt!("by-move capture of %s prohibited \
                                      due to outstanding loan",
                                     self.bccx.cmt_to_str(move_cmt)));
                            self.bccx.span_note(
                                loan_cmt.span,
                                fmt!("loan of %s granted here",
                                     self.bccx.cmt_to_str(loan_cmt)));
                        }
                    }
                }
            }

663
            ast::BorrowedSigil => {}
664 665
        }
    }
666 667
}

668 669 670 671 672 673
fn check_loans_in_local(local: @ast::local,
                        &&self: check_loan_ctxt,
                        vt: visit::vt<check_loan_ctxt>) {
    visit::visit_local(local, self, vt);
}

674 675 676
fn check_loans_in_expr(expr: @ast::expr,
                       &&self: check_loan_ctxt,
                       vt: visit::vt<check_loan_ctxt>) {
677 678 679
    debug!("check_loans_in_expr(expr=%?/%s)",
           expr.id, pprust::expr_to_str(expr, self.tcx().sess.intr()));

680 681
    self.check_for_conflicting_loans(expr.id);

682
    if self.bccx.moves_map.contains_key_ref(&expr.id) {
683
        self.check_move_out_from_expr(expr);
684 685
    }

686
    match expr.node {
B
Brian Anderson 已提交
687
      ast::expr_swap(l, r) => {
688 689 690 691
        self.check_assignment(at_swap, l);
        self.check_assignment(at_swap, r);
      }
      ast::expr_assign(dest, _) |
B
Brian Anderson 已提交
692
      ast::expr_assign_op(_, dest, _) => {
693 694
        self.check_assignment(at_straight_up, dest);
      }
695 696
      ast::expr_call(f, ref args, _) => {
        self.check_call(expr, Some(f), f.id, f.span, *args);
697
      }
698 699
      ast::expr_method_call(_, _, _, ref args, _) => {
        self.check_call(expr, None, expr.callee_id, expr.span, *args);
700
      }
701 702
      ast::expr_index(_, rval) |
      ast::expr_binary(_, _, rval)
703
      if self.bccx.method_map.contains_key_ref(&expr.id) => {
704
        self.check_call(expr,
B
Brian Anderson 已提交
705
                        None,
T
Tim Chevalier 已提交
706
                        expr.callee_id,
707
                        expr.span,
708
                        ~[rval]);
709
      }
710
      ast::expr_unary(*) | ast::expr_index(*)
711
      if self.bccx.method_map.contains_key_ref(&expr.id) => {
712
        self.check_call(expr,
B
Brian Anderson 已提交
713
                        None,
T
Tim Chevalier 已提交
714
                        expr.callee_id,
715
                        expr.span,
716
                        ~[]);
717
      }
718 719 720 721 722 723 724 725 726 727 728 729
      ast::expr_match(*) => {
          // 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.
      }
B
Brian Anderson 已提交
730
      _ => { }
731 732 733 734 735 736 737 738
    }

    visit::visit_expr(expr, self, vt);
}

fn check_loans_in_block(blk: ast::blk,
                        &&self: check_loan_ctxt,
                        vt: visit::vt<check_loan_ctxt>) {
T
Tim Chevalier 已提交
739
    do save_and_restore(&mut(self.declared_purity)) {
740 741
        self.check_for_conflicting_loans(blk.node.id);

742
        match blk.node.rules {
B
Brian Anderson 已提交
743
          ast::default_blk => {
744
          }
B
Brian Anderson 已提交
745
          ast::unsafe_blk => {
746 747 748 749 750 751 752 753
            self.declared_purity = ast::unsafe_fn;
          }
        }

        visit::visit_block(blk, self, vt);
    }
}