check_loans.rs 32.9 KB
Newer Older
1
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// 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

N
Niko Matsakis 已提交
21
use mc = middle::mem_categorization;
22 23
use middle::borrowck::*;
use middle::moves;
24
use middle::ty;
25
use middle::typeck::MethodCall;
26
use std::vec_ng::Vec;
27 28
use syntax::ast;
use syntax::ast_util;
29
use syntax::codemap::Span;
30
use syntax::visit::Visitor;
31
use syntax::visit;
32
use util::ppaux::Repr;
33

E
Erik Price 已提交
34
struct CheckLoanCtxt<'a> {
E
Eduard Burtescu 已提交
35 36 37
    bccx: &'a BorrowckCtxt<'a>,
    dfcx_loans: &'a LoanDataFlow<'a>,
    move_data: move_data::FlowedMoveData<'a>,
E
Erik Price 已提交
38
    all_loans: &'a [Loan],
39 40
}

E
Erik Price 已提交
41
impl<'a> Visitor<()> for CheckLoanCtxt<'a> {
42

E
Eduard Burtescu 已提交
43
    fn visit_expr(&mut self, ex: &ast::Expr, _: ()) {
44
        check_loans_in_expr(self, ex);
45
    }
E
Eduard Burtescu 已提交
46
    fn visit_local(&mut self, l: &ast::Local, _: ()) {
47
        check_loans_in_local(self, l);
48
    }
E
Eduard Burtescu 已提交
49
    fn visit_block(&mut self, b: &ast::Block, _: ()) {
50
        check_loans_in_block(self, b);
51
    }
E
Eduard Burtescu 已提交
52
    fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
53
        check_loans_in_pat(self, p);
54
    }
55 56 57 58 59
    fn visit_fn(&mut self, _fk: &visit::FnKind, _fd: &ast::FnDecl,
                _b: &ast::Block, _s: Span, _n: ast::NodeId, _: ()) {
        // Don't process nested items or closures here,
        // the outer loop will take care of it.
        return;
60
    }
61 62 63

    // FIXME(#10894) should continue recursing
    fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
64 65
}

66
pub fn check_loans(bccx: &BorrowckCtxt,
67 68
                   dfcx_loans: &LoanDataFlow,
                   move_data: move_data::FlowedMoveData,
N
Niko Matsakis 已提交
69
                   all_loans: &[Loan],
E
Eduard Burtescu 已提交
70
                   body: &ast::Block) {
71
    debug!("check_loans(body id={:?})", body.id);
N
Niko Matsakis 已提交
72

73
    let mut clcx = CheckLoanCtxt {
74
        bccx: bccx,
75
        dfcx_loans: dfcx_loans,
E
Eduard Burtescu 已提交
76
        move_data: move_data,
N
Niko Matsakis 已提交
77
        all_loans: all_loans,
78
    };
N
Niko Matsakis 已提交
79

80
    clcx.visit_block(body, ());
81 82
}

83
#[deriving(Eq)]
N
Niko Matsakis 已提交
84 85
enum MoveError {
    MoveOk,
86
    MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/Span)
87 88
}

