check_loans.rs 31.3 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

21
use middle::borrowck::*;
22 23
use euv = middle::expr_use_visitor;
use mc = middle::mem_categorization;
24 25
use middle::ty;
use syntax::ast;
26
use syntax::codemap::Span;
27
use util::ppaux::Repr;
28

E
Eduard Burtescu 已提交
29 30
use std::rc::Rc;

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

38 39 40 41 42 43 44 45
impl<'a> euv::Delegate for CheckLoanCtxt<'a> {
    fn consume(&mut self,
               consume_id: ast::NodeId,
               consume_span: Span,
               cmt: mc::cmt,
               mode: euv::ConsumeMode) {
        debug!("consume(consume_id={}, cmt={}, mode={})",
               consume_id, cmt.repr(self.tcx()), mode);
46

47
        self.consume_common(consume_id, consume_span, cmt, mode);
48
    }
49 50 51 52 53 54 55 56 57 58 59

    fn consume_pat(&mut self,
                   consume_pat: &ast::Pat,
                   cmt: mc::cmt,
                   mode: euv::ConsumeMode) {
        debug!("consume_pat(consume_pat={}, cmt={}, mode={})",
               consume_pat.repr(self.tcx()),
               cmt.repr(self.tcx()),
               mode);

        self.consume_common(consume_pat.id, consume_pat.span, cmt, mode);
60
    }
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

    fn borrow(&mut self,
              borrow_id: ast::NodeId,
              borrow_span: Span,
              cmt: mc::cmt,
              loan_region: ty::Region,
              bk: ty::BorrowKind,
              loan_cause: euv::LoanCause)
    {
        debug!("borrow(borrow_id={}, cmt={}, loan_region={}, \
               bk={}, loan_cause={:?})",
               borrow_id, cmt.repr(self.tcx()), loan_region,
               bk, loan_cause);

        match opt_loan_path(&cmt) {
            Some(lp) => {
                let moved_value_use_kind = match loan_cause {
                    euv::ClosureCapture(_) => MovedInCapture,
                    _ => MovedInUse,
                };
                self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp);
            }
            None => { }
        }

        self.check_for_conflicting_loans(borrow_id);
87
    }
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

    fn mutate(&mut self,
              assignment_id: ast::NodeId,
              assignment_span: Span,
              assignee_cmt: mc::cmt,
              mode: euv::MutateMode)
    {
        debug!("mutate(assignment_id={}, assignee_cmt={})",
               assignment_id, assignee_cmt.repr(self.tcx()));

        match opt_loan_path(&assignee_cmt) {
            Some(lp) => {
                match mode {
                    euv::Init | euv::JustWrite => {
                        // In a case like `path = 1`, then path does not
                        // have to be *FULLY* initialized, but we still
                        // must be careful lest it contains derefs of
                        // pointers.
                        self.check_if_assigned_path_is_moved(assignee_cmt.id,
                                                             assignment_span,
                                                             MovedInUse,
                                                             &lp);
                    }
                    euv::WriteAndRead => {
                        // In a case like `path += 1`, then path must be
                        // fully initialized, since we will read it before
                        // we write it.
                        self.check_if_path_is_moved(assignee_cmt.id,
                                                    assignment_span,
                                                    MovedInUse,
                                                    &lp);
                    }
                }
            }
            None => { }
        }

        self.check_assignment(assignment_id, assignment_span, assignee_cmt, mode);
126
    }
127

128
    fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
129 130
}

131
pub fn check_loans(bccx: &BorrowckCtxt,
132 133
                   dfcx_loans: &LoanDataFlow,
                   move_data: move_data::FlowedMoveData,
N
Niko Matsakis 已提交
134
                   all_loans: &[Loan],
135
                   decl: &ast::FnDecl,
E
Eduard Burtescu 已提交
136
                   body: &ast::Block) {
137
    debug!("check_loans(body id={:?})", body.id);
N
Niko Matsakis 已提交
138

139
    let mut clcx = CheckLoanCtxt {
140
        bccx: bccx,
141
        dfcx_loans: dfcx_loans,
E
Eduard Burtescu 已提交
142
        move_data: move_data,
N
Niko Matsakis 已提交
143
        all_loans: all_loans,
144
    };
N
Niko Matsakis 已提交
145

146 147 148 149
    {
        let mut euv = euv::ExprUseVisitor::new(&mut clcx, bccx.tcx);
        euv.walk_fn(decl, body);
    }
150 151
}

