check_loans.rs 29.2 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 21
use core::prelude::*;

22 23
use core::hashmap::HashSet;
use core::uint;
N
Niko Matsakis 已提交
24
use mc = middle::mem_categorization;
25 26
use middle::borrowck::*;
use middle::moves;
27
use middle::ty;
N
Niko Matsakis 已提交
28
use syntax::ast::{m_mutbl, m_imm, m_const};
29 30
use syntax::ast;
use syntax::ast_util;
N
Niko Matsakis 已提交
31
use syntax::codemap::span;
32 33
use syntax::visit;
use util::ppaux::Repr;
34

N
Niko Matsakis 已提交
35
struct CheckLoanCtxt<'self> {
36
    bccx: @BorrowckCtxt,
37 38
    dfcx_loans: &'self LoanDataFlow,
    move_data: move_data::FlowedMoveData,
N
Niko Matsakis 已提交
39 40
    all_loans: &'self [Loan],
    reported: @mut HashSet<ast::node_id>,
41 42
}

43
pub fn check_loans(bccx: @BorrowckCtxt,
44 45
                   dfcx_loans: &LoanDataFlow,
                   move_data: move_data::FlowedMoveData,
N
Niko Matsakis 已提交
46 47 48 49
                   all_loans: &[Loan],
                   body: &ast::blk) {
    debug!("check_loans(body id=%?)", body.node.id);

50 51
    let clcx = @mut CheckLoanCtxt {
        bccx: bccx,
52 53
        dfcx_loans: dfcx_loans,
        move_data: move_data,
N
Niko Matsakis 已提交
54 55
        all_loans: all_loans,
        reported: @mut HashSet::new(),
56
    };
N
Niko Matsakis 已提交
57

58 59 60
    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,
N
Niko Matsakis 已提交
61
                                           visit_pat: check_loans_in_pat,
62 63
                                           visit_fn: check_loans_in_fn,
                                           .. *visit::default_visitor()});
N
Niko Matsakis 已提交
64
    (vt.visit_block)(body, clcx, vt);
65 66
}

N
Niko Matsakis 已提交
67 68
enum MoveError {
    MoveOk,
69
    MoveWhileBorrowed(/*move*/@LoanPath, /*loan*/@LoanPath, /*loan*/span)
70 71
}