E
Erik Price 已提交
89
impl<'a> CheckLoanCtxt<'a> {
E
Eduard Burtescu 已提交
90
    pub fn tcx(&self) -> &'a ty::ctxt { self.bccx.tcx }
91

92
    pub fn each_issued_loan(&self, scope_id: ast::NodeId, op: |&Loan| -> bool)
93
                            -> bool {
94
        //! Iterates over each loan that has been issued
95 96 97 98
        //! 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.
99

100
        self.dfcx_loans.each_bit_on_entry_frozen(scope_id, |loan_index| {
101
            let loan = &self.all_loans[loan_index];
102
            op(loan)
103
        })
104 105
    }

106
    pub fn each_in_scope_loan(&self,
107
                              scope_id: ast::NodeId,
108
                              op: |&Loan| -> bool)
109
                              -> bool {
110 111
        //! Like `each_issued_loan()`, but only considers loans that are
        //! currently in scope.
112

E
Eduard Burtescu 已提交
113
        let tcx = self.tcx();
114
        self.each_issued_loan(scope_id, |loan| {
E
Eduard Burtescu 已提交
115
            if tcx.region_maps.is_subscope_of(scope_id, loan.kill_scope) {
116 117 118
                op(loan)
            } else {
                true
119
            }
120
        })
121 122
    }

123
    pub fn each_in_scope_restriction(&self,
124
                                     scope_id: ast::NodeId,
125
                                     loan_path: @LoanPath,
126
                                     op: |&Loan, &Restriction| -> bool)
127
                                     -> bool {
128 129 130
        //! Iterates through all the in-scope restrictions for the
        //! given `loan_path`

131
        self.each_in_scope_loan(scope_id, |loan| {
132 133 134
            debug!("each_in_scope_restriction found loan: {:?}",
                   loan.repr(self.tcx()));

135
            let mut ret = true;
D
Daniel Micay 已提交
136
            for restr in loan.restrictions.iter() {
137 138
                if restr.loan_path == loan_path {
                    if !op(loan, restr) {
139 140
                        ret = false;
                        break;
141 142 143
                    }
                }
            }
144
            ret
145
        })
146
    }
147

148
    pub fn loans_generated_by(&self, scope_id: ast::NodeId) -> Vec<uint> {
N
Niko Matsakis 已提交
149 150
        //! Returns a vector of the loans that are generated as
        //! we encounter `scope_id`.
151

152
        let mut result = Vec::new();
153
        self.dfcx_loans.each_gen_bit_frozen(scope_id, |loan_index| {
N
Niko Matsakis 已提交
154
            result.push(loan_index);
155
            true
156
        });
N
Niko Matsakis 已提交
157
        return result;
158 159
    }

160
    pub fn check_for_conflicting_loans(&self, scope_id: ast::NodeId) {
N
Niko Matsakis 已提交
161 162 163 164
        //! 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).
165

166
        debug!("check_for_conflicting_loans(scope_id={:?})", scope_id);
167

N
Niko Matsakis 已提交
168
        let new_loan_indices = self.loans_generated_by(scope_id);
169
        debug!("new_loan_indices = {:?}", new_loan_indices);
170

171
        self.each_issued_loan(scope_id, |issued_loan| {
D
Daniel Micay 已提交
172
            for &new_loan_index in new_loan_indices.iter() {
N
Niko Matsakis 已提交
173 174
                let new_loan = &self.all_loans[new_loan_index];
                self.report_error_if_loans_conflict(issued_loan, new_loan);
175
            }
176
            true
177
        });
178

179 180 181 182
        for (i, &x) in new_loan_indices.iter().enumerate() {
            let old_loan = &self.all_loans[x];
            for &y in new_loan_indices.slice_from(i+1).iter() {
                let new_loan = &self.all_loans[y];
N
Niko Matsakis 已提交
183
                self.report_error_if_loans_conflict(old_loan, new_loan);
184 185 186 187
            }
        }
    }

188 189 190
    pub fn report_error_if_loans_conflict(&self,
                                          old_loan: &Loan,
                                          new_loan: &Loan) {
N
Niko Matsakis 已提交
191 192 193
        //! Checks whether `old_loan` and `new_loan` can safely be issued
        //! simultaneously.

194
        debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
N
Niko Matsakis 已提交
195 196 197 198
               old_loan.repr(self.tcx()),
               new_loan.repr(self.tcx()));

        // Should only be called for loans that are in scope at the same time.
E
Eduard Burtescu 已提交
199 200
        assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
                                                        new_loan.kill_scope));
N
Niko Matsakis 已提交
201 202 203 204 205 206

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

208 209 210 211 212 213
    pub fn report_error_if_loan_conflicts_with_restriction(&self,
                                                           loan1: &Loan,
                                                           loan2: &Loan,
                                                           old_loan: &Loan,
                                                           new_loan: &Loan)
                                                           -> bool {
N
Niko Matsakis 已提交
214 215 216
        //! Checks whether the restrictions introduced by `loan1` would
        //! prohibit `loan2`. Returns false if an error is reported.

217
        debug!("report_error_if_loan_conflicts_with_restriction(\
A
Alex Crichton 已提交
218
                loan1={}, loan2={})",
N
Niko Matsakis 已提交
219 220 221
               loan1.repr(self.tcx()),
               loan2.repr(self.tcx()));

222
        // Restrictions that would cause the new loan to be illegal:
223 224 225 226 227 228 229 230 231 232 233 234 235
        let illegal_if = match loan2.kind {
            // Look for restrictions against mutation. These are
            // generated by all other borrows.
            ty::MutBorrow => RESTR_MUTATE,

            // Look for restrictions against freezing (immutable borrows).
            // These are generated by `&mut` borrows.
            ty::ImmBorrow => RESTR_FREEZE,

            // No matter how the data is borrowed (as `&`, as `&mut`,
            // or as `&unique imm`) it will always generate a
            // restriction against mutating the data. So look for those.
            ty::UniqueImmBorrow => RESTR_MUTATE,
N
Niko Matsakis 已提交
236
        };
237
        debug!("illegal_if={:?}", illegal_if);
N
Niko Matsakis 已提交
238

D
Daniel Micay 已提交
239
        for restr in loan1.restrictions.iter() {
240 241
            if !restr.set.intersects(illegal_if) { continue; }
            if restr.loan_path != loan2.loan_path { continue; }
242

243 244 245 246 247 248 249 250 251
            let old_pronoun = if new_loan.loan_path == old_loan.loan_path {
                ~"it"
            } else {
                format!("`{}`",
                        self.bccx.loan_path_to_str(old_loan.loan_path))
            };

            match (new_loan.kind, old_loan.kind) {
                (ty::MutBorrow, ty::MutBorrow) => {
N
Niko Matsakis 已提交
252 253
                    self.bccx.span_err(
                        new_loan.span,
254 255 256 257 258 259 260 261 262 263 264 265
                        format!("cannot borrow `{}` as mutable \
                                more than once at a time",
                                self.bccx.loan_path_to_str(new_loan.loan_path)));
                }

                (ty::UniqueImmBorrow, _) => {
                    self.bccx.span_err(
                        new_loan.span,
                        format!("closure requires unique access to `{}` \
                                but {} is already borrowed",
                                self.bccx.loan_path_to_str(new_loan.loan_path),
                                old_pronoun));
N
Niko Matsakis 已提交
266 267
                }

268
                (_, ty::UniqueImmBorrow) => {
N
Niko Matsakis 已提交
269 270
                    self.bccx.span_err(
                        new_loan.span,
A
Alex Crichton 已提交
271
                        format!("cannot borrow `{}` as {} because \
272 273 274 275 276 277 278 279 280 281 282 283 284 285
                                previous closure requires unique access",
                                self.bccx.loan_path_to_str(new_loan.loan_path),
                                new_loan.kind.to_user_str()));
                }

                (_, _) => {
                    self.bccx.span_err(
                        new_loan.span,
                        format!("cannot borrow `{}` as {} because \
                                {} is also borrowed as {}",
                                self.bccx.loan_path_to_str(new_loan.loan_path),
                                new_loan.kind.to_user_str(),
                                old_pronoun,
                                old_loan.kind.to_user_str()));
N
Niko Matsakis 已提交
286
                }
287
            }
288

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
            match new_loan.cause {
                ClosureCapture(span) => {
                    self.bccx.span_note(
                        span,
                        format!("borrow occurs due to use of `{}` in closure",
                                self.bccx.loan_path_to_str(new_loan.loan_path)));
                }
                _ => { }
            }

            let rule_summary = match old_loan.kind {
                ty::MutBorrow => {
                    format!("the mutable borrow prevents subsequent \
                            moves, borrows, or modification of `{0}` \
                            until the borrow ends",
                            self.bccx.loan_path_to_str(old_loan.loan_path))
                }

                ty::ImmBorrow => {
                    format!("the immutable borrow prevents subsequent \
                            moves or mutable borrows of `{0}` \
                            until the borrow ends",
                            self.bccx.loan_path_to_str(old_loan.loan_path))
                }

                ty::UniqueImmBorrow => {
                    format!("the unique capture prevents subsequent \
                            moves or borrows of `{0}` \
                            until the borrow ends",
                            self.bccx.loan_path_to_str(old_loan.loan_path))
                }
            };

            let borrow_summary = match old_loan.cause {
                ClosureCapture(_) => {
                    format!("previous borrow of `{}` occurs here due to \
                            use in closure",
                            self.bccx.loan_path_to_str(old_loan.loan_path))
                }

                AddrOf | AutoRef | RefBinding => {
                    format!("previous borrow of `{}` occurs here",
                            self.bccx.loan_path_to_str(old_loan.loan_path))
                }
            };

            self.bccx.span_note(
                old_loan.span,
                format!("{}; {}", borrow_summary, rule_summary));

339
            let old_loan_span = self.tcx().map.span(old_loan.kill_scope);
340 341
            self.bccx.span_end_note(old_loan_span,
                                    "previous borrow ends here");
342

343
            return false;
344
        }