152
#[deriving(PartialEq)]
153 154 155
enum UseError {
    UseOk,
    UseWhileBorrowed(/*loan*/Rc<LoanPath>, /*loan*/Span)
156 157
}

158 159 160 161 162 163
fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
                           borrow_kind2: ty::BorrowKind)
                           -> bool {
    borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
}

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

167
    pub fn each_issued_loan(&self, scope_id: ast::NodeId, op: |&Loan| -> bool)
168
                            -> bool {
169
        //! Iterates over each loan that has been issued
170 171 172 173
        //! 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.
174

175
        self.dfcx_loans.each_bit_on_entry(scope_id, |loan_index| {
176
            let loan = &self.all_loans[loan_index];
177
            op(loan)
178
        })
179 180
    }

181
    pub fn each_in_scope_loan(&self,
182
                              scope_id: ast::NodeId,
183
                              op: |&Loan| -> bool)
184
                              -> bool {
185 186
        //! Like `each_issued_loan()`, but only considers loans that are
        //! currently in scope.
187

E
Eduard Burtescu 已提交
188
        let tcx = self.tcx();
189
        self.each_issued_loan(scope_id, |loan| {
E
Eduard Burtescu 已提交
190
            if tcx.region_maps.is_subscope_of(scope_id, loan.kill_scope) {
191 192 193
                op(loan)
            } else {
                true
194
            }
195
        })
196 197
    }

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 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
    fn each_in_scope_loan_affecting_path(&self,
                                         scope_id: ast::NodeId,
                                         loan_path: &LoanPath,
                                         op: |&Loan| -> bool)
                                         -> bool {
        //! Iterates through all of the in-scope loans affecting `loan_path`,
        //! calling `op`, and ceasing iteration if `false` is returned.

        // First, we check for a loan restricting the path P being used. This
        // accounts for borrows of P but also borrows of subpaths, like P.a.b.
        // Consider the following example:
        //
        //     let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
        //     let y = a;          // Conflicts with restriction

        let cont = self.each_in_scope_loan(scope_id, |loan| {
            let mut ret = true;
            for restr_path in loan.restricted_paths.iter() {
                if **restr_path == *loan_path {
                    if !op(loan) {
                        ret = false;
                        break;
                    }
                }
            }
            ret
        });

        if !cont {
            return false;
        }

        // Next, we must check for *loans* (not restrictions) on the path P or
        // any base path. This rejects examples like the following:
        //
        //     let x = &mut a.b;
        //     let y = a.b.c;
        //
        // Limiting this search to *loans* and not *restrictions* means that
        // examples like the following continue to work:
        //
        //     let x = &mut a.b;
        //     let y = a.c;

        let mut loan_path = loan_path;
        loop {
            match *loan_path {
245
                LpVar(_) | LpUpvar(_) => {
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
                    break;
                }
                LpExtend(ref lp_base, _, _) => {
                    loan_path = &**lp_base;
                }
            }

            let cont = self.each_in_scope_loan(scope_id, |loan| {
                if *loan.loan_path == *loan_path {
                    op(loan)
                } else {
                    true
                }
            });

            if !cont {
                return false;
            }
        }

        return true;
    }

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

273
        let mut result = Vec::new();
274
        self.dfcx_loans.each_gen_bit(scope_id, |loan_index| {
N
Niko Matsakis 已提交
275
            result.push(loan_index);
276
            true
277
        });
N
Niko Matsakis 已提交
278
        return result;
279 280
    }