72 73
impl<'self> CheckLoanCtxt<'self> {
    pub fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
74

75 76 77 78
    pub fn each_issued_loan(&self,
                            scope_id: ast::node_id,
                            op: &fn(&Loan) -> bool)
                            -> bool {
79 80 81 82 83
        //! Iterates over each loan that that has been issued
        //! on entrance to `scope_id`, regardless of whether it is
        //! actually *in scope* at that point.  Sometimes loans
        //! are issued for future scopes and thus they may have been
        //! *issued* but not yet be in effect.
84

85
        for self.dfcx_loans.each_bit_on_entry(scope_id) |loan_index| {
86 87 88 89 90 91 92 93
            let loan = &self.all_loans[loan_index];
            if !op(loan) {
                return false;
            }
        }
        return true;
    }

94 95 96 97
    pub fn each_in_scope_loan(&self,
                              scope_id: ast::node_id,
                              op: &fn(&Loan) -> bool)
                              -> bool {
98 99
        //! Like `each_issued_loan()`, but only considers loans that are
        //! currently in scope.
100

101 102 103 104 105 106 107 108 109 110 111
        let region_maps = self.tcx().region_maps;
        for self.each_issued_loan(scope_id) |loan| {
            if region_maps.is_subscope_of(scope_id, loan.kill_scope) {
                if !op(loan) {
                    return false;
                }
            }
        }
        return true;
    }

112 113 114 115 116
    pub fn each_in_scope_restriction(&self,
                                     scope_id: ast::node_id,
                                     loan_path: @LoanPath,
                                     op: &fn(&Loan, &Restriction) -> bool)
                                     -> bool {
117 118 119 120 121 122 123 124 125 126 127 128 129 130
        //! Iterates through all the in-scope restrictions for the
        //! given `loan_path`

        for self.each_in_scope_loan(scope_id) |loan| {
            for loan.restrictions.each |restr| {
                if restr.loan_path == loan_path {
                    if !op(loan, restr) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
131

132
    pub fn loans_generated_by(&self, scope_id: ast::node_id) -> ~[uint] {
N
Niko Matsakis 已提交
133 134
        //! Returns a vector of the loans that are generated as
        //! we encounter `scope_id`.
135

N
Niko Matsakis 已提交
136
        let mut result = ~[];
137
        for self.dfcx_loans.each_gen_bit(scope_id) |loan_index| {
N
Niko Matsakis 已提交
138 139 140
            result.push(loan_index);
        }
        return result;
141 142
    }

143
    pub fn check_for_conflicting_loans(&mut self, scope_id: ast::node_id) {
N
Niko Matsakis 已提交
144 145 146 147
        //! Checks to see whether any of the loans that are issued
        //! by `scope_id` conflict with loans that have already been
        //! issued when we enter `scope_id` (for example, we do not
        //! permit two `&mut` borrows of the same variable).
148

N
Niko Matsakis 已提交
149
        debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);
150

N
Niko Matsakis 已提交
151 152
        let new_loan_indices = self.loans_generated_by(scope_id);
        debug!("new_loan_indices = %?", new_loan_indices);
153

N
Niko Matsakis 已提交
154 155 156 157
        for self.each_issued_loan(scope_id) |issued_loan| {
            for new_loan_indices.each |&new_loan_index| {
                let new_loan = &self.all_loans[new_loan_index];
                self.report_error_if_loans_conflict(issued_loan, new_loan);
158 159 160
            }
        }

N
Niko Matsakis 已提交
161 162 163 164 165
        for uint::range(0, new_loan_indices.len()) |i| {
            let old_loan = &self.all_loans[new_loan_indices[i]];
            for uint::range(i+1, new_loan_indices.len()) |j| {
                let new_loan = &self.all_loans[new_loan_indices[j]];
                self.report_error_if_loans_conflict(old_loan, new_loan);
166 167 168 169
            }
        }
    }

170 171 172
    pub fn report_error_if_loans_conflict(&self,
                                          old_loan: &Loan,
                                          new_loan: &Loan) {
N
Niko Matsakis 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        //! Checks whether `old_loan` and `new_loan` can safely be issued
        //! simultaneously.

        debug!("report_error_if_loans_conflict(old_loan=%s, new_loan=%s)",
               old_loan.repr(self.tcx()),
               new_loan.repr(self.tcx()));

        // Should only be called for loans that are in scope at the same time.
        let region_maps = self.tcx().region_maps;
        assert!(region_maps.scopes_intersect(old_loan.kill_scope,
                                             new_loan.kill_scope));

        self.report_error_if_loan_conflicts_with_restriction(
            old_loan, new_loan, old_loan, new_loan) &&
        self.report_error_if_loan_conflicts_with_restriction(
            new_loan, old_loan, old_loan, new_loan);
    }
190

191 192 193 194 195 196
    pub fn report_error_if_loan_conflicts_with_restriction(&self,
                                                           loan1: &Loan,
                                                           loan2: &Loan,
                                                           old_loan: &Loan,
                                                           new_loan: &Loan)
                                                           -> bool {
N
Niko Matsakis 已提交
197 198 199 200 201 202 203 204
        //! Checks whether the restrictions introduced by `loan1` would
        //! prohibit `loan2`. Returns false if an error is reported.

        debug!("report_error_if_loan_conflicts_with_restriction(\
                loan1=%s, loan2=%s)",
               loan1.repr(self.tcx()),
               loan2.repr(self.tcx()));

205
        // Restrictions that would cause the new loan to be illegal:
N
Niko Matsakis 已提交
206
        let illegal_if = match loan2.mutbl {
207
            m_mutbl => RESTR_ALIAS | RESTR_FREEZE | RESTR_CLAIM,
N
Niko Matsakis 已提交
208 209 210 211 212 213 214 215
            m_imm =>   RESTR_ALIAS | RESTR_FREEZE,
            m_const => RESTR_ALIAS,
        };
        debug!("illegal_if=%?", illegal_if);

        for loan1.restrictions.each |restr| {
            if !restr.set.intersects(illegal_if) { loop; }
            if restr.loan_path != loan2.loan_path { loop; }
216

N
Niko Matsakis 已提交
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
            match (new_loan.mutbl, old_loan.mutbl) {
                (m_mutbl, m_mutbl) => {
                    self.bccx.span_err(
                        new_loan.span,
                        fmt!("cannot borrow `%s` as mutable \
                              more than once at at a time",
                             self.bccx.loan_path_to_str(new_loan.loan_path)));
                    self.bccx.span_note(
                        old_loan.span,
                        fmt!("second borrow of `%s` as mutable occurs here",
                             self.bccx.loan_path_to_str(new_loan.loan_path)));
                    return false;
                }

                _ => {
                    self.bccx.span_err(
                        new_loan.span,
                        fmt!("cannot borrow `%s` as %s because \
                              it is also borrowed as %s"
                             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)));
                    self.bccx.span_note(
                        old_loan.span,
                        fmt!("second borrow of `%s` occurs here",
                             self.bccx.loan_path_to_str(new_loan.loan_path)));
                    return false;
                }
245 246
            }
        }
N
Niko Matsakis 已提交
247 248

        true
249 250
    }

251
    pub fn is_local_variable(&self, cmt: mc::cmt) -> bool {
252
        match cmt.cat {
N
Niko Matsakis 已提交
253
          mc::cat_local(_) => true,
B
Brian Anderson 已提交
254
          _ => false
255 256 257
        }
    }

258 259 260 261 262
    pub fn check_if_path_is_moved(&self,
                                  id: ast::node_id,
                                  span: span,
                                  use_kind: MovedValueUseKind,
                                  lp: @LoanPath) {
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
        /*!
         * Reports an error if `expr` (which should be a path)
         * is using a moved/uninitialized value
         */

        debug!("check_if_path_is_moved(id=%?, use_kind=%?, lp=%s)",
               id, use_kind, lp.repr(self.bccx.tcx));
        for self.move_data.each_move_of(id, lp) |move, moved_lp| {
            self.bccx.report_use_of_moved_value(
                span,
                use_kind,
                lp,
                move,
                moved_lp);
            return;
        }
    }

281
    pub fn check_assignment(&self, expr: @ast::expr) {
282 283
        // We don't use cat_expr() here because we don't want to treat
        // auto-ref'd parameters in overloaded operators as rvalues.
N
Niko Matsakis 已提交
284 285 286
        let cmt = match self.bccx.tcx.adjustments.find(&expr.id) {
            None => self.bccx.cat_expr_unadjusted(expr),
            Some(&adj) => self.bccx.cat_expr_autoderefd(expr, adj)
287
        };
288

N
Niko Matsakis 已提交
289 290
        debug!("check_assignment(cmt=%s)", cmt.repr(self.tcx()));

291 292 293 294 295 296 297 298 299
        // Mutable values can be assigned, as long as they obey loans
        // and aliasing restrictions:
        if cmt.mutbl.is_mutable() {
            if check_for_aliasable_mutable_writes(self, expr, cmt) {
                if check_for_assignment_to_restricted_or_frozen_location(
                    self, expr, cmt)
                {
                    // Safe, but record for lint pass later:
                    mark_variable_as_used_mut(self, cmt);
300
                }
N
Niko Matsakis 已提交
301
            }
302 303 304 305 306 307 308 309 310 311 312 313 314
            return;
        }

        // For immutable local variables, assignments are legal
        // if they cannot already have been assigned
        if self.is_local_variable(cmt) {
            assert!(cmt.mutbl.is_immutable()); // no "const" locals
            let lp = opt_loan_path(cmt).get();
            for self.move_data.each_assignment_of(expr.id, lp) |assign| {
                self.bccx.report_reassigned_immutable_variable(
                    expr.span,
                    lp,
                    assign);
N
Niko Matsakis 已提交
315
                return;
316
            }
317
            return;
318 319
        }

320 321 322 323 324 325 326
        // Otherwise, just a plain error.
        self.bccx.span_err(
            expr.span,
            fmt!("cannot assign to %s %s"
                 cmt.mutbl.to_user_str(),
                 self.bccx.cmt_to_str(cmt)));
        return;
327

328
        fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
329
                                     cmt: mc::cmt) {
N
Niko Matsakis 已提交
330
            //! If the mutability of the `cmt` being written is inherited
331
            //! from a local variable, liveness will
N
Niko Matsakis 已提交
332 333
            //! not have been able to detect that this variable's mutability
            //! is important, so we must add the variable to the
334
            //! `used_mut_nodes` table here.
N
Niko Matsakis 已提交
335 336 337 338

            let mut cmt = cmt;
            loop {
                debug!("mark_writes_through_upvars_as_used_mut(cmt=%s)",
339
                       cmt.repr(this.tcx()));
N
Niko Matsakis 已提交
340 341
                match cmt.cat {
                    mc::cat_local(id) |
342
                    mc::cat_arg(id) |
N
Niko Matsakis 已提交
343
                    mc::cat_self(id) => {
344
                        this.tcx().used_mut_nodes.insert(id);
N
Niko Matsakis 已提交
345 346
                        return;
                    }
347

N
Niko Matsakis 已提交
348 349 350
                    mc::cat_stack_upvar(b) => {
                        cmt = b;
                    }
351

N
Niko Matsakis 已提交
352 353 354 355 356 357 358 359 360 361
                    mc::cat_rvalue |
                    mc::cat_static_item |
                    mc::cat_implicit_self |
                    mc::cat_copied_upvar(*) |
                    mc::cat_deref(_, _, mc::unsafe_ptr(*)) |
                    mc::cat_deref(_, _, mc::gc_ptr(*)) |
                    mc::cat_deref(_, _, mc::region_ptr(*)) => {
                        assert_eq!(cmt.mutbl, mc::McDeclared);
                        return;
                    }
362

N
Niko Matsakis 已提交
363 364 365 366 367 368
                    mc::cat_discr(b, _) |
                    mc::cat_deref(b, _, mc::uniq_ptr(*)) => {
                        assert_eq!(cmt.mutbl, mc::McInherited);
                        cmt = b;
                    }

369
                    mc::cat_downcast(b) |
N
Niko Matsakis 已提交
370 371 372 373 374 375
                    mc::cat_interior(b, _) => {
                        if cmt.mutbl == mc::McInherited {
                            cmt = b;
                        } else {
                            return; // field declared as mutable or some such
                        }
376 377 378 379
                    }
                }
            }
        }
380

381
        fn check_for_aliasable_mutable_writes(this: &CheckLoanCtxt,
N
Niko Matsakis 已提交
382 383 384 385 386
                                              expr: @ast::expr,
                                              cmt: mc::cmt) -> bool {
            //! Safety checks related to writes to aliasable, mutable locations

            let guarantor = cmt.guarantor();
387
            debug!("check_for_aliasable_mutable_writes(cmt=%s, guarantor=%s)",
388
                   cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
N
Niko Matsakis 已提交
389 390 391 392 393 394 395
            match guarantor.cat {
                mc::cat_deref(b, _, mc::region_ptr(m_mutbl, _)) => {
                    // Statically prohibit writes to `&mut` when aliasable

                    match b.freely_aliasable() {
                        None => {}
                        Some(cause) => {
396
                            this.bccx.report_aliasability_violation(
N
Niko Matsakis 已提交
397 398 399 400 401
                                expr.span,
                                MutabilityViolation,
                                cause);
                        }
                    }
402
                }
N
Niko Matsakis 已提交
403

404
                mc::cat_deref(_, deref_count, mc::gc_ptr(ast::m_mutbl)) => {
N
Niko Matsakis 已提交
405 406 407
                    // Dynamically check writes to `@mut`

                    let key = root_map_key {
408
                        id: guarantor.id,
N
Niko Matsakis 已提交
409 410
                        derefs: deref_count
                    };
411
                    debug!("Inserting write guard at %?", key);
412
                    this.bccx.write_guard_map.insert(key);
N
Niko Matsakis 已提交
413 414 415
                }

                _ => {}
416 417
            }

N
Niko Matsakis 已提交
418
            return true; // no errors reported
419 420
        }

N
Niko Matsakis 已提交
421
        fn check_for_assignment_to_restricted_or_frozen_location(
422
            this: &CheckLoanCtxt,
N
Niko Matsakis 已提交
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
            expr: @ast::expr,
            cmt: mc::cmt) -> bool
        {
            //! Check for assignments that violate the terms of an
            //! outstanding loan.

            let loan_path = match opt_loan_path(cmt) {
                Some(lp) => lp,
                None => { return true; /* no loan path, can't be any loans */ }
            };

            // Start by searching for an assignment to a *restricted*
            // location. Here is one example of the kind of error caught
            // by this check:
            //
            //    let mut v = ~[1, 2, 3];
            //    let p = &v;
            //    v = ~[4];
            //
            // In this case, creating `p` triggers a RESTR_MUTATE
            // restriction on the path `v`.
            //
            // Here is a second, more subtle example:
            //
            //    let mut v = ~[1, 2, 3];
            //    let p = &const v[0];
            //    v[0] = 4;                   // OK
            //    v[1] = 5;                   // OK
            //    v = ~[4, 5, 3];             // Error
            //
            // In this case, `p` is pointing to `v[0]`, and it is a
            // `const` pointer in any case. So the first two
            // assignments are legal (and would be permitted by this
            // check). However, the final assignment (which is
            // logically equivalent) is forbidden, because it would
            // cause the existing `v` array to be freed, thus
            // invalidating `p`. In the code, this error results
            // because `gather_loans::restrictions` adds a
            // `RESTR_MUTATE` restriction whenever the contents of an
            // owned pointer are borrowed, and hence while `v[*]` is not
            // restricted from being written, `v` is.
464
            for this.each_in_scope_restriction(expr.id, loan_path)
N
Niko Matsakis 已提交
465 466 467
                |loan, restr|
            {
                if restr.set.intersects(RESTR_MUTATE) {
468
                    this.report_illegal_mutation(expr, loan_path, loan);
N
Niko Matsakis 已提交
469 470 471 472 473 474 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 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
                    return false;
                }
            }

            // The previous code handled assignments to paths that
            // have been restricted. This covers paths that have been
            // directly lent out and their base paths, but does not
            // cover random extensions of those paths. For example,
            // the following program is not declared illegal by the
            // previous check:
            //
            //    let mut v = ~[1, 2, 3];
            //    let p = &v;
            //    v[0] = 4; // declared error by loop below, not code above
            //
            // The reason that this passes the previous check whereas
            // an assignment like `v = ~[4]` fails is because the assignment
            // here is to `v[*]`, and the existing restrictions were issued
            // for `v`, not `v[*]`.
            //
            // So in this loop, we walk back up the loan path so long
            // as the mutability of the path is dependent on a super
            // path, and check that the super path was not lent out as
            // mutable or immutable (a const loan is ok).
            //
            // Note that we are *not* checking for any and all
            // restrictions.  We are only interested in the pointers
            // that the user created, whereas we add restrictions for
            // all kinds of paths that are not directly aliased. If we checked
            // for all restrictions, and not just loans, then the following
            // valid program would be considered illegal:
            //
            //    let mut v = ~[1, 2, 3];
            //    let p = &const v[0];
            //    v[1] = 5; // ok
            //
            // Here the restriction that `v` not be mutated would be misapplied
            // to block the subpath `v[1]`.
            let full_loan_path = loan_path;
            let mut loan_path = loan_path;
            loop {
                match *loan_path {
                    // Peel back one layer if `loan_path` has
                    // inherited mutability
                    LpExtend(lp_base, mc::McInherited, _) => {
                        loan_path = lp_base;
                    }

                    // Otherwise stop iterating
                    LpExtend(_, mc::McDeclared, _) |
                    LpExtend(_, mc::McImmutable, _) |
                    LpExtend(_, mc::McReadOnly, _) |
                    LpVar(_) => {
                        return true;
                    }
                }

                // Check for a non-const loan of `loan_path`
527
                for this.each_in_scope_loan(expr.id) |loan| {
N
Niko Matsakis 已提交
528
                    if loan.loan_path == loan_path && loan.mutbl != m_const {
529
                        this.report_illegal_mutation(expr, full_loan_path, loan);
N
Niko Matsakis 已提交
530 531 532
                        return false;
                    }
                }
533 534 535 536
            }
        }
    }

537 538 539 540
    pub fn report_illegal_mutation(&self,
                                   expr: @ast::expr,
                                   loan_path: &LoanPath,
                                   loan: &Loan) {
N
Niko Matsakis 已提交
541 542 543 544 545 546 547 548 549 550
        self.bccx.span_err(
            expr.span,
            fmt!("cannot assign to `%s` because it is borrowed",
                 self.bccx.loan_path_to_str(loan_path)));
        self.bccx.span_note(
            loan.span,
            fmt!("borrow of `%s` occurs here",
                 self.bccx.loan_path_to_str(loan_path)));
    }

551
    pub fn check_move_out_from_expr(&self, ex: @ast::expr) {
552 553 554 555 556 557 558 559 560 561
        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 => {}
562
                    MoveWhileBorrowed(move_path, loan_path, loan_span) => {
563 564
                        self.bccx.span_err(
                            cmt.span,
N
Niko Matsakis 已提交
565 566
                            fmt!("cannot move out of `%s` \
                                  because it is borrowed",
567
                                 self.bccx.loan_path_to_str(move_path)));
568
                        self.bccx.span_note(
N
Niko Matsakis 已提交
569 570 571
                            loan_span,
                            fmt!("borrow of `%s` occurs here",
                                 self.bccx.loan_path_to_str(loan_path)));
572 573 574 575
                    }
                }
            }
        }
576 577
    }