N
Niko Matsakis 已提交
345 346

        true
347 348
    }

349
    pub fn is_local_variable(&self, cmt: mc::cmt) -> bool {
350
        match cmt.cat {
N
Niko Matsakis 已提交
351
          mc::cat_local(_) => true,
B
Brian Anderson 已提交
352
          _ => false
353 354 355
        }
    }

356
    pub fn check_if_path_is_moved(&self,
357
                                  id: ast::NodeId,
358
                                  span: Span,
359 360
                                  use_kind: MovedValueUseKind,
                                  lp: @LoanPath) {
361 362 363 364 365
        /*!
         * Reports an error if `expr` (which should be a path)
         * is using a moved/uninitialized value
         */

366
        debug!("check_if_path_is_moved(id={:?}, use_kind={:?}, lp={})",
367
               id, use_kind, lp.repr(self.bccx.tcx));
368
        self.move_data.each_move_of(id, lp, |move, moved_lp| {
369 370 371 372 373 374
            self.bccx.report_use_of_moved_value(
                span,
                use_kind,
                lp,
                move,
                moved_lp);
375
            false
376
        });
377 378
    }

E
Eduard Burtescu 已提交
379
    pub fn check_assignment(&self, expr: &ast::Expr) {
380 381
        // We don't use cat_expr() here because we don't want to treat
        // auto-ref'd parameters in overloaded operators as rvalues.
382 383 384 385 386
        let adj = {
            let adjustments = self.bccx.tcx.adjustments.borrow();
            adjustments.get().find_copy(&expr.id)
        };
        let cmt = match adj {
N
Niko Matsakis 已提交
387
            None => self.bccx.cat_expr_unadjusted(expr),
388
            Some(adj) => self.bccx.cat_expr_autoderefd(expr, adj)
389
        };
390

391
        debug!("check_assignment(cmt={})", cmt.repr(self.tcx()));
N
Niko Matsakis 已提交
392

393 394 395 396 397 398 399 400 401
        // 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);
402
                }
N
Niko Matsakis 已提交
403
            }
404 405 406 407 408 409 410
            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
411
            let lp = opt_loan_path(cmt).unwrap();
412
            self.move_data.each_assignment_of(expr.id, lp, |assign| {
413 414 415 416
                self.bccx.report_reassigned_immutable_variable(
                    expr.span,
                    lp,
                    assign);
417
                false
418
            });
419
            return;
420 421
        }

422
        // Otherwise, just a plain error.
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
        match opt_loan_path(cmt) {
            Some(lp) => {
                self.bccx.span_err(
                    expr.span,
                    format!("cannot assign to {} {} `{}`",
                            cmt.mutbl.to_user_str(),
                            self.bccx.cmt_to_str(cmt),
                            self.bccx.loan_path_to_str(lp)));
            }
            None => {
                self.bccx.span_err(
                    expr.span,
                    format!("cannot assign to {} {}",
                            cmt.mutbl.to_user_str(),
                            self.bccx.cmt_to_str(cmt)));
            }
        }
440
        return;
441

442
        fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
443
                                     cmt: mc::cmt) {
N
Niko Matsakis 已提交
444
            //! If the mutability of the `cmt` being written is inherited
445
            //! from a local variable, liveness will
N
Niko Matsakis 已提交
446 447
            //! not have been able to detect that this variable's mutability
            //! is important, so we must add the variable to the
448
            //! `used_mut_nodes` table here.
N
Niko Matsakis 已提交
449 450 451

            let mut cmt = cmt;
            loop {
452
                debug!("mark_writes_through_upvars_as_used_mut(cmt={})",
453
                       cmt.repr(this.tcx()));
N
Niko Matsakis 已提交
454
                match cmt.cat {
455
                    mc::cat_local(id) | mc::cat_arg(id) => {
456 457 458 459
                        let mut used_mut_nodes = this.tcx()
                                                     .used_mut_nodes
                                                     .borrow_mut();
                        used_mut_nodes.get().insert(id);
N
Niko Matsakis 已提交
460 461
                        return;
                    }
462

463 464
                    mc::cat_upvar(..) => {
                        return;
N
Niko Matsakis 已提交
465
                    }
466

467
                    mc::cat_deref(_, _, mc::GcPtr) => {
468 469 470 471
                        assert_eq!(cmt.mutbl, mc::McImmutable);
                        return;
                    }

A
Alex Crichton 已提交
472
                    mc::cat_rvalue(..) |
N
Niko Matsakis 已提交
473
                    mc::cat_static_item |
A
Alex Crichton 已提交
474
                    mc::cat_copied_upvar(..) |
475 476
                    mc::cat_deref(_, _, mc::UnsafePtr(..)) |
                    mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
N
Niko Matsakis 已提交
477 478 479
                        assert_eq!(cmt.mutbl, mc::McDeclared);
                        return;
                    }
480

N
Niko Matsakis 已提交
481
                    mc::cat_discr(b, _) |
482
                    mc::cat_deref(b, _, mc::OwnedPtr) => {
N
Niko Matsakis 已提交
483 484 485 486
                        assert_eq!(cmt.mutbl, mc::McInherited);
                        cmt = b;
                    }

487
                    mc::cat_downcast(b) |
N
Niko Matsakis 已提交
488
                    mc::cat_interior(b, _) => {
489 490
                        assert_eq!(cmt.mutbl, mc::McInherited);
                        cmt = b;
491 492 493 494
                    }
                }
            }
        }