281
    pub fn check_for_conflicting_loans(&self, scope_id: ast::NodeId) {
N
Niko Matsakis 已提交
282 283 284 285
        //! 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).
286

287
        debug!("check_for_conflicting_loans(scope_id={:?})", scope_id);
288

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

292
        self.each_issued_loan(scope_id, |issued_loan| {
D
Daniel Micay 已提交
293
            for &new_loan_index in new_loan_indices.iter() {
N
Niko Matsakis 已提交
294 295
                let new_loan = &self.all_loans[new_loan_index];
                self.report_error_if_loans_conflict(issued_loan, new_loan);
296
            }
297
            true
298
        });
299

300 301 302 303
        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 已提交
304
                self.report_error_if_loans_conflict(old_loan, new_loan);
305 306 307 308
            }
        }
    }

309 310 311
    pub fn report_error_if_loans_conflict(&self,
                                          old_loan: &Loan,
                                          new_loan: &Loan) {
N
Niko Matsakis 已提交
312 313 314
        //! Checks whether `old_loan` and `new_loan` can safely be issued
        //! simultaneously.

315
        debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
N
Niko Matsakis 已提交
316 317 318 319
               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 已提交
320 321
        assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
                                                        new_loan.kill_scope));
N
Niko Matsakis 已提交
322 323 324 325 326 327

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

329 330 331 332 333 334
    pub fn report_error_if_loan_conflicts_with_restriction(&self,
                                                           loan1: &Loan,
                                                           loan2: &Loan,
                                                           old_loan: &Loan,
                                                           new_loan: &Loan)
                                                           -> bool {
N
Niko Matsakis 已提交
335 336 337
        //! Checks whether the restrictions introduced by `loan1` would
        //! prohibit `loan2`. Returns false if an error is reported.

338
        debug!("report_error_if_loan_conflicts_with_restriction(\
A
Alex Crichton 已提交
339
                loan1={}, loan2={})",
N
Niko Matsakis 已提交
340 341 342
               loan1.repr(self.tcx()),
               loan2.repr(self.tcx()));

343 344 345
        if compatible_borrow_kinds(loan1.kind, loan2.kind) {
            return true;
        }
N
Niko Matsakis 已提交
346

C
Cameron Zwarich 已提交
347 348
        for restr_path in loan1.restricted_paths.iter() {
            if *restr_path != loan2.loan_path { continue; }
349

350
            let old_pronoun = if new_loan.loan_path == old_loan.loan_path {
351
                "it".to_string()
352 353
            } else {
                format!("`{}`",
354
                        self.bccx.loan_path_to_string(&*old_loan.loan_path))
355 356 357 358
            };

            match (new_loan.kind, old_loan.kind) {
                (ty::MutBorrow, ty::MutBorrow) => {
N
Niko Matsakis 已提交
359 360
                    self.bccx.span_err(
                        new_loan.span,
361 362
                        format!("cannot borrow `{}` as mutable \
                                more than once at a time",
363
                                self.bccx.loan_path_to_string(
364
                                    &*new_loan.loan_path)).as_slice());
365 366 367 368 369 370 371
                }

                (ty::UniqueImmBorrow, _) => {
                    self.bccx.span_err(
                        new_loan.span,
                        format!("closure requires unique access to `{}` \
                                but {} is already borrowed",
372
                                self.bccx.loan_path_to_string(&*new_loan.loan_path),
373
                                old_pronoun).as_slice());
N
Niko Matsakis 已提交
374 375
                }

376
                (_, ty::UniqueImmBorrow) => {
N
Niko Matsakis 已提交
377 378
                    self.bccx.span_err(
                        new_loan.span,
A
Alex Crichton 已提交
379
                        format!("cannot borrow `{}` as {} because \
380
                                previous closure requires unique access",
381
                                self.bccx.loan_path_to_string(&*new_loan.loan_path),
382
                                new_loan.kind.to_user_str()).as_slice());
383 384 385 386 387 388 389
                }

                (_, _) => {
                    self.bccx.span_err(
                        new_loan.span,
                        format!("cannot borrow `{}` as {} because \
                                {} is also borrowed as {}",
390
                                self.bccx.loan_path_to_string(&*new_loan.loan_path),
391 392
                                new_loan.kind.to_user_str(),
                                old_pronoun,
393
                                old_loan.kind.to_user_str()).as_slice());
N
Niko Matsakis 已提交
394
                }
395
            }
396

397
            match new_loan.cause {
398
                euv::ClosureCapture(span) => {
399 400 401
                    self.bccx.span_note(
                        span,
                        format!("borrow occurs due to use of `{}` in closure",
402
                                self.bccx.loan_path_to_string(
403
                                    &*new_loan.loan_path)).as_slice());
404 405 406 407 408 409 410 411 412
                }
                _ => { }
            }

            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",
413
                            self.bccx.loan_path_to_string(
414
                                &*old_loan.loan_path))