578
    pub fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
579
        debug!("analyze_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
580

N
Niko Matsakis 已提交
581
        // FIXME(#4384) inadequare if/when we permit `move a.b`
582 583

        // check for a conflicting loan:
N
Niko Matsakis 已提交
584 585 586
        for opt_loan_path(cmt).each |&lp| {
            for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
                // Any restriction prevents moves.
587
                return MoveWhileBorrowed(lp, loan.loan_path, loan.span);
588
            }
589
        }
590 591

        return MoveOk;
592 593
    }

594 595 596 597 598 599
    pub fn check_call(&mut self,
                      _expr: @ast::expr,
                      _callee: Option<@ast::expr>,
                      _callee_id: ast::node_id,
                      _callee_span: span,
                      _args: &[@ast::expr]) {
N
Niko Matsakis 已提交
600 601 602 603 604
        // NB: This call to check for conflicting loans is not truly
        // necessary, because the callee_id never issues new loans.
        // However, I added it for consistency and lest the system
        // should change in the future.
        //
N
Niko Matsakis 已提交
605
        // FIXME(#6268) nested method calls
N
Niko Matsakis 已提交
606
        // self.check_for_conflicting_loans(callee_id);
607
    }
608 609
}

N
Niko Matsakis 已提交
610 611 612 613 614
fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
                         decl: &ast::fn_decl,
                         body: &ast::blk,
                         sp: span,
                         id: ast::node_id,
