(index<- )        ./librustc/middle/borrowck/check_loans.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
   2  // file at the top-level directory of this distribution and at
   3  // http://rust-lang.org/COPYRIGHT.
   4  //
   5  // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
   6  // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
   7  // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
   8  // option. This file may not be copied, modified, or distributed
   9  // except according to those terms.
  10  
  11  // ----------------------------------------------------------------------
  12  // Checking loans
  13  //
  14  // Phase 2 of check: we walk down the tree and check that:
  15  // 1. assignments are always made to mutable locations;
  16  // 2. loans made in overlapping scopes do not conflict
  17  // 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  use euv = middle::expr_use_visitor;
  23  use middle::freevars;
  24  use mc = middle::mem_categorization;
  25  use middle::ty;
  26  use middle::typeck::MethodCall;
  27  use syntax::ast;
  28  use syntax::ast_util;
  29  use syntax::codemap::Span;
  30  use syntax::visit::Visitor;
  31  use syntax::visit;
  32  use util::ppaux::Repr;
  33  
  34  use std::rc::Rc;
  35  
  36  struct CheckLoanCtxt<'a> {
  37      bccx: &'a BorrowckCtxt<'a>,
  38      dfcx_loans: &'a LoanDataFlow<'a>,
  39      move_data: move_data::FlowedMoveData<'a>,
  40      all_loans: &'a [Loan],
  41  }
  42  
  43  impl<'a> Visitor<()> for CheckLoanCtxt<'a> {
  44  
  45      fn visit_expr(&mut self, ex&ast::Expr, _()) {
  46          check_loans_in_expr(self, ex);
  47      }
  48      fn visit_local(&mut self, l&ast::Local, _()) {
  49          check_loans_in_local(self, l);
  50      }
  51      fn visit_block(&mut self, b&ast::Block, _()) {
  52          check_loans_in_block(self, b);
  53      }
  54      fn visit_pat(&mut self, p&ast::Pat, _()) {
  55          check_loans_in_pat(self, p);
  56      }
  57      fn visit_fn(&mut self, _fk&visit::FnKind, _fd&ast::FnDecl,
  58                  _b&ast::Block, _sSpan, _nast::NodeId, _()) {
  59          // Don't process nested items or closures here,
  60          // the outer loop will take care of it.
  61          return;
  62      }
  63  
  64      // FIXME(#10894) should continue recursing
  65      fn visit_ty(&mut self, _t&ast::Ty, _()) {}
  66  }
  67  
  68  pub fn check_loans(bccx: &BorrowckCtxt,
  69                     dfcx_loans: &LoanDataFlow,
  70                     move_datamove_data::FlowedMoveData,
  71                     all_loans: &[Loan],
  72                     body: &ast::Block) {
  73      debug!("check_loans(body id={:?})", body.id);
  74  
  75      let mut clcx = CheckLoanCtxt {
  76          bccx: bccx,
  77          dfcx_loans: dfcx_loans,
  78          move_data: move_data,
  79          all_loans: all_loans,
  80      };
  81  
  82      clcx.visit_block(body, ());
  83  }
  84  
  85  #[deriving(Eq)]
  86  enum MoveError {
  87      MoveOk,
  88      MoveWhileBorrowed(/*loan*/Rc<LoanPath>, /*loan*/Span)
  89  }
  90  
  91  impl<'a> CheckLoanCtxt<'a> {
  92      pub fn tcx(&self) -> &'a ty::ctxt { self.bccx.tcx }
  93  
  94      pub fn each_issued_loan(&self, scope_idast::NodeId, op|&Loan-> bool)
  95                              -> bool {
  96          //! Iterates over each loan that has been issued
  97          //! on entrance to `scope_id`, regardless of whether it is
  98          //! actually *in scope* at that point.  Sometimes loans
  99          //! are issued for future scopes and thus they may have been
 100          //! *issued* but not yet be in effect.
 101  
 102          self.dfcx_loans.each_bit_on_entry_frozen(scope_id, |loan_index| {
 103              let loan = &self.all_loans[loan_index];
 104              op(loan)
 105          })
 106      }
 107  
 108      pub fn each_in_scope_loan(&self,
 109                                scope_idast::NodeId,
 110                                op|&Loan-> bool)
 111                                -> bool {
 112          //! Like `each_issued_loan()`, but only considers loans that are
 113          //! currently in scope.
 114  
 115          let tcx = self.tcx();
 116          self.each_issued_loan(scope_id, |loan| {
 117              if tcx.region_maps.is_subscope_of(scope_id, loan.kill_scope) {
 118                  op(loan)
 119              } else {
 120                  true
 121              }
 122          })
 123      }
 124  
 125      pub fn each_in_scope_restriction(&self,
 126                                       scope_idast::NodeId,
 127                                       loan_path&LoanPath,
 128                                       op|&Loan, &Restriction-> bool)
 129                                       -> bool {
 130          //! Iterates through all the in-scope restrictions for the
 131          //! given `loan_path`
 132  
 133          self.each_in_scope_loan(scope_id, |loan| {
 134              debug!("each_in_scope_restriction found loan: {:?}",
 135                     loan.repr(self.tcx()));
 136  
 137              let mut ret = true;
 138              for restr in loan.restrictions.iter() {
 139                  if *restr.loan_path == *loan_path {
 140                      if !op(loan, restr) {
 141                          ret = false;
 142                          break;
 143                      }
 144                  }
 145              }
 146              ret
 147          })
 148      }
 149  
 150      pub fn loans_generated_by(&self, scope_idast::NodeId) -> Vec<uint> {
 151          //! Returns a vector of the loans that are generated as
 152          //! we encounter `scope_id`.
 153  
 154          let mut result = Vec::new();
 155          self.dfcx_loans.each_gen_bit_frozen(scope_id, |loan_index| {
 156              result.push(loan_index);
 157              true
 158          });
 159          return result;
 160      }
 161  
 162      pub fn check_for_conflicting_loans(&self, scope_idast::NodeId) {
 163          //! Checks to see whether any of the loans that are issued
 164          //! by `scope_id` conflict with loans that have already been
 165          //! issued when we enter `scope_id` (for example, we do not
 166          //! permit two `&mut` borrows of the same variable).
 167  
 168          debug!("check_for_conflicting_loans(scope_id={:?})", scope_id);
 169  
 170          let new_loan_indices = self.loans_generated_by(scope_id);
 171          debug!("new_loan_indices = {:?}", new_loan_indices);
 172  
 173          self.each_issued_loan(scope_id, |issued_loan| {
 174              for &new_loan_index in new_loan_indices.iter() {
 175                  let new_loan = &self.all_loans[new_loan_index];
 176                  self.report_error_if_loans_conflict(issued_loan, new_loan);
 177              }
 178              true
 179          });
 180  
 181          for (i, &x) in new_loan_indices.iter().enumerate() {
 182              let old_loan = &self.all_loans[x];
 183              for &y in new_loan_indices.slice_from(i+1).iter() {
 184                  let new_loan = &self.all_loans[y];
 185                  self.report_error_if_loans_conflict(old_loan, new_loan);
 186              }
 187          }
 188      }
 189  
 190      pub fn report_error_if_loans_conflict(&self,
 191                                            old_loan&Loan,
 192                                            new_loan&Loan) {
 193          //! Checks whether `old_loan` and `new_loan` can safely be issued
 194          //! simultaneously.
 195  
 196          debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
 197                 old_loan.repr(self.tcx()),
 198                 new_loan.repr(self.tcx()));
 199  
 200          // Should only be called for loans that are in scope at the same time.
 201          assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
 202                                                          new_loan.kill_scope));
 203  
 204          self.report_error_if_loan_conflicts_with_restriction(
 205              old_loan, new_loan, old_loan, new_loan) &&
 206          self.report_error_if_loan_conflicts_with_restriction(
 207              new_loan, old_loan, old_loan, new_loan);
 208      }
 209  
 210      pub fn report_error_if_loan_conflicts_with_restriction(&self,
 211                                                             loan1&Loan,
 212                                                             loan2&Loan,
 213                                                             old_loan&Loan,
 214                                                             new_loan&Loan)
 215                                                             -> bool {
 216          //! Checks whether the restrictions introduced by `loan1` would
 217          //! prohibit `loan2`. Returns false if an error is reported.
 218  
 219          debug!("report_error_if_loan_conflicts_with_restriction(\
 220                  loan1={}, loan2={})",
 221                 loan1.repr(self.tcx()),
 222                 loan2.repr(self.tcx()));
 223  
 224          // Restrictions that would cause the new loan to be illegal:
 225          let illegal_if = match loan2.kind {
 226              // Look for restrictions against mutation. These are
 227              // generated by all other borrows.
 228              ty::MutBorrow => RESTR_MUTATE,
 229  
 230              // Look for restrictions against freezing (immutable borrows).
 231              // These are generated by `&mut` borrows.
 232              ty::ImmBorrow => RESTR_FREEZE,
 233  
 234              // No matter how the data is borrowed (as `&`, as `&mut`,
 235              // or as `&unique imm`) it will always generate a
 236              // restriction against mutating the data. So look for those.
 237              ty::UniqueImmBorrow => RESTR_MUTATE,
 238          };
 239          debug!("illegal_if={:?}", illegal_if);
 240  
 241          for restr in loan1.restrictions.iter() {
 242              if !restr.set.intersects(illegal_if) { continue; }
 243              if restr.loan_path != loan2.loan_path { continue; }
 244  
 245              let old_pronoun = if new_loan.loan_path == old_loan.loan_path {
 246                  "it".to_owned()
 247              } else {
 248                  format!("`{}`",
 249                          self.bccx.loan_path_to_str(&*old_loan.loan_path))
 250              };
 251  
 252              match (new_loan.kind, old_loan.kind) {
 253                  (ty::MutBorrow, ty::MutBorrow) => {
 254                      self.bccx.span_err(
 255                          new_loan.span,
 256                          format!("cannot borrow `{}` as mutable \
 257                                  more than once at a time",
 258                                  self.bccx.loan_path_to_str(&*new_loan.loan_path)));
 259                  }
 260  
 261                  (ty::UniqueImmBorrow, _) => {
 262                      self.bccx.span_err(
 263                          new_loan.span,
 264                          format!("closure requires unique access to `{}` \
 265                                  but {} is already borrowed",
 266                                  self.bccx.loan_path_to_str(&*new_loan.loan_path),
 267                                  old_pronoun));
 268                  }
 269  
 270                  (_, ty::UniqueImmBorrow) => {
 271                      self.bccx.span_err(
 272                          new_loan.span,
 273                          format!("cannot borrow `{}` as {} because \
 274                                  previous closure requires unique access",
 275                                  self.bccx.loan_path_to_str(&*new_loan.loan_path),
 276                                  new_loan.kind.to_user_str()));
 277                  }
 278  
 279                  (_, _) => {
 280                      self.bccx.span_err(
 281                          new_loan.span,
 282                          format!("cannot borrow `{}` as {} because \
 283                                  {} is also borrowed as {}",
 284                                  self.bccx.loan_path_to_str(&*new_loan.loan_path),
 285                                  new_loan.kind.to_user_str(),
 286                                  old_pronoun,
 287                                  old_loan.kind.to_user_str()));
 288                  }
 289              }
 290  
 291              match new_loan.cause {
 292                  euv::ClosureCapture(span) => {
 293                      self.bccx.span_note(
 294                          span,
 295                          format!("borrow occurs due to use of `{}` in closure",
 296                                  self.bccx.loan_path_to_str(&*new_loan.loan_path)));
 297                  }
 298                  _ => { }
 299              }
 300  
 301              let rule_summary = match old_loan.kind {
 302                  ty::MutBorrow => {
 303                      format!("the mutable borrow prevents subsequent \
 304                              moves, borrows, or modification of `{0}` \
 305                              until the borrow ends",
 306                              self.bccx.loan_path_to_str(&*old_loan.loan_path))
 307                  }
 308  
 309                  ty::ImmBorrow => {
 310                      format!("the immutable borrow prevents subsequent \
 311                              moves or mutable borrows of `{0}` \
 312                              until the borrow ends",
 313                              self.bccx.loan_path_to_str(&*old_loan.loan_path))
 314                  }
 315  
 316                  ty::UniqueImmBorrow => {
 317                      format!("the unique capture prevents subsequent \
 318                              moves or borrows of `{0}` \
 319                              until the borrow ends",
 320                              self.bccx.loan_path_to_str(&*old_loan.loan_path))
 321                  }
 322              };
 323  
 324              let borrow_summary = match old_loan.cause {
 325                  euv::ClosureCapture(_) => {
 326                      format!("previous borrow of `{}` occurs here due to \
 327                              use in closure",
 328                              self.bccx.loan_path_to_str(&*old_loan.loan_path))
 329                  }
 330  
 331                  euv::OverloadedOperator(..) |
 332                  euv::AddrOf(..) |
 333                  euv::AutoRef(..) |
 334                  euv::ClosureInvocation(..) |
 335                  euv::RefBinding(..) => {
 336                      format!("previous borrow of `{}` occurs here",
 337                              self.bccx.loan_path_to_str(&*old_loan.loan_path))
 338                  }
 339              };
 340  
 341              self.bccx.span_note(
 342                  old_loan.span,
 343                  format!("{}; {}", borrow_summary, rule_summary));
 344  
 345              let old_loan_span = self.tcx().map.span(old_loan.kill_scope);
 346              self.bccx.span_end_note(old_loan_span,
 347                                      "previous borrow ends here");
 348  
 349              return false;
 350          }
 351  
 352          true
 353      }
 354  
 355      pub fn is_local_variable(&self, cmtmc::cmt) -> bool {
 356          match cmt.cat {
 357            mc::cat_local(_) => true,
 358            _ => false
 359          }
 360      }
 361  
 362      pub fn check_if_path_is_moved(&self,
 363                                    idast::NodeId,
 364                                    spanSpan,
 365                                    use_kindMovedValueUseKind,
 366                                    lp&Rc<LoanPath>) {
 367          /*!
 368           * Reports an error if `expr` (which should be a path)
 369           * is using a moved/uninitialized value
 370           */
 371  
 372          debug!("check_if_path_is_moved(id={:?}, use_kind={:?}, lp={})",
 373                 id, use_kind, lp.repr(self.bccx.tcx));
 374          self.move_data.each_move_of(id, lp, |move, moved_lp| {
 375              self.bccx.report_use_of_moved_value(
 376                  span,
 377                  use_kind,
 378                  &**lp,
 379                  move,
 380                  moved_lp);
 381              false
 382          });
 383      }
 384  
 385      pub fn check_assignment(&self, expr&ast::Expr) {
 386          // We don't use cat_expr() here because we don't want to treat
 387          // auto-ref'd parameters in overloaded operators as rvalues.
 388          let cmt = match self.bccx.tcx.adjustments.borrow().find(&expr.id) {
 389              None => self.bccx.cat_expr_unadjusted(expr),
 390              Some(adj) => self.bccx.cat_expr_autoderefd(expr, adj)
 391          };
 392  
 393          debug!("check_assignment(cmt={})", cmt.repr(self.tcx()));
 394  
 395          // Mutable values can be assigned, as long as they obey loans
 396          // and aliasing restrictions:
 397          if cmt.mutbl.is_mutable() {
 398              if check_for_aliasable_mutable_writes(self, expr, cmt.clone()) {
 399                  if check_for_assignment_to_restricted_or_frozen_location(
 400                      self, expr, cmt.clone()) {
 401                      // Safe, but record for lint pass later:
 402                      mark_variable_as_used_mut(self, cmt);
 403                  }
 404              }
 405              return;
 406          }
 407  
 408          // For immutable local variables, assignments are legal
 409          // if they cannot already have been assigned
 410          if self.is_local_variable(cmt.clone()) {
 411              assert!(cmt.mutbl.is_immutable()); // no "const" locals
 412              let lp = opt_loan_path(&cmt).unwrap();
 413              self.move_data.each_assignment_of(expr.id, &lp, |assign| {
 414                  self.bccx.report_reassigned_immutable_variable(
 415                      expr.span,
 416                      &*lp,
 417                      assign);
 418                  false
 419              });
 420              return;
 421          }
 422  
 423          // Otherwise, just a plain error.
 424          match opt_loan_path(&cmt) {
 425              Some(lp) => {
 426                  self.bccx.span_err(
 427                      expr.span,
 428                      format!("cannot assign to {} {} `{}`",
 429                              cmt.mutbl.to_user_str(),
 430                              self.bccx.cmt_to_str(&*cmt),
 431                              self.bccx.loan_path_to_str(&*lp)));
 432              }
 433              None => {
 434                  self.bccx.span_err(
 435                      expr.span,
 436                      format!("cannot assign to {} {}",
 437                              cmt.mutbl.to_user_str(),
 438                              self.bccx.cmt_to_str(&*cmt)));
 439              }
 440          }
 441          return;
 442  
 443          fn mark_variable_as_used_mut(this&CheckLoanCtxt,
 444                                       cmtmc::cmt) {
 445              //! If the mutability of the `cmt` being written is inherited
 446              //! from a local variable, liveness will
 447              //! not have been able to detect that this variable's mutability
 448              //! is important, so we must add the variable to the
 449              //! `used_mut_nodes` table here.
 450  
 451              let mut cmt = cmt;
 452              loop {
 453                  debug!("mark_writes_through_upvars_as_used_mut(cmt={})",
 454                         cmt.repr(this.tcx()));
 455                  match cmt.cat.clone() {
 456                      mc::cat_local(id) | mc::cat_arg(id) => {
 457                          this.tcx().used_mut_nodes.borrow_mut().insert(id);
 458                          return;
 459                      }
 460  
 461                      mc::cat_upvar(..) => {
 462                          return;
 463                      }
 464  
 465                      mc::cat_deref(_, _, mc::GcPtr) => {
 466                          assert_eq!(cmt.mutbl, mc::McImmutable);
 467                          return;
 468                      }
 469  
 470                      mc::cat_rvalue(..) |
 471                      mc::cat_static_item |
 472                      mc::cat_copied_upvar(..) |
 473                      mc::cat_deref(_, _, mc::UnsafePtr(..)) |
 474                      mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
 475                          assert_eq!(cmt.mutbl, mc::McDeclared);
 476                          return;
 477                      }
 478  
 479                      mc::cat_discr(b, _) |
 480                      mc::cat_deref(b, _, mc::OwnedPtr) => {
 481                          assert_eq!(cmt.mutbl, mc::McInherited);
 482                          cmt = b;
 483                      }
 484  
 485                      mc::cat_downcast(b) |
 486                      mc::cat_interior(b, _) => {
 487                          assert_eq!(cmt.mutbl, mc::McInherited);
 488                          cmt = b;
 489                      }
 490                  }
 491              }
 492          }
 493  
 494          fn check_for_aliasable_mutable_writes(this&CheckLoanCtxt,
 495                                                expr&ast::Expr,
 496                                                cmtmc::cmt) -> bool {
 497              //! Safety checks related to writes to aliasable, mutable locations
 498  
 499              let guarantor = cmt.guarantor();
 500              debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
 501                     cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
 502              match guarantor.cat {
 503                  mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
 504                      // Statically prohibit writes to `&mut` when aliasable
 505  
 506                      check_for_aliasability_violation(this, expr, b.clone());
 507                  }
 508  
 509                  _ => {}
 510              }
 511  
 512              return true; // no errors reported
 513          }
 514  
 515          fn check_for_aliasability_violation(this: &CheckLoanCtxt,
 516                                              expr: &ast::Expr,
 517                                              cmtmc::cmt)
 518                                              -> bool {
 519              match cmt.freely_aliasable(this.tcx()) {
 520                  None => {
 521                      return true;
 522                  }
 523                  Some(mc::AliasableStaticMut(..)) => {
 524                      return true;
 525                  }
 526                  Some(cause) => {
 527                      this.bccx.report_aliasability_violation(
 528                          expr.span,
 529                          MutabilityViolation,
 530                          cause);
 531                      return false;
 532                  }
 533              }
 534          }
 535  
 536          fn check_for_assignment_to_restricted_or_frozen_location(
 537              this&CheckLoanCtxt,
 538              expr&ast::Expr,
 539              cmtmc::cmt) -> bool
 540          {
 541              //! Check for assignments that violate the terms of an
 542              //! outstanding loan.
 543  
 544              let loan_path = match opt_loan_path(&cmt) {
 545                  Some(lp) => lp,
 546                  None => { return true; /* no loan path, can't be any loans */ }
 547              };
 548  
 549              // Start by searching for an assignment to a *restricted*
 550              // location. Here is one example of the kind of error caught
 551              // by this check:
 552              //
 553              //    let mut v = ~[1, 2, 3];
 554              //    let p = &v;
 555              //    v = ~[4];
 556              //
 557              // In this case, creating `p` triggers a RESTR_MUTATE
 558              // restriction on the path `v`.
 559              //
 560              // Here is a second, more subtle example:
 561              //
 562              //    let mut v = ~[1, 2, 3];
 563              //    let p = &const v[0];
 564              //    v[0] = 4;                   // OK
 565              //    v[1] = 5;                   // OK
 566              //    v = ~[4, 5, 3];             // Error
 567              //
 568              // In this case, `p` is pointing to `v[0]`, and it is a
 569              // `const` pointer in any case. So the first two
 570              // assignments are legal (and would be permitted by this
 571              // check). However, the final assignment (which is
 572              // logically equivalent) is forbidden, because it would
 573              // cause the existing `v` array to be freed, thus
 574              // invalidating `p`. In the code, this error results
 575              // because `gather_loans::restrictions` adds a
 576              // `RESTR_MUTATE` restriction whenever the contents of an
 577              // owned pointer are borrowed, and hence while `v[*]` is not
 578              // restricted from being written, `v` is.
 579              let cont = this.each_in_scope_restriction(expr.id,
 580                                                        &*loan_path,
 581                                                        |loan, restr| {
 582                  if restr.set.intersects(RESTR_MUTATE) {
 583                      this.report_illegal_mutation(expr, &*loan_path, loan);
 584                      false
 585                  } else {
 586                      true
 587                  }
 588              });
 589  
 590              if !cont { return false }
 591  
 592              // The previous code handled assignments to paths that
 593              // have been restricted. This covers paths that have been
 594              // directly lent out and their base paths, but does not
 595              // cover random extensions of those paths. For example,
 596              // the following program is not declared illegal by the
 597              // previous check:
 598              //
 599              //    let mut v = ~[1, 2, 3];
 600              //    let p = &v;
 601              //    v[0] = 4; // declared error by loop below, not code above
 602              //
 603              // The reason that this passes the previous check whereas
 604              // an assignment like `v = ~[4]` fails is because the assignment
 605              // here is to `v[*]`, and the existing restrictions were issued
 606              // for `v`, not `v[*]`.
 607              //
 608              // So in this loop, we walk back up the loan path so long
 609              // as the mutability of the path is dependent on a super
 610              // path, and check that the super path was not lent out as
 611              // mutable or immutable (a const loan is ok).
 612              //
 613              // Mutability of a path can be dependent on the super path
 614              // in two ways. First, it might be inherited mutability.
 615              // Second, the pointee of an `&mut` pointer can only be
 616              // mutated if it is found in an unaliased location, so we
 617              // have to check that the owner location is not borrowed.
 618              //
 619              // Note that we are *not* checking for any and all
 620              // restrictions.  We are only interested in the pointers
 621              // that the user created, whereas we add restrictions for
 622              // all kinds of paths that are not directly aliased. If we checked
 623              // for all restrictions, and not just loans, then the following
 624              // valid program would be considered illegal:
 625              //
 626              //    let mut v = ~[1, 2, 3];
 627              //    let p = &const v[0];
 628              //    v[1] = 5; // ok
 629              //
 630              // Here the restriction that `v` not be mutated would be misapplied
 631              // to block the subpath `v[1]`.
 632              let full_loan_path = loan_path.clone();
 633              let mut loan_path = loan_path;
 634              loop {
 635                  loan_path = match *loan_path {
 636                      // Peel back one layer if, for `loan_path` to be
 637                      // mutable, `lp_base` must be mutable. This occurs
 638                      // with inherited mutability and with `&mut`
 639                      // pointers.
 640                      LpExtend(ref lp_base, mc::McInherited, _) |
 641                      LpExtend(ref lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => {
 642                          lp_base.clone()
 643                      }
 644  
 645                      // Otherwise stop iterating
 646                      LpExtend(_, mc::McDeclared, _) |
 647                      LpExtend(_, mc::McImmutable, _) |
 648                      LpVar(_) => {
 649                          return true;
 650                      }
 651                  };
 652  
 653                  // Check for a non-const loan of `loan_path`
 654                  let cont = this.each_in_scope_loan(expr.id, |loan| {
 655                      if loan.loan_path == loan_path {
 656                          this.report_illegal_mutation(expr, &*full_loan_path, loan);
 657                          false
 658                      } else {
 659                          true
 660                      }
 661                  });
 662  
 663                  if !cont { return false }
 664              }
 665          }
 666      }
 667  
 668      pub fn report_illegal_mutation(&self,
 669                                     expr&ast::Expr,
 670                                     loan_path&LoanPath,
 671                                     loan&Loan) {
 672          self.bccx.span_err(
 673              expr.span,
 674              format!("cannot assign to `{}` because it is borrowed",
 675                   self.bccx.loan_path_to_str(loan_path)));
 676          self.bccx.span_note(
 677              loan.span,
 678              format!("borrow of `{}` occurs here",
 679                   self.bccx.loan_path_to_str(loan_path)));
 680      }
 681  
 682      fn check_move_out_from_expr(&self, expr&ast::Expr) {
 683          match expr.node {
 684              ast::ExprFnBlock(..) | ast::ExprProc(..) => {
 685                  // Moves due to captures are checked in
 686                  // check_captured_variables() because it allows
 687                  // us to give a more precise error message with
 688                  // a more precise span.
 689              }
 690              _ => {
 691                  self.check_move_out_from_id(expr.id, expr.span)
 692              }
 693          }
 694      }
 695  
 696      fn check_move_out_from_id(&self, idast::NodeId, spanSpan) {
 697          self.move_data.each_path_moved_by(id, |_, move_path| {
 698              match self.analyze_move_out_from(id, move_path) {
 699                  MoveOk => {}
 700                  MoveWhileBorrowed(loan_path, loan_span) => {
 701                      self.bccx.span_err(
 702                          span,
 703                          format!("cannot move out of `{}` \
 704                                  because it is borrowed",
 705                               self.bccx.loan_path_to_str(move_path)));
 706                      self.bccx.span_note(
 707                          loan_span,
 708                          format!("borrow of `{}` occurs here",
 709                                  self.bccx.loan_path_to_str(&*loan_path)));
 710                  }
 711              }
 712              true
 713          });
 714      }
 715  
 716      fn check_captured_variables(&self,
 717                                  closure_idast::NodeId,
 718                                  spanSpan) {
 719          let freevar_mode = freevars::get_capture_mode(self.tcx(), closure_id);
 720          freevars::with_freevars(self.tcx(), closure_id, |freevars| {
 721              for freevar in freevars.iter() {
 722                  let var_id = ast_util::def_id_of_def(freevar.def).node;
 723                  let var_path = Rc::new(LpVar(var_id));
 724                  self.check_if_path_is_moved(closure_id, span,
 725                                              MovedInCapture, &var_path);
 726                  match freevar_mode {
 727                      freevars::CaptureByRef => { }
 728                      freevars::CaptureByValue => {
 729                          check_by_move_capture(self, closure_id, freevar, &*var_path);
 730                      }
 731                  }
 732              }
 733          });
 734          return;
 735  
 736          fn check_by_move_capture(this&CheckLoanCtxt,
 737                                   closure_idast::NodeId,
 738                                   freevar&freevars::freevar_entry,
 739                                   move_path&LoanPath) {
 740              let move_err = this.analyze_move_out_from(closure_id, move_path);
 741              match move_err {
 742                  MoveOk => {}
 743                  MoveWhileBorrowed(loan_path, loan_span) => {
 744                      this.bccx.span_err(
 745                          freevar.span,
 746                          format!("cannot move `{}` into closure \
 747                                  because it is borrowed",
 748                                  this.bccx.loan_path_to_str(move_path)));
 749                      this.bccx.span_note(
 750                          loan_span,
 751                          format!("borrow of `{}` occurs here",
 752                                  this.bccx.loan_path_to_str(&*loan_path)));
 753                  }
 754              }
 755          }
 756      }
 757  
 758      pub fn analyze_move_out_from(&self,
 759                                   expr_idast::NodeId,
 760                                   move_path&LoanPath)
 761                                   -> MoveError {
 762          debug!("analyze_move_out_from(expr_id={:?}, move_path={})",
 763                 self.tcx().map.node_to_str(expr_id),
 764                 move_path.repr(self.tcx()));
 765  
 766          // We must check every element of a move path. See
 767          // `borrowck-move-subcomponent.rs` for a test case.
 768  
 769          // check for a conflicting loan:
 770          let mut ret = MoveOk;
 771          self.each_in_scope_restriction(expr_id, move_path, |loan, _| {
 772              // Any restriction prevents moves.
 773              ret = MoveWhileBorrowed(loan.loan_path.clone(), loan.span);
 774              false
 775          });
 776  
 777          if ret != MoveOk {
 778              return ret
 779          }
 780  
 781          match *move_path {
 782              LpVar(_) => MoveOk,
 783              LpExtend(ref subpath, _, _) => {
 784                  self.analyze_move_out_from(expr_id, &**subpath)
 785              }
 786          }
 787      }
 788  
 789      pub fn check_call(&self,
 790                        _expr&ast::Expr,
 791                        _calleeOption<@ast::Expr>,
 792                        _callee_spanSpan,
 793                        _args&[@ast::Expr]) {
 794          // NB: This call to check for conflicting loans is not truly
 795          // necessary, because the callee_id never issues new loans.
 796          // However, I added it for consistency and lest the system
 797          // should change in the future.
 798          //
 799          // FIXME(#6268) nested method calls
 800          // self.check_for_conflicting_loans(callee_id);
 801      }
 802  }
 803  
 804  fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>,
 805                              local: &ast::Local) {
 806      visit::walk_local(this, local, ());
 807  }
 808  
 809  fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>,
 810                             expr: &ast::Expr) {
 811      visit::walk_expr(this, expr, ());
 812  
 813      debug!("check_loans_in_expr(expr={})",
 814             expr.repr(this.tcx()));
 815  
 816      this.check_for_conflicting_loans(expr.id);
 817      this.check_move_out_from_expr(expr);
 818  
 819      let method_map = this.bccx.tcx.method_map.borrow();
 820      match expr.node {
 821        ast::ExprPath(..) => {
 822            if !this.move_data.is_assignee(expr.id) {
 823                let cmt = this.bccx.cat_expr_unadjusted(expr);
 824                debug!("path cmt={}", cmt.repr(this.tcx()));
 825                for lp in opt_loan_path(&cmt).iter() {
 826                    this.check_if_path_is_moved(expr.id, expr.span, MovedInUse, lp);
 827                }
 828            }
 829        }
 830        ast::ExprFnBlock(..) | ast::ExprProc(..) => {
 831            this.check_captured_variables(expr.id, expr.span)
 832        }
 833        ast::ExprAssign(dest, _) |
 834        ast::ExprAssignOp(_, dest, _) => {
 835          this.check_assignment(dest);
 836        }
 837        ast::ExprCall(f, ref args) => {
 838          this.check_call(expr, Some(f), f.span, args.as_slice());
 839        }
 840        ast::ExprMethodCall(_, _, ref args) => {
 841          this.check_call(expr, None, expr.span, args.as_slice());
 842        }
 843        ast::ExprIndex(_, rval) | ast::ExprBinary(_, _, rval)
 844        if method_map.contains_key(&MethodCall::expr(expr.id)) => {
 845          this.check_call(expr, None, expr.span, [rval]);
 846        }
 847        ast::ExprUnary(_, _) | ast::ExprIndex(_, _)
 848        if method_map.contains_key(&MethodCall::expr(expr.id)) => {
 849          this.check_call(expr, None, expr.span, []);
 850        }
 851        ast::ExprInlineAsm(ref ia) => {
 852            for &(_, out) in ia.outputs.iter() {
 853                this.check_assignment(out);
 854            }
 855        }
 856        _ => {}
 857      }
 858  }
 859  
 860  fn check_loans_in_pat<'a>(this: &mut CheckLoanCtxt<'a>,
 861                            pat: &ast::Pat)
 862  {
 863      this.check_for_conflicting_loans(pat.id);
 864      this.check_move_out_from_id(pat.id, pat.span);
 865      visit::walk_pat(this, pat, ());
 866  }
 867  
 868  fn check_loans_in_block<'a>(this: &mut CheckLoanCtxt<'a>,
 869                              blk: &ast::Block)
 870  {
 871      visit::walk_block(this, blk, ());
 872      this.check_for_conflicting_loans(blk.id);
 873  }