415 416 417 418 419 420
                }

                ty::ImmBorrow => {
                    format!("the immutable borrow prevents subsequent \
                            moves or mutable borrows of `{0}` \
                            until the borrow ends",
421
                            self.bccx.loan_path_to_string(&*old_loan.loan_path))
422 423 424 425 426 427
                }

                ty::UniqueImmBorrow => {
                    format!("the unique capture prevents subsequent \
                            moves or borrows of `{0}` \
                            until the borrow ends",
428
                            self.bccx.loan_path_to_string(&*old_loan.loan_path))
429 430 431 432
                }
            };

            let borrow_summary = match old_loan.cause {
433
                euv::ClosureCapture(_) => {
434 435
                    format!("previous borrow of `{}` occurs here due to \
                            use in closure",
436
                            self.bccx.loan_path_to_string(&*old_loan.loan_path))
437 438
                }

439 440 441 442 443
                euv::OverloadedOperator(..) |
                euv::AddrOf(..) |
                euv::AutoRef(..) |
                euv::ClosureInvocation(..) |
                euv::RefBinding(..) => {
444
                    format!("previous borrow of `{}` occurs here",
445
                            self.bccx.loan_path_to_string(&*old_loan.loan_path))
446 447 448 449 450
                }
            };

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

453
            let old_loan_span = self.tcx().map.span(old_loan.kill_scope);
454 455
            self.bccx.span_end_note(old_loan_span,
                                    "previous borrow ends here");
456

457
            return false;
458
        }
N
Niko Matsakis 已提交
459 460

        true
461 462
    }

463
    pub fn is_local_variable_or_arg(&self, cmt: mc::cmt) -> bool {
464
        match cmt.cat {
465
          mc::cat_local(_) | mc::cat_arg(_) => true,
B
Brian Anderson 已提交
466
          _ => false
467 468 469
        }
    }

470 471 472 473 474 475 476 477 478
    fn consume_common(&self,
                      id: ast::NodeId,
                      span: Span,
                      cmt: mc::cmt,
                      mode: euv::ConsumeMode) {
        match opt_loan_path(&cmt) {
            Some(lp) => {
                let moved_value_use_kind = match mode {
                    euv::Copy => {
479
                        self.check_for_copy_of_frozen_path(id, span, &*lp);
480 481 482 483 484 485 486 487 488 489 490 491 492 493
                        MovedInUse
                    }
                    euv::Move(_) => {
                        match self.move_data.kind_of_move_of_path(id, &lp) {
                            None => {
                                // Sometimes moves don't have a move kind;
                                // this either means that the original move
                                // was from something illegal to move,
                                // or was moved from referent of an unsafe
                                // pointer or something like that.
                                MovedInUse
                            }
                            Some(move_kind) => {
                                self.check_for_move_of_borrowed_path(id, span,
494
                                                                     &*lp, move_kind);
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
                                if move_kind == move_data::Captured {
                                    MovedInCapture
                                } else {
                                    MovedInUse
                                }
                            }
                        }
                    }
                };

                self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
            }
            None => { }
        }
    }

511 512 513 514 515 516 517 518 519 520
    fn check_for_copy_of_frozen_path(&self,
                                     id: ast::NodeId,
                                     span: Span,
                                     copy_path: &LoanPath) {
        match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
            UseOk => { }
            UseWhileBorrowed(loan_path, loan_span) => {
                self.bccx.span_err(
                    span,
                    format!("cannot use `{}` because it was mutably borrowed",
521
                            self.bccx.loan_path_to_string(copy_path).as_slice())
522 523 524 525
                    .as_slice());
                self.bccx.span_note(
                    loan_span,
                    format!("borrow of `{}` occurs here",
526
                            self.bccx.loan_path_to_string(&*loan_path).as_slice())
527 528 529 530 531
                    .as_slice());
            }
        }
    }