615
                         this: @mut CheckLoanCtxt<'a>,
N
Niko Matsakis 已提交
616
                         visitor: visit::vt<@mut CheckLoanCtxt<'a>>) {
617
    match *fk {
N
Niko Matsakis 已提交
618
        visit::fk_item_fn(*) |
619
        visit::fk_method(*) => {
N
Niko Matsakis 已提交
620 621
            // Don't process nested items.
            return;
622 623
        }

N
Niko Matsakis 已提交
624 625
        visit::fk_anon(*) |
        visit::fk_fn_block(*) => {
626
            check_captured_variables(this, id, sp);
627 628
        }
    }
629

630
    visit::visit_fn(fk, decl, body, sp, id, this, visitor);
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
    fn check_captured_variables(this: @mut CheckLoanCtxt,
                                closure_id: ast::node_id,
                                span: span) {
        let cap_vars = this.bccx.capture_map.get(&closure_id);
        for cap_vars.each |cap_var| {
            match cap_var.mode {
                moves::CapRef | moves::CapCopy => {
                    let var_id = ast_util::def_id_of_def(cap_var.def).node;
                    let lp = @LpVar(var_id);
                    this.check_if_path_is_moved(closure_id, span,
                                                MovedInCapture, lp);
                }
                moves::CapMove => {
                    check_by_move_capture(this, closure_id, cap_var);
                }
            }
        }
        return;

        fn check_by_move_capture(this: @mut CheckLoanCtxt,
                                 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);
            match move_err {
                MoveOk => {}
661
                MoveWhileBorrowed(move_path, loan_path, loan_span) => {
662 663 664 665
                    this.bccx.span_err(
                        cap_var.span,
                        fmt!("cannot move `%s` into closure \
                              because it is borrowed",
666
                             this.bccx.loan_path_to_str(move_path)));
667 668 669 670
                    this.bccx.span_note(
                        loan_span,
                        fmt!("borrow of `%s` occurs here",
                             this.bccx.loan_path_to_str(loan_path)));
671 672 673 674
                }
            }
        }
    }