495

496
        fn check_for_aliasable_mutable_writes(this: &CheckLoanCtxt,
E
Eduard Burtescu 已提交
497
                                              expr: &ast::Expr,
N
Niko Matsakis 已提交
498 499 500 501
                                              cmt: mc::cmt) -> bool {
            //! Safety checks related to writes to aliasable, mutable locations

            let guarantor = cmt.guarantor();
502
            debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
503
                   cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
N
Niko Matsakis 已提交
504
            match guarantor.cat {
505
                mc::cat_deref(b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
N
Niko Matsakis 已提交
506 507
                    // Statically prohibit writes to `&mut` when aliasable

508
                    check_for_aliasability_violation(this, expr, b);
509
                }
N
Niko Matsakis 已提交
510 511

                _ => {}
512 513
            }

N
Niko Matsakis 已提交
514
            return true; // no errors reported
515 516
        }

517
        fn check_for_aliasability_violation(this: &CheckLoanCtxt,
E
Eduard Burtescu 已提交
518
                                            expr: &ast::Expr,
519 520 521 522 523 524
                                            cmt: mc::cmt)
                                            -> bool {
            match cmt.freely_aliasable() {
                None => {
                    return true;
                }
X
xales 已提交
525 526 527
                Some(mc::AliasableStaticMut) => {
                    return true;
                }
528 529 530 531 532 533
                Some(cause) => {
                    this.bccx.report_aliasability_violation(
                        expr.span,
                        MutabilityViolation,
                        cause);
                    return false;
534 535 536 537
                }
            }
        }

N
Niko Matsakis 已提交
538
        fn check_for_assignment_to_restricted_or_frozen_location(
539
            this: &CheckLoanCtxt,
E
Eduard Burtescu 已提交
540
            expr: &ast::Expr,
N
Niko Matsakis 已提交
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
            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.
581 582 583
            let cont = this.each_in_scope_restriction(expr.id,
                                                      loan_path,
                                                      |loan, restr| {
N
Niko Matsakis 已提交
584
                if restr.set.intersects(RESTR_MUTATE) {
585
                    this.report_illegal_mutation(expr, loan_path, loan);
586 587 588
                    false
                } else {
                    true
N
Niko Matsakis 已提交
589
                }
590
            });
591 592

            if !cont { return false }
N
Niko Matsakis 已提交
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614

            // 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).
            //
615 616 617 618 619 620
            // Mutability of a path can be dependent on the super path
            // in two ways. First, it might be inherited mutability.
            // Second, the pointee of an `&mut` pointer can only be
            // mutated if it is found in an unaliased location, so we
            // have to check that the owner location is not borrowed.
            //
N
Niko Matsakis 已提交
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
            // 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 {
638 639 640 641 642
                    // Peel back one layer if, for `loan_path` to be
                    // mutable, `lp_base` must be mutable. This occurs
                    // with inherited mutability and with `&mut`
                    // pointers.
                    LpExtend(lp_base, mc::McInherited, _) |
643
                    LpExtend(lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => {
N
Niko Matsakis 已提交
644 645 646 647 648 649 650 651 652 653 654 655
                        loan_path = lp_base;
                    }

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

                // Check for a non-const loan of `loan_path`
656
                let cont = this.each_in_scope_loan(expr.id, |loan| {
N
Niko Matsakis 已提交
657
                    if loan.loan_path == loan_path {
658
                        this.report_illegal_mutation(expr, full_loan_path, loan);
659 660 661
                        false
                    } else {
                        true
N
Niko Matsakis 已提交
662
                    }
663
                });
664 665

                if !cont { return false }
666 667 668 669
            }
        }
    }