532 533 534
    fn check_for_move_of_borrowed_path(&self,
                                       id: ast::NodeId,
                                       span: Span,
535
                                       move_path: &LoanPath,
536
                                       move_kind: move_data::MoveKind) {
537 538 539
        // We want to detect if there are any loans at all, so we search for
        // any loans incompatible with MutBorrrow, since all other kinds of
        // loans are incompatible with that.
540 541 542
        match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
            UseOk => { }
            UseWhileBorrowed(loan_path, loan_span) => {
543 544 545
                let err_message = match move_kind {
                    move_data::Captured =>
                        format!("cannot move `{}` into closure because it is borrowed",
546
                                self.bccx.loan_path_to_string(move_path).as_slice()),
547 548 549 550
                    move_data::Declared |
                    move_data::MoveExpr |
                    move_data::MovePat =>
                        format!("cannot move out of `{}` because it is borrowed",
551
                                self.bccx.loan_path_to_string(move_path).as_slice())
552 553 554 555 556 557
                };

                self.bccx.span_err(span, err_message.as_slice());
                self.bccx.span_note(
                    loan_span,
                    format!("borrow of `{}` occurs here",
558
                            self.bccx.loan_path_to_string(&*loan_path).as_slice())
559 560 561 562 563
                    .as_slice());
            }
        }
    }

C
Cameron Zwarich 已提交
564 565 566 567 568 569
    pub fn analyze_restrictions_on_use(&self,
                                       expr_id: ast::NodeId,
                                       use_path: &LoanPath,
                                       borrow_kind: ty::BorrowKind)
                                       -> UseError {
        debug!("analyze_restrictions_on_use(expr_id={:?}, use_path={})",
570
               self.tcx().map.node_to_string(expr_id),
C
Cameron Zwarich 已提交
571 572 573 574
               use_path.repr(self.tcx()));

        let mut ret = UseOk;

575
        self.each_in_scope_loan_affecting_path(expr_id, use_path, |loan| {
576
            if !compatible_borrow_kinds(loan.kind, borrow_kind) {
C
Cameron Zwarich 已提交
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
                ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
                false
            } else {
                true
            }
        });

        return ret;
    }

    fn check_if_path_is_moved(&self,
                              id: ast::NodeId,
                              span: Span,
                              use_kind: MovedValueUseKind,
                              lp: &Rc<LoanPath>) {
        /*!
         * 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={})",
               id, use_kind, lp.repr(self.bccx.tcx));
        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);
            false
        });
    }

610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
    fn check_if_assigned_path_is_moved(&self,
                                       id: ast::NodeId,
                                       span: Span,
                                       use_kind: MovedValueUseKind,
                                       lp: &Rc<LoanPath>)
    {
        /*!
         * Reports an error if assigning to `lp` will use a
         * moved/uninitialized value. Mainly this is concerned with
         * detecting derefs of uninitialized pointers.
         *
         * For example:
         *
         *     let a: int;
         *     a = 10; // ok, even though a is uninitialized
         *
         *     struct Point { x: uint, y: uint }
         *     let p: Point;
         *     p.x = 22; // ok, even though `p` is uninitialized
         *
         *     let p: ~Point;
         *     (*p).x = 22; // not ok, p is uninitialized, can't deref
         */

        match **lp {
635
            LpVar(_) | LpUpvar(_) => {
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
                // assigning to `x` does not require that `x` is initialized
            }
            LpExtend(ref lp_base, _, LpInterior(_)) => {
                // assigning to `P.f` is ok if assigning to `P` is ok
                self.check_if_assigned_path_is_moved(id, span,
                                                     use_kind, lp_base);
            }
            LpExtend(ref lp_base, _, LpDeref(_)) => {
                // assigning to `(*P)` requires that `P` be initialized
                self.check_if_path_is_moved(id, span,
                                            use_kind, lp_base);
            }
        }
    }