675 676
}

N
Niko Matsakis 已提交
677
fn check_loans_in_local<'a>(local: @ast::local,
678
                            this: @mut CheckLoanCtxt<'a>,
N
Niko Matsakis 已提交
679
                            vt: visit::vt<@mut CheckLoanCtxt<'a>>) {
680
    visit::visit_local(local, this, vt);
681 682
}

N
Niko Matsakis 已提交
683
fn check_loans_in_expr<'a>(expr: @ast::expr,
684
                           this: @mut CheckLoanCtxt<'a>,
N
Niko Matsakis 已提交
685
                           vt: visit::vt<@mut CheckLoanCtxt<'a>>) {
686 687
    visit::visit_expr(expr, this, vt);

N
Niko Matsakis 已提交
688
    debug!("check_loans_in_expr(expr=%s)",
689
           expr.repr(this.tcx()));
N
Niko Matsakis 已提交
690

691
    this.check_for_conflicting_loans(expr.id);
692

693 694
    if this.bccx.moves_map.contains(&expr.id) {
        this.check_move_out_from_expr(expr);
695 696
    }

697
    match expr.node {
698 699 700 701 702 703 704 705 706 707 708
      ast::expr_self |
      ast::expr_path(*) => {
          if !this.move_data.is_assignee(expr.id) {
              let cmt = this.bccx.cat_expr_unadjusted(expr);
              debug!("path cmt=%s", cmt.repr(this.tcx()));
              for opt_loan_path(cmt).each |&lp| {
                  this.check_if_path_is_moved(expr.id, expr.span,
                                              MovedInUse, lp);
              }
          }
      }
709
      ast::expr_assign(dest, _) |
710
      ast::expr_assign_op(_, _, dest, _) => {
711
        this.check_assignment(dest);
712
      }
713
      ast::expr_call(f, ref args, _) => {
714
        this.check_call(expr, Some(f), f.id, f.span, *args);
715
      }
716 717
      ast::expr_method_call(callee_id, _, _, _, ref args, _) => {
        this.check_call(expr, None, callee_id, expr.span, *args);
718
      }
719 720
      ast::expr_index(callee_id, _, rval) |
      ast::expr_binary(callee_id, _, _, rval)
721 722
      if this.bccx.method_map.contains_key(&expr.id) => {
        this.check_call(expr,
B
Brian Anderson 已提交
723
                        None,
724
                        callee_id,
725
                        expr.span,
726
                        [rval]);
727
      }
728
      ast::expr_unary(callee_id, _, _) | ast::expr_index(callee_id, _, _)
729 730
      if this.bccx.method_map.contains_key(&expr.id) => {
        this.check_call(expr,
B
Brian Anderson 已提交
731
                        None,
732
                        callee_id,
733
                        expr.span,
734
                        []);
735
      }
B
Brian Anderson 已提交
736
      _ => { }
737 738 739
    }
}

N
Niko Matsakis 已提交
740
fn check_loans_in_pat<'a>(pat: @ast::pat,
741
                          this: @mut CheckLoanCtxt<'a>,
N
Niko Matsakis 已提交
742 743
                          vt: visit::vt<@mut CheckLoanCtxt<'a>>)
{
744
    this.check_for_conflicting_loans(pat.id);
N
Niko Matsakis 已提交
745 746 747 748 749 750 751 752 753 754 755 756

    // 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.

757
    visit::visit_pat(pat, this, vt);
N
Niko Matsakis 已提交
758
}
759

N
Niko Matsakis 已提交
760
fn check_loans_in_block<'a>(blk: &ast::blk,
761
                            this: @mut CheckLoanCtxt<'a>,
N
Niko Matsakis 已提交
762 763
                            vt: visit::vt<@mut CheckLoanCtxt<'a>>)
{
764 765
    visit::visit_block(blk, this, vt);
    this.check_for_conflicting_loans(blk.node.id);
766
}