670
    pub fn report_illegal_mutation(&self,
E
Eduard Burtescu 已提交
671
                                   expr: &ast::Expr,
672 673
                                   loan_path: &LoanPath,
                                   loan: &Loan) {
N
Niko Matsakis 已提交
674 675
        self.bccx.span_err(
            expr.span,
A
Alex Crichton 已提交
676
            format!("cannot assign to `{}` because it is borrowed",
N
Niko Matsakis 已提交
677 678 679
                 self.bccx.loan_path_to_str(loan_path)));
        self.bccx.span_note(
            loan.span,
A
Alex Crichton 已提交
680
            format!("borrow of `{}` occurs here",
N
Niko Matsakis 已提交
681 682 683
                 self.bccx.loan_path_to_str(loan_path)));
    }

E
Eduard Burtescu 已提交
684
    fn check_move_out_from_expr(&self, expr: &ast::Expr) {
685
        match expr.node {
A
Alex Crichton 已提交
686
            ast::ExprFnBlock(..) | ast::ExprProc(..) => {
687 688 689 690
                // Moves due to captures are checked in
                // check_captured_variables() because it allows
                // us to give a more precise error message with
                // a more precise span.
691 692
            }
            _ => {
693 694 695 696 697
                self.check_move_out_from_id(expr.id, expr.span)
            }
        }
    }

698
    fn check_move_out_from_id(&self, id: ast::NodeId, span: Span) {
699
        self.move_data.each_path_moved_by(id, |_, move_path| {
700 701 702 703 704
            match self.analyze_move_out_from(id, move_path) {
                MoveOk => {}
                MoveWhileBorrowed(loan_path, loan_span) => {
                    self.bccx.span_err(
                        span,
A
Alex Crichton 已提交
705
                        format!("cannot move out of `{}` \
706
                                because it is borrowed",
707 708 709
                             self.bccx.loan_path_to_str(move_path)));
                    self.bccx.span_note(
                        loan_span,
A
Alex Crichton 已提交
710
                        format!("borrow of `{}` occurs here",
711
                                self.bccx.loan_path_to_str(loan_path)));
712 713
                }
            }
714
            true
715
        });
716 717
    }

718 719 720
    fn check_captured_variables(&self,
                                closure_id: ast::NodeId,
                                span: Span) {
721
        for cap_var in self.bccx.capture_map.get(&closure_id).deref().iter() {
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
            let var_id = ast_util::def_id_of_def(cap_var.def).node;
            let var_path = @LpVar(var_id);
            self.check_if_path_is_moved(closure_id, span,
                                        MovedInCapture, var_path);
            match cap_var.mode {
                moves::CapRef | moves::CapCopy => {}
                moves::CapMove => {
                    check_by_move_capture(self, closure_id, cap_var, var_path);
                }
            }
        }
        return;

        fn check_by_move_capture(this: &CheckLoanCtxt,
                                 closure_id: ast::NodeId,
                                 cap_var: &moves::CaptureVar,
                                 move_path: @LoanPath) {
            let move_err = this.analyze_move_out_from(closure_id, move_path);
            match move_err {
                MoveOk => {}
                MoveWhileBorrowed(loan_path, loan_span) => {
                    this.bccx.span_err(
                        cap_var.span,
                        format!("cannot move `{}` into closure \
                                because it is borrowed",
                                this.bccx.loan_path_to_str(move_path)));
                    this.bccx.span_note(
                        loan_span,
                        format!("borrow of `{}` occurs here",
                                this.bccx.loan_path_to_str(loan_path)));
                }
            }
        }
    }