C
Cameron Zwarich 已提交
651 652 653
    fn check_assignment(&self,
                        assignment_id: ast::NodeId,
                        assignment_span: Span,
654 655
                        assignee_cmt: mc::cmt,
                        mode: euv::MutateMode) {
C
Cameron Zwarich 已提交
656
        debug!("check_assignment(assignee_cmt={})", assignee_cmt.repr(self.tcx()));
N
Niko Matsakis 已提交
657

658 659
        // Mutable values can be assigned, as long as they obey loans
        // and aliasing restrictions:
C
Cameron Zwarich 已提交
660 661
        if assignee_cmt.mutbl.is_mutable() {
            if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) {
662
                if mode != euv::Init {
663
                    check_for_assignment_to_borrowed_path(
664
                        self, assignment_id, assignment_span, assignee_cmt.clone());
C
Cameron Zwarich 已提交
665
                    mark_variable_as_used_mut(self, assignee_cmt);
666
                }
N
Niko Matsakis 已提交
667
            }
668 669 670 671 672
            return;
        }

        // For immutable local variables, assignments are legal
        // if they cannot already have been assigned
673
        if self.is_local_variable_or_arg(assignee_cmt.clone()) {
C
Cameron Zwarich 已提交
674 675 676
            assert!(assignee_cmt.mutbl.is_immutable()); // no "const" locals
            let lp = opt_loan_path(&assignee_cmt).unwrap();
            self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
677
                self.bccx.report_reassigned_immutable_variable(
C
Cameron Zwarich 已提交
678
                    assignment_span,
E
Eduard Burtescu 已提交
679
                    &*lp,
680
                    assign);
681
                false
682
            });
683
            return;
684 685
        }

686
        // Otherwise, just a plain error.
C
Cameron Zwarich 已提交
687
        match opt_loan_path(&assignee_cmt) {
688 689
            Some(lp) => {
                self.bccx.span_err(
C
Cameron Zwarich 已提交
690
                    assignment_span,
691
                    format!("cannot assign to {} {} `{}`",
C
Cameron Zwarich 已提交
692
                            assignee_cmt.mutbl.to_user_str(),
693 694
                            self.bccx.cmt_to_string(&*assignee_cmt),
                            self.bccx.loan_path_to_string(&*lp)).as_slice());
695 696 697
            }
            None => {
                self.bccx.span_err(
C
Cameron Zwarich 已提交
698
                    assignment_span,
699
                    format!("cannot assign to {} {}",
C
Cameron Zwarich 已提交
700
                            assignee_cmt.mutbl.to_user_str(),
701
                            self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
702 703
            }
        }
704
        return;
705

706
        fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
707
                                     cmt: mc::cmt) {
N
Niko Matsakis 已提交
708
            //! If the mutability of the `cmt` being written is inherited
709
            //! from a local variable, liveness will
N
Niko Matsakis 已提交
710 711
            //! not have been able to detect that this variable's mutability
            //! is important, so we must add the variable to the
712
            //! `used_mut_nodes` table here.
N
Niko Matsakis 已提交
713 714 715

            let mut cmt = cmt;
            loop {
716
                debug!("mark_writes_through_upvars_as_used_mut(cmt={})",
717
                       cmt.repr(this.tcx()));
718
                match cmt.cat.clone() {
719
                    mc::cat_local(id) | mc::cat_arg(id) => {
720
                        this.tcx().used_mut_nodes.borrow_mut().insert(id);
N
Niko Matsakis 已提交
721 722
                        return;
                    }
723

724 725
                    mc::cat_upvar(..) => {
                        return;
N
Niko Matsakis 已提交
726
                    }
727

728
                    mc::cat_deref(_, _, mc::GcPtr) => {
729 730 731 732
                        assert_eq!(cmt.mutbl, mc::McImmutable);
                        return;
                    }

A
Alex Crichton 已提交
733
                    mc::cat_rvalue(..) |
N
Niko Matsakis 已提交
734
                    mc::cat_static_item |
A
Alex Crichton 已提交
735
                    mc::cat_copied_upvar(..) |
736
                    mc::cat_deref(_, _, mc::UnsafePtr(..)) |
737 738
                    mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
                    mc::cat_deref(_, _, mc::Implicit(..)) => {
N
Niko Matsakis 已提交
739 740 741
                        assert_eq!(cmt.mutbl, mc::McDeclared);
                        return;
                    }
742

N
Niko Matsakis 已提交
743
                    mc::cat_discr(b, _) |
744
                    mc::cat_deref(b, _, mc::OwnedPtr) => {
N
Niko Matsakis 已提交
745 746 747 748
                        assert_eq!(cmt.mutbl, mc::McInherited);
                        cmt = b;
                    }

749
                    mc::cat_downcast(b) |
N
Niko Matsakis 已提交
750
                    mc::cat_interior(b, _) => {
751 752
                        assert_eq!(cmt.mutbl, mc::McInherited);
                        cmt = b;
753 754 755 756
                    }
                }
            }
        }
757

758
        fn check_for_aliasable_mutable_writes(this: &CheckLoanCtxt,
C
Cameron Zwarich 已提交
759
                                              span: Span,
N
Niko Matsakis 已提交
760 761 762 763
                                              cmt: mc::cmt) -> bool {
            //! Safety checks related to writes to aliasable, mutable locations

            let guarantor = cmt.guarantor();
764
            debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
765
                   cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
N
Niko Matsakis 已提交
766
            match guarantor.cat {
767
                mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
N
Niko Matsakis 已提交
768 769
                    // Statically prohibit writes to `&mut` when aliasable

C
Cameron Zwarich 已提交
770
                    check_for_aliasability_violation(this, span, b.clone());
771
                }
N
Niko Matsakis 已提交
772 773

                _ => {}
774 775
            }

N
Niko Matsakis 已提交
776
            return true; // no errors reported
777 778
        }

779
        fn check_for_aliasability_violation(this: &CheckLoanCtxt,
C
Cameron Zwarich 已提交
780
                                            span: Span,
781 782
                                            cmt: mc::cmt)
                                            -> bool {
783
            match cmt.freely_aliasable(this.tcx()) {
784 785 786
                None => {
                    return true;
                }
787
                Some(mc::AliasableStaticMut(..)) => {
X
xales 已提交
788 789
                    return true;
                }
790 791
                Some(cause) => {
                    this.bccx.report_aliasability_violation(
C
Cameron Zwarich 已提交
792
                        span,
793 794 795
                        MutabilityViolation,
                        cause);
                    return false;
796 797 798 799
                }
            }
        }

800
        fn check_for_assignment_to_borrowed_path(
801
            this: &CheckLoanCtxt,
C
Cameron Zwarich 已提交
802 803
            assignment_id: ast::NodeId,
            assignment_span: Span,
C
Cameron Zwarich 已提交
804
            assignee_cmt: mc::cmt)
N
Niko Matsakis 已提交
805 806 807 808
        {
            //! Check for assignments that violate the terms of an
            //! outstanding loan.

C
Cameron Zwarich 已提交
809
            let loan_path = match opt_loan_path(&assignee_cmt) {
N
Niko Matsakis 已提交
810
                Some(lp) => lp,
C
Cameron Zwarich 已提交
811
                None => { return; /* no loan path, can't be any loans */ }
N
Niko Matsakis 已提交
812 813
            };

814
            this.each_in_scope_loan_affecting_path(assignment_id, &*loan_path, |loan| {
815 816
                this.report_illegal_mutation(assignment_span, &*loan_path, loan);
                false
817
            });
818 819 820
        }
    }

821
    pub fn report_illegal_mutation(&self,
C
Cameron Zwarich 已提交
822
                                   span: Span,
823 824
                                   loan_path: &LoanPath,
                                   loan: &Loan) {
N
Niko Matsakis 已提交
825
        self.bccx.span_err(
C
Cameron Zwarich 已提交
826
            span,
A
Alex Crichton 已提交
827
            format!("cannot assign to `{}` because it is borrowed",
828
                    self.bccx.loan_path_to_string(loan_path)).as_slice());
N
Niko Matsakis 已提交
829 830
        self.bccx.span_note(
            loan.span,
A
Alex Crichton 已提交
831
            format!("borrow of `{}` occurs here",
832
                    self.bccx.loan_path_to_string(loan_path)).as_slice());
N
Niko Matsakis 已提交
833
    }
834
}