757
    pub fn analyze_move_out_from(&self,
758
                                 expr_id: ast::NodeId,
759 760
                                 mut move_path: @LoanPath)
                                 -> MoveError {
761
        debug!("analyze_move_out_from(expr_id={:?}, move_path={})",
762
               self.tcx().map.node_to_str(expr_id),
763 764 765 766 767 768 769 770 771 772 773 774
               move_path.repr(self.tcx()));

        // We must check every element of a move path. See
        // `borrowck-move-subcomponent.rs` for a test case.
        loop {
            // check for a conflicting loan:
            let mut ret = MoveOk;
            self.each_in_scope_restriction(expr_id, move_path, |loan, _| {
                // Any restriction prevents moves.
                ret = MoveWhileBorrowed(loan.loan_path, loan.span);
                false
            });
775

776 777 778
            if ret != MoveOk {
                return ret
            }
779

780 781 782 783 784
            match *move_path {
                LpVar(_) => return MoveOk,
                LpExtend(subpath, _, _) => move_path = subpath,
            }
        }
785 786
    }

787
    pub fn check_call(&self,
E
Eduard Burtescu 已提交
788
                      _expr: &ast::Expr,
789
                      _callee: Option<@ast::Expr>,
790
                      _callee_span: Span,
791
                      _args: &[@ast::Expr]) {
N
Niko Matsakis 已提交
792 793 794 795 796
        // 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 已提交
797
        // FIXME(#6268) nested method calls
N
Niko Matsakis 已提交
798
        // self.check_for_conflicting_loans(callee_id);
799
    }
800 801
}

802
fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>,
E
Eduard Burtescu 已提交
803
                            local: &ast::Local) {
804
    visit::walk_local(this, local, ());
805 806
}

807
fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>,
E
Eduard Burtescu 已提交
808
                           expr: &ast::Expr) {
809
    visit::walk_expr(this, expr, ());
810

811
    debug!("check_loans_in_expr(expr={})",
812
           expr.repr(this.tcx()));
N
Niko Matsakis 已提交
813

814
    this.check_for_conflicting_loans(expr.id);
815
    this.check_move_out_from_expr(expr);
816

817
    let method_map = this.bccx.method_map.borrow();
818
    match expr.node {
A
Alex Crichton 已提交
819
      ast::ExprPath(..) => {
820 821
          if !this.move_data.is_assignee(expr.id) {
              let cmt = this.bccx.cat_expr_unadjusted(expr);
822
              debug!("path cmt={}", cmt.repr(this.tcx()));
823
              let r = opt_loan_path(cmt);
D
Daniel Micay 已提交
824
              for &lp in r.iter() {
825
                  this.check_if_path_is_moved(expr.id, expr.span, MovedInUse, lp);
826 827 828
              }
          }
      }
829 830 831
      ast::ExprFnBlock(..) | ast::ExprProc(..) => {
          this.check_captured_variables(expr.id, expr.span)
      }
832
      ast::ExprAssign(dest, _) |
833
      ast::ExprAssignOp(_, dest, _) => {
834
        this.check_assignment(dest);
835
      }
836
      ast::ExprCall(f, ref args) => {
837
        this.check_call(expr, Some(f), f.span, args.as_slice());
838
      }
839
      ast::ExprMethodCall(_, _, ref args) => {
840
        this.check_call(expr, None, expr.span, args.as_slice());
841
      }
842
      ast::ExprIndex(_, rval) | ast::ExprBinary(_, _, rval)
843
      if method_map.get().contains_key(&MethodCall::expr(expr.id)) => {
844
        this.check_call(expr, None, expr.span, [rval]);
845
      }
846
      ast::ExprUnary(_, _) | ast::ExprIndex(_, _)
847
      if method_map.get().contains_key(&MethodCall::expr(expr.id)) => {
848
        this.check_call(expr, None, expr.span, []);
849
      }
850 851 852 853 854
      ast::ExprInlineAsm(ref ia) => {
          for &(_, out) in ia.outputs.iter() {
              this.check_assignment(out);
          }
      }
855
      _ => {}
856 857 858
    }
}

859
fn check_loans_in_pat<'a>(this: &mut CheckLoanCtxt<'a>,
S
Seo Sanghyeon 已提交
860
                          pat: &ast::Pat)
N
Niko Matsakis 已提交
861
{
862
    this.check_for_conflicting_loans(pat.id);
863
    this.check_move_out_from_id(pat.id, pat.span);
864
    visit::walk_pat(this, pat, ());
N
Niko Matsakis 已提交
865
}
866

867
fn check_loans_in_block<'a>(this: &mut CheckLoanCtxt<'a>,
E
Eduard Burtescu 已提交
868
                            blk: &ast::Block)
N
Niko Matsakis 已提交
869
{
870
    visit::walk_block(this, blk, ());
871
    this.check_for_conflicting_loans(blk.id);
872
}