]> git.lizzy.rs Git - rust.git/commitdiff
Separate borrowck into its own crate and remove dead code as well.
authorNiko Matsakis <niko@alum.mit.edu>
Fri, 5 Dec 2014 19:17:35 +0000 (14:17 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Sat, 13 Dec 2014 11:01:19 +0000 (06:01 -0500)
29 files changed:
mk/crates.mk
mk/tests.mk
src/librustc/lib.rs
src/librustc/middle/borrowck/check_loans.rs [deleted file]
src/librustc/middle/borrowck/doc.rs [deleted file]
src/librustc/middle/borrowck/fragments.rs [deleted file]
src/librustc/middle/borrowck/gather_loans/gather_moves.rs [deleted file]
src/librustc/middle/borrowck/gather_loans/lifetime.rs [deleted file]
src/librustc/middle/borrowck/gather_loans/mod.rs [deleted file]
src/librustc/middle/borrowck/gather_loans/move_error.rs [deleted file]
src/librustc/middle/borrowck/gather_loans/restrictions.rs [deleted file]
src/librustc/middle/borrowck/graphviz.rs [deleted file]
src/librustc/middle/borrowck/mod.rs [deleted file]
src/librustc/middle/borrowck/move_data.rs [deleted file]
src/librustc_borrowck/borrowck/check_loans.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/doc.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/fragments.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/gather_loans/lifetime.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/gather_loans/mod.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/gather_loans/move_error.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/gather_loans/restrictions.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/mod.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/move_data.rs [new file with mode: 0644]
src/librustc_borrowck/graphviz.rs [new file with mode: 0644]
src/librustc_borrowck/lib.rs [new file with mode: 0644]
src/librustc_driver/driver.rs
src/librustc_driver/lib.rs
src/librustc_driver/pretty.rs

index 3a2def389cc24e92e907ad9fbcdf9e5c56393d35..a47d4871326dff477c93d34ca90a086ea5e57bca 100644 (file)
@@ -53,7 +53,7 @@ TARGET_CRATES := libc std flate arena term \
                  serialize getopts collections test time rand \
                  log regex graphviz core rbml alloc rustrt \
                  unicode
-RUSTC_CRATES := rustc rustc_typeck rustc_driver rustc_trans rustc_back rustc_llvm 
+RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_driver rustc_trans rustc_back rustc_llvm 
 HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc regex_macros fmt_macros
 CRATES := $(TARGET_CRATES) $(HOST_CRATES)
 TOOLS := compiletest rustdoc rustc
@@ -67,11 +67,12 @@ DEPS_std := core libc rand alloc collections rustrt unicode \
        native:rust_builtin native:backtrace
 DEPS_graphviz := std
 DEPS_syntax := std term serialize log fmt_macros arena libc
-DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back \
+DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
                      rustc_typeck log syntax serialize rustc_llvm rustc_trans
 DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \
                        log syntax serialize rustc_llvm
 DEPS_rustc_typeck := rustc syntax
+DEPS_rustc_borrowck := rustc log graphviz syntax
 DEPS_rustc := syntax flate arena serialize getopts rbml \
               time log graphviz rustc_llvm rustc_back
 DEPS_rustc_llvm := native:rustllvm libc std
@@ -117,9 +118,10 @@ ONLY_RLIB_unicode := 1
 DOC_CRATES := $(filter-out rustc, \
               $(filter-out rustc_trans, \
               $(filter-out rustc_typeck, \
+              $(filter-out rustc_borrowck, \
               $(filter-out rustc_driver, \
-              $(filter-out syntax, $(CRATES))))))
-COMPILER_DOC_CRATES := rustc rustc_trans rustc_typeck rustc_driver syntax
+              $(filter-out syntax, $(CRATES)))))))
+COMPILER_DOC_CRATES := rustc rustc_trans rustc_borrowck rustc_typeck rustc_driver syntax
 
 # This macro creates some simple definitions for each crate being built, just
 # some munging of all of the parameters above.
index b4b8249a8cb42ef5a7ba8ddb30d82faa6517e42d..3340f9b4969ea2d7c12335e120bb9e22cba1ce38 100644 (file)
@@ -21,7 +21,7 @@ $(eval $(call RUST_CRATE,coretest))
 
 TEST_TARGET_CRATES = $(filter-out core unicode,$(TARGET_CRATES)) coretest
 TEST_DOC_CRATES = $(DOC_CRATES)
-TEST_HOST_CRATES = $(filter-out rustc_typeck rustc_trans,$(HOST_CRATES))
+TEST_HOST_CRATES = $(filter-out rustc_typeck rustc_borrowck rustc_trans,$(HOST_CRATES))
 TEST_CRATES = $(TEST_TARGET_CRATES) $(TEST_HOST_CRATES)
 
 ######################################################################
index 918f612aaa5d5a7fab1860d86c48c617d53236a6..51cb7e193ffb8e31c6473962a1e54f5c007ff94d 100644 (file)
@@ -61,7 +61,6 @@ pub mod back {
 pub mod middle {
     pub mod astconv_util;
     pub mod astencode;
-    pub mod borrowck;
     pub mod cfg;
     pub mod check_const;
     pub mod check_static_recursion;
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
deleted file mode 100644 (file)
index 5f03032..0000000
+++ /dev/null
@@ -1,969 +0,0 @@
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ----------------------------------------------------------------------
-// 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
-// 4. moves do not affect things loaned out in any way
-use self::UseError::*;
-
-use middle::borrowck::*;
-use middle::borrowck::LoanPathElem::*;
-use middle::borrowck::LoanPathKind::*;
-use middle::expr_use_visitor as euv;
-use middle::mem_categorization as mc;
-use middle::region;
-use middle::ty::ParameterEnvironment;
-use middle::ty;
-use syntax::ast::NodeId;
-use syntax::ast;
-use syntax::codemap::Span;
-use util::ppaux::Repr;
-
-use std::rc::Rc;
-
-// FIXME (#16118): These functions are intended to allow the borrow checker to
-// be less precise in its handling of Box while still allowing moves out of a
-// Box. They should be removed when OwnedPtr is removed from LoanPath.
-
-fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
-    //! Returns the base of the leftmost dereference of an OwnedPtr in
-    //! `loan_path`. If there is no dereference of an OwnedPtr in `loan_path`,
-    //! then it just returns `loan_path` itself.
-
-    return match helper(loan_path) {
-        Some(new_loan_path) => new_loan_path,
-        None => loan_path.clone()
-    };
-
-    fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
-        match loan_path.kind {
-            LpVar(_) | LpUpvar(_) => None,
-            LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
-                match helper(&**lp_base) {
-                    v @ Some(_) => v,
-                    None => Some(&**lp_base)
-                }
-            }
-            LpDowncast(ref lp_base, _) |
-            LpExtend(ref lp_base, _, _) => helper(&**lp_base)
-        }
-    }
-}
-
-fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
-    //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
-    //! a &LoanPath.
-
-    return match helper(loan_path) {
-        Some(new_loan_path) => new_loan_path,
-        None => loan_path.clone()
-    };
-
-    fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
-        match loan_path.kind {
-            LpVar(_) | LpUpvar(_) => None,
-            LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
-                match helper(lp_base) {
-                    v @ Some(_) => v,
-                    None => Some(lp_base.clone())
-                }
-            }
-            LpDowncast(ref lp_base, _) |
-            LpExtend(ref lp_base, _, _) => helper(lp_base)
-        }
-    }
-}
-
-struct CheckLoanCtxt<'a, 'tcx: 'a> {
-    bccx: &'a BorrowckCtxt<'a, 'tcx>,
-    dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
-    move_data: move_data::FlowedMoveData<'a, 'tcx>,
-    all_loans: &'a [Loan<'tcx>],
-    param_env: &'a ParameterEnvironment<'tcx>,
-}
-
-impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
-    fn consume(&mut self,
-               consume_id: ast::NodeId,
-               consume_span: Span,
-               cmt: mc::cmt<'tcx>,
-               mode: euv::ConsumeMode) {
-        debug!("consume(consume_id={}, cmt={}, mode={})",
-               consume_id, cmt.repr(self.tcx()), mode);
-
-        self.consume_common(consume_id, consume_span, cmt, mode);
-    }
-
-    fn matched_pat(&mut self,
-                   _matched_pat: &ast::Pat,
-                   _cmt: mc::cmt,
-                   _mode: euv::MatchMode) { }
-
-    fn consume_pat(&mut self,
-                   consume_pat: &ast::Pat,
-                   cmt: mc::cmt<'tcx>,
-                   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);
-    }
-
-    fn borrow(&mut self,
-              borrow_id: ast::NodeId,
-              borrow_span: Span,
-              cmt: mc::cmt<'tcx>,
-              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(region::CodeExtent::from_node_id(borrow_id));
-    }
-
-    fn mutate(&mut self,
-              assignment_id: ast::NodeId,
-              assignment_span: Span,
-              assignee_cmt: mc::cmt<'tcx>,
-              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);
-    }
-
-    fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
-}
-
-pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                     dfcx_loans: &LoanDataFlow<'b, 'tcx>,
-                                     move_data: move_data::FlowedMoveData<'c, 'tcx>,
-                                     all_loans: &[Loan<'tcx>],
-                                     fn_id: NodeId,
-                                     decl: &ast::FnDecl,
-                                     body: &ast::Block) {
-    debug!("check_loans(body id={})", body.id);
-
-    let param_env = ParameterEnvironment::for_item(bccx.tcx, fn_id);
-
-    let mut clcx = CheckLoanCtxt {
-        bccx: bccx,
-        dfcx_loans: dfcx_loans,
-        move_data: move_data,
-        all_loans: all_loans,
-        param_env: &param_env,
-    };
-
-    {
-        let mut euv = euv::ExprUseVisitor::new(&mut clcx,
-                                               bccx.tcx,
-                                               param_env.clone());
-        euv.walk_fn(decl, body);
-    }
-}
-
-#[deriving(PartialEq)]
-enum UseError<'tcx> {
-    UseOk,
-    UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
-}
-
-fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
-                           borrow_kind2: ty::BorrowKind)
-                           -> bool {
-    borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
-}
-
-impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
-    pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
-
-    pub fn each_issued_loan(&self, scope: region::CodeExtent, op: |&Loan<'tcx>| -> bool)
-                            -> bool {
-        //! Iterates over each loan that has been issued
-        //! on entrance to `scope`, 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.
-
-        self.dfcx_loans.each_bit_on_entry(scope.node_id(), |loan_index| {
-            let loan = &self.all_loans[loan_index];
-            op(loan)
-        })
-    }
-
-    pub fn each_in_scope_loan(&self,
-                              scope: region::CodeExtent,
-                              op: |&Loan<'tcx>| -> bool)
-                              -> bool {
-        //! Like `each_issued_loan()`, but only considers loans that are
-        //! currently in scope.
-
-        let tcx = self.tcx();
-        self.each_issued_loan(scope, |loan| {
-            if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
-                op(loan)
-            } else {
-                true
-            }
-        })
-    }
-
-    fn each_in_scope_loan_affecting_path(&self,
-                                         scope: region::CodeExtent,
-                                         loan_path: &LoanPath<'tcx>,
-                                         op: |&Loan<'tcx>| -> 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 loan_path = owned_ptr_base_path(loan_path);
-        let cont = self.each_in_scope_loan(scope, |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.kind {
-                LpVar(_) | LpUpvar(_) => {
-                    break;
-                }
-                LpDowncast(ref lp_base, _) |
-                LpExtend(ref lp_base, _, _) => {
-                    loan_path = &**lp_base;
-                }
-            }
-
-            let cont = self.each_in_scope_loan(scope, |loan| {
-                if *loan.loan_path == *loan_path {
-                    op(loan)
-                } else {
-                    true
-                }
-            });
-
-            if !cont {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    pub fn loans_generated_by(&self, scope: region::CodeExtent) -> Vec<uint> {
-        //! Returns a vector of the loans that are generated as
-        //! we enter `scope`.
-
-        let mut result = Vec::new();
-        self.dfcx_loans.each_gen_bit(scope.node_id(), |loan_index| {
-            result.push(loan_index);
-            true
-        });
-        return result;
-    }
-
-    pub fn check_for_conflicting_loans(&self, scope: region::CodeExtent) {
-        //! Checks to see whether any of the loans that are issued
-        //! on entrance to `scope` conflict with loans that have already been
-        //! issued when we enter `scope` (for example, we do not
-        //! permit two `&mut` borrows of the same variable).
-        //!
-        //! (Note that some loans can be *issued* without necessarily
-        //! taking effect yet.)
-
-        debug!("check_for_conflicting_loans(scope={})", scope);
-
-        let new_loan_indices = self.loans_generated_by(scope);
-        debug!("new_loan_indices = {}", new_loan_indices);
-
-        self.each_issued_loan(scope, |issued_loan| {
-            for &new_loan_index in new_loan_indices.iter() {
-                let new_loan = &self.all_loans[new_loan_index];
-                self.report_error_if_loans_conflict(issued_loan, new_loan);
-            }
-            true
-        });
-
-        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];
-                self.report_error_if_loans_conflict(old_loan, new_loan);
-            }
-        }
-    }
-
-    pub fn report_error_if_loans_conflict(&self,
-                                          old_loan: &Loan<'tcx>,
-                                          new_loan: &Loan<'tcx>) {
-        //! Checks whether `old_loan` and `new_loan` can safely be issued
-        //! simultaneously.
-
-        debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
-               old_loan.repr(self.tcx()),
-               new_loan.repr(self.tcx()));
-
-        // Should only be called for loans that are in scope at the same time.
-        assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
-                                                        new_loan.kill_scope));
-
-        self.report_error_if_loan_conflicts_with_restriction(
-            old_loan, new_loan, old_loan, new_loan) &&
-        self.report_error_if_loan_conflicts_with_restriction(
-            new_loan, old_loan, old_loan, new_loan);
-    }
-
-    pub fn report_error_if_loan_conflicts_with_restriction(&self,
-                                                           loan1: &Loan<'tcx>,
-                                                           loan2: &Loan<'tcx>,
-                                                           old_loan: &Loan<'tcx>,
-                                                           new_loan: &Loan<'tcx>)
-                                                           -> bool {
-        //! Checks whether the restrictions introduced by `loan1` would
-        //! prohibit `loan2`. Returns false if an error is reported.
-
-        debug!("report_error_if_loan_conflicts_with_restriction(\
-                loan1={}, loan2={})",
-               loan1.repr(self.tcx()),
-               loan2.repr(self.tcx()));
-
-        if compatible_borrow_kinds(loan1.kind, loan2.kind) {
-            return true;
-        }
-
-        let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
-        for restr_path in loan1.restricted_paths.iter() {
-            if *restr_path != loan2_base_path { continue; }
-
-            // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
-            // normally generate a rather confusing message (in this case, for multiple mutable
-            // borrows):
-            //
-            //     error: cannot borrow `x.b` as mutable more than once at a time
-            //     note: previous borrow of `x.a` occurs here; the mutable borrow prevents
-            //     subsequent moves, borrows, or modification of `x.a` until the borrow ends
-            //
-            // What we want to do instead is get the 'common ancestor' of the two borrow paths and
-            // use that for most of the message instead, giving is something like this:
-            //
-            //     error: cannot borrow `x` as mutable more than once at a time
-            //     note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
-            //     borrow prevents subsequent moves, borrows, or modification of `x` until the
-            //     borrow ends
-
-            let common = new_loan.loan_path.common(&*old_loan.loan_path);
-            let (nl, ol, new_loan_msg, old_loan_msg) =
-                if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
-                    let nl = self.bccx.loan_path_to_string(&common.unwrap());
-                    let ol = nl.clone();
-                    let new_loan_msg = format!(" (here through borrowing `{}`)",
-                                               self.bccx.loan_path_to_string(
-                                                   &*new_loan.loan_path));
-                    let old_loan_msg = format!(" (through borrowing `{}`)",
-                                               self.bccx.loan_path_to_string(
-                                                   &*old_loan.loan_path));
-                    (nl, ol, new_loan_msg, old_loan_msg)
-                } else {
-                    (self.bccx.loan_path_to_string(&*new_loan.loan_path),
-                     self.bccx.loan_path_to_string(&*old_loan.loan_path),
-                     String::new(), String::new())
-                };
-
-            let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
-                "it".to_string()
-            } else {
-                format!("`{}`", ol)
-            };
-
-            match (new_loan.kind, old_loan.kind) {
-                (ty::MutBorrow, ty::MutBorrow) => {
-                    self.bccx.span_err(
-                        new_loan.span,
-                        format!("cannot borrow `{}`{} as mutable \
-                                more than once at a time",
-                                nl, new_loan_msg).as_slice())
-                }
-
-                (ty::UniqueImmBorrow, _) => {
-                    self.bccx.span_err(
-                        new_loan.span,
-                        format!("closure requires unique access to `{}` \
-                                but {} is already borrowed{}",
-                                nl, ol_pronoun, old_loan_msg).as_slice());
-                }
-
-                (_, ty::UniqueImmBorrow) => {
-                    self.bccx.span_err(
-                        new_loan.span,
-                        format!("cannot borrow `{}`{} as {} because \
-                                previous closure requires unique access",
-                                nl, new_loan_msg, new_loan.kind.to_user_str()).as_slice());
-                }
-
-                (_, _) => {
-                    self.bccx.span_err(
-                        new_loan.span,
-                        format!("cannot borrow `{}`{} as {} because \
-                                {} is also borrowed as {}{}",
-                                nl,
-                                new_loan_msg,
-                                new_loan.kind.to_user_str(),
-                                ol_pronoun,
-                                old_loan.kind.to_user_str(),
-                                old_loan_msg).as_slice());
-                }
-            }
-
-            match new_loan.cause {
-                euv::ClosureCapture(span) => {
-                    self.bccx.span_note(
-                        span,
-                        format!("borrow occurs due to use of `{}` in closure",
-                                nl).as_slice());
-                }
-                _ => { }
-            }
-
-            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",
-                            ol)
-                }
-
-                ty::ImmBorrow => {
-                    format!("the immutable borrow prevents subsequent \
-                            moves or mutable borrows of `{0}` \
-                            until the borrow ends",
-                            ol)
-                }
-
-                ty::UniqueImmBorrow => {
-                    format!("the unique capture prevents subsequent \
-                            moves or borrows of `{0}` \
-                            until the borrow ends",
-                            ol)
-                }
-            };
-
-            let borrow_summary = match old_loan.cause {
-                euv::ClosureCapture(_) => {
-                    format!("previous borrow of `{}` occurs here{} due to \
-                            use in closure",
-                            ol, old_loan_msg)
-                }
-
-                euv::OverloadedOperator(..) |
-                euv::AddrOf(..) |
-                euv::AutoRef(..) |
-                euv::ClosureInvocation(..) |
-                euv::ForLoop(..) |
-                euv::RefBinding(..) |
-                euv::MatchDiscriminant(..) => {
-                    format!("previous borrow of `{}` occurs here{}",
-                            ol, old_loan_msg)
-                }
-            };
-
-            self.bccx.span_note(
-                old_loan.span,
-                format!("{}; {}", borrow_summary, rule_summary).as_slice());
-
-            let old_loan_span = self.tcx().map.span(old_loan.kill_scope.node_id());
-            self.bccx.span_end_note(old_loan_span,
-                                    "previous borrow ends here");
-
-            return false;
-        }
-
-        true
-    }
-
-    fn is_local_variable_or_arg(&self, cmt: mc::cmt<'tcx>) -> bool {
-        match cmt.cat {
-          mc::cat_local(_) => true,
-          _ => false
-        }
-    }
-
-    fn consume_common(&self,
-                      id: ast::NodeId,
-                      span: Span,
-                      cmt: mc::cmt<'tcx>,
-                      mode: euv::ConsumeMode) {
-        match opt_loan_path(&cmt) {
-            Some(lp) => {
-                let moved_value_use_kind = match mode {
-                    euv::Copy => {
-                        self.check_for_copy_of_frozen_path(id, span, &*lp);
-                        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,
-                                                                     &*lp, move_kind);
-                                if move_kind == move_data::Captured {
-                                    MovedInCapture
-                                } else {
-                                    MovedInUse
-                                }
-                            }
-                        }
-                    }
-                };
-
-                self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
-            }
-            None => { }
-        }
-    }
-
-    fn check_for_copy_of_frozen_path(&self,
-                                     id: ast::NodeId,
-                                     span: Span,
-                                     copy_path: &LoanPath<'tcx>) {
-        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",
-                            self.bccx.loan_path_to_string(copy_path).as_slice())
-                    .as_slice());
-                self.bccx.span_note(
-                    loan_span,
-                    format!("borrow of `{}` occurs here",
-                            self.bccx.loan_path_to_string(&*loan_path).as_slice())
-                    .as_slice());
-            }
-        }
-    }
-
-    fn check_for_move_of_borrowed_path(&self,
-                                       id: ast::NodeId,
-                                       span: Span,
-                                       move_path: &LoanPath<'tcx>,
-                                       move_kind: move_data::MoveKind) {
-        // 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.
-        match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
-            UseOk => { }
-            UseWhileBorrowed(loan_path, loan_span) => {
-                let err_message = match move_kind {
-                    move_data::Captured =>
-                        format!("cannot move `{}` into closure because it is borrowed",
-                                self.bccx.loan_path_to_string(move_path).as_slice()),
-                    move_data::Declared |
-                    move_data::MoveExpr |
-                    move_data::MovePat =>
-                        format!("cannot move out of `{}` because it is borrowed",
-                                self.bccx.loan_path_to_string(move_path).as_slice())
-                };
-
-                self.bccx.span_err(span, err_message.as_slice());
-                self.bccx.span_note(
-                    loan_span,
-                    format!("borrow of `{}` occurs here",
-                            self.bccx.loan_path_to_string(&*loan_path).as_slice())
-                    .as_slice());
-            }
-        }
-    }
-
-    pub fn analyze_restrictions_on_use(&self,
-                                       expr_id: ast::NodeId,
-                                       use_path: &LoanPath<'tcx>,
-                                       borrow_kind: ty::BorrowKind)
-                                       -> UseError<'tcx> {
-        debug!("analyze_restrictions_on_use(expr_id={}, use_path={})",
-               self.tcx().map.node_to_string(expr_id),
-               use_path.repr(self.tcx()));
-
-        let mut ret = UseOk;
-
-        self.each_in_scope_loan_affecting_path(
-            region::CodeExtent::from_node_id(expr_id), use_path, |loan| {
-            if !compatible_borrow_kinds(loan.kind, borrow_kind) {
-                ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
-                false
-            } else {
-                true
-            }
-        });
-
-        return ret;
-    }
-
-    /// Reports an error if `expr` (which should be a path)
-    /// is using a moved/uninitialized value
-    fn check_if_path_is_moved(&self,
-                              id: ast::NodeId,
-                              span: Span,
-                              use_kind: MovedValueUseKind,
-                              lp: &Rc<LoanPath<'tcx>>) {
-        debug!("check_if_path_is_moved(id={}, use_kind={}, lp={})",
-               id, use_kind, lp.repr(self.bccx.tcx));
-        let base_lp = owned_ptr_base_path_rc(lp);
-        self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
-            self.bccx.report_use_of_moved_value(
-                span,
-                use_kind,
-                &**lp,
-                the_move,
-                moved_lp,
-                self.param_env);
-            false
-        });
-    }
-
-    /// 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
-    /// ```
-    fn check_if_assigned_path_is_moved(&self,
-                                       id: ast::NodeId,
-                                       span: Span,
-                                       use_kind: MovedValueUseKind,
-                                       lp: &Rc<LoanPath<'tcx>>)
-    {
-        match lp.kind {
-            LpVar(_) | LpUpvar(_) => {
-                // assigning to `x` does not require that `x` is initialized
-            }
-            LpDowncast(ref lp_base, _) => {
-                // assigning to `(P->Variant).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, _, 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);
-            }
-        }
-    }
-
-    fn check_assignment(&self,
-                        assignment_id: ast::NodeId,
-                        assignment_span: Span,
-                        assignee_cmt: mc::cmt<'tcx>,
-                        mode: euv::MutateMode) {
-        debug!("check_assignment(assignee_cmt={})", assignee_cmt.repr(self.tcx()));
-
-        // Mutable values can be assigned, as long as they obey loans
-        // and aliasing restrictions:
-        if assignee_cmt.mutbl.is_mutable() {
-            if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) {
-                if mode != euv::Init {
-                    check_for_assignment_to_borrowed_path(
-                        self, assignment_id, assignment_span, assignee_cmt.clone());
-                    mark_variable_as_used_mut(self, assignee_cmt);
-                }
-            }
-            return;
-        }
-
-        // Initializations are OK.
-        if mode == euv::Init {
-            return
-        }
-
-        // For immutable local variables, assignments are legal
-        // if they cannot already have been assigned
-        if self.is_local_variable_or_arg(assignee_cmt.clone()) {
-            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| {
-                self.bccx.report_reassigned_immutable_variable(
-                    assignment_span,
-                    &*lp,
-                    assign);
-                false
-            });
-            return;
-        }
-
-        // Otherwise, just a plain error.
-        match assignee_cmt.note {
-            mc::NoteClosureEnv(upvar_id) => {
-                // If this is an `Fn` closure, it simply can't mutate upvars.
-                // If it's an `FnMut` closure, the original variable was declared immutable.
-                // We need to determine which is the case here.
-                let kind = match assignee_cmt.upvar().unwrap().cat {
-                    mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
-                    _ => unreachable!()
-                };
-                if kind == ty::FnUnboxedClosureKind {
-                    self.bccx.span_err(
-                        assignment_span,
-                        format!("cannot assign to {}",
-                                self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
-                    self.bccx.span_help(
-                        self.tcx().map.span(upvar_id.closure_expr_id),
-                        "consider changing this closure to take self by mutable reference");
-                } else {
-                    self.bccx.span_err(
-                        assignment_span,
-                        format!("cannot assign to {} {}",
-                                assignee_cmt.mutbl.to_user_str(),
-                                self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
-                }
-            }
-            _ => match opt_loan_path(&assignee_cmt) {
-                Some(lp) => {
-                    self.bccx.span_err(
-                        assignment_span,
-                        format!("cannot assign to {} {} `{}`",
-                                assignee_cmt.mutbl.to_user_str(),
-                                self.bccx.cmt_to_string(&*assignee_cmt),
-                                self.bccx.loan_path_to_string(&*lp)).as_slice());
-                }
-                None => {
-                    self.bccx.span_err(
-                        assignment_span,
-                        format!("cannot assign to {} {}",
-                                assignee_cmt.mutbl.to_user_str(),
-                                self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
-                }
-            }
-        }
-        return;
-
-        fn mark_variable_as_used_mut<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
-                                               mut cmt: mc::cmt<'tcx>) {
-            //! If the mutability of the `cmt` being written is inherited
-            //! from a local variable, liveness will
-            //! not have been able to detect that this variable's mutability
-            //! is important, so we must add the variable to the
-            //! `used_mut_nodes` table here.
-
-            loop {
-                debug!("mark_variable_as_used_mut(cmt={})", cmt.repr(this.tcx()));
-                match cmt.cat.clone() {
-                    mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: id, .. }, .. }) |
-                    mc::cat_local(id) => {
-                        this.tcx().used_mut_nodes.borrow_mut().insert(id);
-                        return;
-                    }
-
-                    mc::cat_rvalue(..) |
-                    mc::cat_static_item |
-                    mc::cat_deref(_, _, mc::UnsafePtr(..)) |
-                    mc::cat_deref(_, _, mc::Implicit(..)) => {
-                        assert_eq!(cmt.mutbl, mc::McDeclared);
-                        return;
-                    }
-
-                    mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
-                        assert_eq!(cmt.mutbl, mc::McDeclared);
-                        // We need to drill down to upvar if applicable
-                        match cmt.upvar() {
-                            Some(b) => cmt = b,
-                            None => return
-                        }
-                    }
-
-                    mc::cat_deref(b, _, mc::OwnedPtr) => {
-                        assert_eq!(cmt.mutbl, mc::McInherited);
-                        cmt = b;
-                    }
-
-                    mc::cat_downcast(b, _) |
-                    mc::cat_interior(b, _) => {
-                        assert_eq!(cmt.mutbl, mc::McInherited);
-                        cmt = b;
-                    }
-                }
-            }
-        }
-
-        fn check_for_aliasable_mutable_writes<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
-                                                        span: Span,
-                                                        cmt: mc::cmt<'tcx>) -> bool {
-            //! Safety checks related to writes to aliasable, mutable locations
-
-            let guarantor = cmt.guarantor();
-            debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
-                   cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
-            if let mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) = guarantor.cat {
-                // Statically prohibit writes to `&mut` when aliasable
-                check_for_aliasability_violation(this, span, b.clone());
-            }
-
-            return true; // no errors reported
-        }
-
-        fn check_for_aliasability_violation<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
-                                                      span: Span,
-                                                      cmt: mc::cmt<'tcx>)
-                                                      -> bool {
-            match cmt.freely_aliasable(this.tcx()) {
-                None => {
-                    return true;
-                }
-                Some(mc::AliasableStaticMut(..)) => {
-                    return true;
-                }
-                Some(cause) => {
-                    this.bccx.report_aliasability_violation(
-                        span,
-                        MutabilityViolation,
-                        cause);
-                    return false;
-                }
-            }
-        }
-
-        fn check_for_assignment_to_borrowed_path<'a, 'tcx>(
-            this: &CheckLoanCtxt<'a, 'tcx>,
-            assignment_id: ast::NodeId,
-            assignment_span: Span,
-            assignee_cmt: mc::cmt<'tcx>)
-        {
-            //! Check for assignments that violate the terms of an
-            //! outstanding loan.
-
-            let loan_path = match opt_loan_path(&assignee_cmt) {
-                Some(lp) => lp,
-                None => { return; /* no loan path, can't be any loans */ }
-            };
-
-            let scope = region::CodeExtent::from_node_id(assignment_id);
-            this.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
-                this.report_illegal_mutation(assignment_span, &*loan_path, loan);
-                false
-            });
-        }
-    }
-
-    pub fn report_illegal_mutation(&self,
-                                   span: Span,
-                                   loan_path: &LoanPath<'tcx>,
-                                   loan: &Loan) {
-        self.bccx.span_err(
-            span,
-            format!("cannot assign to `{}` because it is borrowed",
-                    self.bccx.loan_path_to_string(loan_path)).as_slice());
-        self.bccx.span_note(
-            loan.span,
-            format!("borrow of `{}` occurs here",
-                    self.bccx.loan_path_to_string(loan_path)).as_slice());
-    }
-}
diff --git a/src/librustc/middle/borrowck/doc.rs b/src/librustc/middle/borrowck/doc.rs
deleted file mode 100644 (file)
index c6db534..0000000
+++ /dev/null
@@ -1,1222 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! # The Borrow Checker
-//!
-//! This pass has the job of enforcing memory safety. This is a subtle
-//! topic. This docs aim to explain both the practice and the theory
-//! behind the borrow checker. They start with a high-level overview of
-//! how it works, and then proceed to dive into the theoretical
-//! background. Finally, they go into detail on some of the more subtle
-//! aspects.
-//!
-//! # Table of contents
-//!
-//! These docs are long. Search for the section you are interested in.
-//!
-//! - Overview
-//! - Formal model
-//! - Borrowing and loans
-//! - Moves and initialization
-//! - Drop flags and structural fragments
-//! - Future work
-//!
-//! # Overview
-//!
-//! The borrow checker checks one function at a time. It operates in two
-//! passes. The first pass, called `gather_loans`, walks over the function
-//! and identifies all of the places where borrows (e.g., `&` expressions
-//! and `ref` bindings) and moves (copies or captures of a linear value)
-//! occur. It also tracks initialization sites. For each borrow and move,
-//! it checks various basic safety conditions at this time (for example,
-//! that the lifetime of the borrow doesn't exceed the lifetime of the
-//! value being borrowed, or that there is no move out of an `&T`
-//! referent).
-//!
-//! It then uses the dataflow module to propagate which of those borrows
-//! may be in scope at each point in the procedure. A loan is considered
-//! to come into scope at the expression that caused it and to go out of
-//! scope when the lifetime of the resulting reference expires.
-//!
-//! Once the in-scope loans are known for each point in the program, the
-//! borrow checker walks the IR again in a second pass called
-//! `check_loans`. This pass examines each statement and makes sure that
-//! it is safe with respect to the in-scope loans.
-//!
-//! # Formal model
-//!
-//! Throughout the docs we'll consider a simple subset of Rust in which
-//! you can only borrow from lvalues, defined like so:
-//!
-//! ```text
-//! LV = x | LV.f | *LV
-//! ```
-//!
-//! Here `x` represents some variable, `LV.f` is a field reference,
-//! and `*LV` is a pointer dereference. There is no auto-deref or other
-//! niceties. This means that if you have a type like:
-//!
-//! ```text
-//! struct S { f: uint }
-//! ```
-//!
-//! and a variable `a: Box<S>`, then the rust expression `a.f` would correspond
-//! to an `LV` of `(*a).f`.
-//!
-//! Here is the formal grammar for the types we'll consider:
-//!
-//! ```text
-//! TY = () | S<'LT...> | Box<TY> | & 'LT MQ TY
-//! MQ = mut | imm | const
-//! ```
-//!
-//! Most of these types should be pretty self explanatory. Here `S` is a
-//! struct name and we assume structs are declared like so:
-//!
-//! ```text
-//! SD = struct S<'LT...> { (f: TY)... }
-//! ```
-//!
-//! # Borrowing and loans
-//!
-//! ## An intuitive explanation
-//!
-//! ### Issuing loans
-//!
-//! Now, imagine we had a program like this:
-//!
-//! ```text
-//! struct Foo { f: uint, g: uint }
-//! ...
-//! 'a: {
-//!   let mut x: Box<Foo> = ...;
-//!   let y = &mut (*x).f;
-//!   x = ...;
-//! }
-//! ```
-//!
-//! This is of course dangerous because mutating `x` will free the old
-//! value and hence invalidate `y`. The borrow checker aims to prevent
-//! this sort of thing.
-//!
-//! #### Loans and restrictions
-//!
-//! The way the borrow checker works is that it analyzes each borrow
-//! expression (in our simple model, that's stuff like `&LV`, though in
-//! real life there are a few other cases to consider). For each borrow
-//! expression, it computes a `Loan`, which is a data structure that
-//! records (1) the value being borrowed, (2) the mutability and scope of
-//! the borrow, and (3) a set of restrictions. In the code, `Loan` is a
-//! struct defined in `middle::borrowck`. Formally, we define `LOAN` as
-//! follows:
-//!
-//! ```text
-//! LOAN = (LV, LT, MQ, RESTRICTION*)
-//! RESTRICTION = (LV, ACTION*)
-//! ACTION = MUTATE | CLAIM | FREEZE
-//! ```
-//!
-//! Here the `LOAN` tuple defines the lvalue `LV` being borrowed; the
-//! lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a
-//! list of restrictions. The restrictions indicate actions which, if
-//! taken, could invalidate the loan and lead to type safety violations.
-//!
-//! Each `RESTRICTION` is a pair of a restrictive lvalue `LV` (which will
-//! either be the path that was borrowed or some prefix of the path that
-//! was borrowed) and a set of restricted actions.  There are three kinds
-//! of actions that may be restricted for the path `LV`:
-//!
-//! - `MUTATE` means that `LV` cannot be assigned to;
-//! - `CLAIM` means that the `LV` cannot be borrowed mutably;
-//! - `FREEZE` means that the `LV` cannot be borrowed immutably;
-//!
-//! Finally, it is never possible to move from an lvalue that appears in a
-//! restriction. This implies that the "empty restriction" `(LV, [])`,
-//! which contains an empty set of actions, still has a purpose---it
-//! prevents moves from `LV`. I chose not to make `MOVE` a fourth kind of
-//! action because that would imply that sometimes moves are permitted
-//! from restrictived values, which is not the case.
-//!
-//! #### Example
-//!
-//! To give you a better feeling for what kind of restrictions derived
-//! from a loan, let's look at the loan `L` that would be issued as a
-//! result of the borrow `&mut (*x).f` in the example above:
-//!
-//! ```text
-//! L = ((*x).f, 'a, mut, RS) where
-//!     RS = [((*x).f, [MUTATE, CLAIM, FREEZE]),
-//!           (*x, [MUTATE, CLAIM, FREEZE]),
-//!           (x, [MUTATE, CLAIM, FREEZE])]
-//! ```
-//!
-//! The loan states that the expression `(*x).f` has been loaned as
-//! mutable for the lifetime `'a`. Because the loan is mutable, that means
-//! that the value `(*x).f` may be mutated via the newly created reference
-//! (and *only* via that pointer). This is reflected in the
-//! restrictions `RS` that accompany the loan.
-//!
-//! The first restriction `((*x).f, [MUTATE, CLAIM, FREEZE])` states that
-//! the lender may not mutate, freeze, nor alias `(*x).f`. Mutation is
-//! illegal because `(*x).f` is only supposed to be mutated via the new
-//! reference, not by mutating the original path `(*x).f`. Freezing is
-//! illegal because the path now has an `&mut` alias; so even if we the
-//! lender were to consider `(*x).f` to be immutable, it might be mutated
-//! via this alias. They will be enforced for the lifetime `'a` of the
-//! loan. After the loan expires, the restrictions no longer apply.
-//!
-//! The second restriction on `*x` is interesting because it does not
-//! apply to the path that was lent (`(*x).f`) but rather to a prefix of
-//! the borrowed path. This is due to the rules of inherited mutability:
-//! if the user were to assign to (or freeze) `*x`, they would indirectly
-//! overwrite (or freeze) `(*x).f`, and thus invalidate the reference
-//! that was created. In general it holds that when a path is
-//! lent, restrictions are issued for all the owning prefixes of that
-//! path. In this case, the path `*x` owns the path `(*x).f` and,
-//! because `x` is an owned pointer, the path `x` owns the path `*x`.
-//! Therefore, borrowing `(*x).f` yields restrictions on both
-//! `*x` and `x`.
-//!
-//! ### Checking for illegal assignments, moves, and reborrows
-//!
-//! Once we have computed the loans introduced by each borrow, the borrow
-//! checker uses a data flow propagation to compute the full set of loans
-//! in scope at each expression and then uses that set to decide whether
-//! that expression is legal.  Remember that the scope of loan is defined
-//! by its lifetime LT.  We sometimes say that a loan which is in-scope at
-//! a particular point is an "outstanding loan", and the set of
-//! restrictions included in those loans as the "outstanding
-//! restrictions".
-//!
-//! The kinds of expressions which in-scope loans can render illegal are:
-//! - *assignments* (`lv = v`): illegal if there is an in-scope restriction
-//!   against mutating `lv`;
-//! - *moves*: illegal if there is any in-scope restriction on `lv` at all;
-//! - *mutable borrows* (`&mut lv`): illegal there is an in-scope restriction
-//!   against claiming `lv`;
-//! - *immutable borrows* (`&lv`): illegal there is an in-scope restriction
-//!   against freezing `lv`.
-//!
-//! ## Formal rules
-//!
-//! Now that we hopefully have some kind of intuitive feeling for how the
-//! borrow checker works, let's look a bit more closely now at the precise
-//! conditions that it uses. For simplicity I will ignore const loans.
-//!
-//! I will present the rules in a modified form of standard inference
-//! rules, which looks as follows:
-//!
-//! ```text
-//! PREDICATE(X, Y, Z)                  // Rule-Name
-//!   Condition 1
-//!   Condition 2
-//!   Condition 3
-//! ```
-//!
-//! The initial line states the predicate that is to be satisfied.  The
-//! indented lines indicate the conditions that must be met for the
-//! predicate to be satisfied. The right-justified comment states the name
-//! of this rule: there are comments in the borrowck source referencing
-//! these names, so that you can cross reference to find the actual code
-//! that corresponds to the formal rule.
-//!
-//! ### Invariants
-//!
-//! I want to collect, at a high-level, the invariants the borrow checker
-//! maintains. I will give them names and refer to them throughout the
-//! text. Together these invariants are crucial for the overall soundness
-//! of the system.
-//!
-//! **Mutability requires uniqueness.** To mutate a path
-//!
-//! **Unique mutability.** There is only one *usable* mutable path to any
-//! given memory at any given time. This implies that when claiming memory
-//! with an expression like `p = &mut x`, the compiler must guarantee that
-//! the borrowed value `x` can no longer be mutated so long as `p` is
-//! live. (This is done via restrictions, read on.)
-//!
-//! **.**
-//!
-//!
-//! ### The `gather_loans` pass
-//!
-//! We start with the `gather_loans` pass, which walks the AST looking for
-//! borrows.  For each borrow, there are three bits of information: the
-//! lvalue `LV` being borrowed and the mutability `MQ` and lifetime `LT`
-//! of the resulting pointer. Given those, `gather_loans` applies four
-//! validity tests:
-//!
-//! 1. `MUTABILITY(LV, MQ)`: The mutability of the reference is
-//! compatible with the mutability of `LV` (i.e., not borrowing immutable
-//! data as mutable).
-//!
-//! 2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is
-//! compatible with the aliasability of `LV`. The goal is to prevent
-//! `&mut` borrows of aliasability data.
-//!
-//! 3. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
-//! the lifetime of the value being borrowed.
-//!
-//! 4. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
-//! restrictions to maintain memory safety. These are the restrictions
-//! that will go into the final loan. We'll discuss in more detail below.
-//!
-//! ## Checking mutability
-//!
-//! Checking mutability is fairly straightforward. We just want to prevent
-//! immutable data from being borrowed as mutable. Note that it is ok to
-//! borrow mutable data as immutable, since that is simply a
-//! freeze. Formally we define a predicate `MUTABLE(LV, MQ)` which, if
-//! defined, means that "borrowing `LV` with mutability `MQ` is ok. The
-//! Rust code corresponding to this predicate is the function
-//! `check_mutability` in `middle::borrowck::gather_loans`.
-//!
-//! ### Checking mutability of variables
-//!
-//! *Code pointer:* Function `check_mutability()` in `gather_loans/mod.rs`,
-//! but also the code in `mem_categorization`.
-//!
-//! Let's begin with the rules for variables, which state that if a
-//! variable is declared as mutable, it may be borrowed any which way, but
-//! otherwise the variable must be borrowed as immutable or const:
-//!
-//! ```text
-//! MUTABILITY(X, MQ)                   // M-Var-Mut
-//!   DECL(X) = mut
-//!
-//! MUTABILITY(X, MQ)                   // M-Var-Imm
-//!   DECL(X) = imm
-//!   MQ = imm | const
-//! ```
-//!
-//! ### Checking mutability of owned content
-//!
-//! Fields and owned pointers inherit their mutability from
-//! their base expressions, so both of their rules basically
-//! delegate the check to the base expression `LV`:
-//!
-//! ```text
-//! MUTABILITY(LV.f, MQ)                // M-Field
-//!   MUTABILITY(LV, MQ)
-//!
-//! MUTABILITY(*LV, MQ)                 // M-Deref-Unique
-//!   TYPE(LV) = Box<Ty>
-//!   MUTABILITY(LV, MQ)
-//! ```
-//!
-//! ### Checking mutability of immutable pointer types
-//!
-//! Immutable pointer types like `&T` can only
-//! be borrowed if MQ is immutable or const:
-//!
-//! ```text
-//! MUTABILITY(*LV, MQ)                // M-Deref-Borrowed-Imm
-//!   TYPE(LV) = &Ty
-//!   MQ == imm | const
-//! ```
-//!
-//! ### Checking mutability of mutable pointer types
-//!
-//! `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
-//!
-//! ```text
-//! MUTABILITY(*LV, MQ)                 // M-Deref-Borrowed-Mut
-//!   TYPE(LV) = &mut Ty
-//! ```
-//!
-//! ## Checking aliasability
-//!
-//! The goal of the aliasability check is to ensure that we never permit
-//! `&mut` borrows of aliasable data. Formally we define a predicate
-//! `ALIASABLE(LV, MQ)` which if defined means that
-//! "borrowing `LV` with mutability `MQ` is ok". The
-//! Rust code corresponding to this predicate is the function
-//! `check_aliasability()` in `middle::borrowck::gather_loans`.
-//!
-//! ### Checking aliasability of variables
-//!
-//! Local variables are never aliasable as they are accessible only within
-//! the stack frame.
-//!
-//! ```text
-//!     ALIASABLE(X, MQ)                   // M-Var-Mut
-//! ```
-//!
-//! ### Checking aliasable of owned content
-//!
-//! Owned content is aliasable if it is found in an aliasable location:
-//!
-//! ```text
-//! ALIASABLE(LV.f, MQ)                // M-Field
-//!   ALIASABLE(LV, MQ)
-//!
-//! ALIASABLE(*LV, MQ)                 // M-Deref-Unique
-//!   ALIASABLE(LV, MQ)
-//! ```
-//!
-//! ### Checking mutability of immutable pointer types
-//!
-//! Immutable pointer types like `&T` are aliasable, and hence can only be
-//! borrowed immutably:
-//!
-//! ```text
-//! ALIASABLE(*LV, imm)                // M-Deref-Borrowed-Imm
-//!   TYPE(LV) = &Ty
-//! ```
-//!
-//! ### Checking mutability of mutable pointer types
-//!
-//! `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
-//!
-//! ```text
-//! ALIASABLE(*LV, MQ)                 // M-Deref-Borrowed-Mut
-//!   TYPE(LV) = &mut Ty
-//! ```
-//!
-//! ## Checking lifetime
-//!
-//! These rules aim to ensure that no data is borrowed for a scope that exceeds
-//! its lifetime. These two computations wind up being intimately related.
-//! Formally, we define a predicate `LIFETIME(LV, LT, MQ)`, which states that
-//! "the lvalue `LV` can be safely borrowed for the lifetime `LT` with mutability
-//! `MQ`". The Rust code corresponding to this predicate is the module
-//! `middle::borrowck::gather_loans::lifetime`.
-//!
-//! ### The Scope function
-//!
-//! Several of the rules refer to a helper function `SCOPE(LV)=LT`.  The
-//! `SCOPE(LV)` yields the lifetime `LT` for which the lvalue `LV` is
-//! guaranteed to exist, presuming that no mutations occur.
-//!
-//! The scope of a local variable is the block where it is declared:
-//!
-//! ```text
-//!   SCOPE(X) = block where X is declared
-//! ```
-//!
-//! The scope of a field is the scope of the struct:
-//!
-//! ```text
-//!   SCOPE(LV.f) = SCOPE(LV)
-//! ```
-//!
-//! The scope of a unique referent is the scope of the pointer, since
-//! (barring mutation or moves) the pointer will not be freed until
-//! the pointer itself `LV` goes out of scope:
-//!
-//! ```text
-//!   SCOPE(*LV) = SCOPE(LV) if LV has type Box<T>
-//! ```
-//!
-//! The scope of a borrowed referent is the scope associated with the
-//! pointer.  This is a conservative approximation, since the data that
-//! the pointer points at may actually live longer:
-//!
-//! ```text
-//!   SCOPE(*LV) = LT if LV has type &'LT T or &'LT mut T
-//! ```
-//!
-//! ### Checking lifetime of variables
-//!
-//! The rule for variables states that a variable can only be borrowed a
-//! lifetime `LT` that is a subregion of the variable's scope:
-//!
-//! ```text
-//! LIFETIME(X, LT, MQ)                 // L-Local
-//!   LT <= SCOPE(X)
-//! ```
-//!
-//! ### Checking lifetime for owned content
-//!
-//! The lifetime of a field or owned pointer is the same as the lifetime
-//! of its owner:
-//!
-//! ```text
-//! LIFETIME(LV.f, LT, MQ)              // L-Field
-//!   LIFETIME(LV, LT, MQ)
-//!
-//! LIFETIME(*LV, LT, MQ)               // L-Deref-Send
-//!   TYPE(LV) = Box<Ty>
-//!   LIFETIME(LV, LT, MQ)
-//! ```
-//!
-//! ### Checking lifetime for derefs of references
-//!
-//! References have a lifetime `LT'` associated with them.  The
-//! data they point at has been guaranteed to be valid for at least this
-//! lifetime. Therefore, the borrow is valid so long as the lifetime `LT`
-//! of the borrow is shorter than the lifetime `LT'` of the pointer
-//! itself:
-//!
-//! ```text
-//! LIFETIME(*LV, LT, MQ)               // L-Deref-Borrowed
-//!   TYPE(LV) = &LT' Ty OR &LT' mut Ty
-//!   LT <= LT'
-//! ```
-//!
-//! ## Computing the restrictions
-//!
-//! The final rules govern the computation of *restrictions*, meaning that
-//! we compute the set of actions that will be illegal for the life of the
-//! loan. The predicate is written `RESTRICTIONS(LV, LT, ACTIONS) =
-//! RESTRICTION*`, which can be read "in order to prevent `ACTIONS` from
-//! occurring on `LV`, the restrictions `RESTRICTION*` must be respected
-//! for the lifetime of the loan".
-//!
-//! Note that there is an initial set of restrictions: these restrictions
-//! are computed based on the kind of borrow:
-//!
-//! ```text
-//! &mut LV =>   RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE)
-//! &LV =>       RESTRICTIONS(LV, LT, MUTATE|CLAIM)
-//! &const LV => RESTRICTIONS(LV, LT, [])
-//! ```
-//!
-//! The reasoning here is that a mutable borrow must be the only writer,
-//! therefore it prevents other writes (`MUTATE`), mutable borrows
-//! (`CLAIM`), and immutable borrows (`FREEZE`). An immutable borrow
-//! permits other immutable borrows but forbids writes and mutable borrows.
-//! Finally, a const borrow just wants to be sure that the value is not
-//! moved out from under it, so no actions are forbidden.
-//!
-//! ### Restrictions for loans of a local variable
-//!
-//! The simplest case is a borrow of a local variable `X`:
-//!
-//! ```text
-//! RESTRICTIONS(X, LT, ACTIONS) = (X, ACTIONS)            // R-Variable
-//! ```
-//!
-//! In such cases we just record the actions that are not permitted.
-//!
-//! ### Restrictions for loans of fields
-//!
-//! Restricting a field is the same as restricting the owner of that
-//! field:
-//!
-//! ```text
-//! RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS)  // R-Field
-//!   RESTRICTIONS(LV, LT, ACTIONS) = RS
-//! ```
-//!
-//! The reasoning here is as follows. If the field must not be mutated,
-//! then you must not mutate the owner of the field either, since that
-//! would indirectly modify the field. Similarly, if the field cannot be
-//! frozen or aliased, we cannot allow the owner to be frozen or aliased,
-//! since doing so indirectly freezes/aliases the field. This is the
-//! origin of inherited mutability.
-//!
-//! ### Restrictions for loans of owned referents
-//!
-//! Because the mutability of owned referents is inherited, restricting an
-//! owned referent is similar to restricting a field, in that it implies
-//! restrictions on the pointer. However, owned pointers have an important
-//! twist: if the owner `LV` is mutated, that causes the owned referent
-//! `*LV` to be freed! So whenever an owned referent `*LV` is borrowed, we
-//! must prevent the owned pointer `LV` from being mutated, which means
-//! that we always add `MUTATE` and `CLAIM` to the restriction set imposed
-//! on `LV`:
-//!
-//! ```text
-//! RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS)    // R-Deref-Send-Pointer
-//!   TYPE(LV) = Box<Ty>
-//!   RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS
-//! ```
-//!
-//! ### Restrictions for loans of immutable borrowed referents
-//!
-//! Immutable borrowed referents are freely aliasable, meaning that
-//! the compiler does not prevent you from copying the pointer.  This
-//! implies that issuing restrictions is useless. We might prevent the
-//! user from acting on `*LV` itself, but there could be another path
-//! `*LV1` that refers to the exact same memory, and we would not be
-//! restricting that path. Therefore, the rule for `&Ty` pointers
-//! always returns an empty set of restrictions, and it only permits
-//! restricting `MUTATE` and `CLAIM` actions:
-//!
-//! ```text
-//! RESTRICTIONS(*LV, LT, ACTIONS) = []                    // R-Deref-Imm-Borrowed
-//!   TYPE(LV) = &LT' Ty
-//!   LT <= LT'                                            // (1)
-//!   ACTIONS subset of [MUTATE, CLAIM]
-//! ```
-//!
-//! The reason that we can restrict `MUTATE` and `CLAIM` actions even
-//! without a restrictions list is that it is never legal to mutate nor to
-//! borrow mutably the contents of a `&Ty` pointer. In other words,
-//! those restrictions are already inherent in the type.
-//!
-//! Clause (1) in the rule for `&Ty` deserves mention. Here I
-//! specify that the lifetime of the loan must be less than the lifetime
-//! of the `&Ty` pointer. In simple cases, this clause is redundant, since
-//! the `LIFETIME()` function will already enforce the required rule:
-//!
-//! ```
-//! fn foo(point: &'a Point) -> &'static f32 {
-//!     &point.x // Error
-//! }
-//! ```
-//!
-//! The above example fails to compile both because of clause (1) above
-//! but also by the basic `LIFETIME()` check. However, in more advanced
-//! examples involving multiple nested pointers, clause (1) is needed:
-//!
-//! ```
-//! fn foo(point: &'a &'b mut Point) -> &'b f32 {
-//!     &point.x // Error
-//! }
-//! ```
-//!
-//! The `LIFETIME` rule here would accept `'b` because, in fact, the
-//! *memory is* guaranteed to remain valid (i.e., not be freed) for the
-//! lifetime `'b`, since the `&mut` pointer is valid for `'b`. However, we
-//! are returning an immutable reference, so we need the memory to be both
-//! valid and immutable. Even though `point.x` is referenced by an `&mut`
-//! pointer, it can still be considered immutable so long as that `&mut`
-//! pointer is found in an aliased location. That means the memory is
-//! guaranteed to be *immutable* for the lifetime of the `&` pointer,
-//! which is only `'a`, not `'b`. Hence this example yields an error.
-//!
-//! As a final twist, consider the case of two nested *immutable*
-//! pointers, rather than a mutable pointer within an immutable one:
-//!
-//! ```
-//! fn foo(point: &'a &'b Point) -> &'b f32 {
-//!     &point.x // OK
-//! }
-//! ```
-//!
-//! This function is legal. The reason for this is that the inner pointer
-//! (`*point : &'b Point`) is enough to guarantee the memory is immutable
-//! and valid for the lifetime `'b`.  This is reflected in
-//! `RESTRICTIONS()` by the fact that we do not recurse (i.e., we impose
-//! no restrictions on `LV`, which in this particular case is the pointer
-//! `point : &'a &'b Point`).
-//!
-//! #### Why both `LIFETIME()` and `RESTRICTIONS()`?
-//!
-//! Given the previous text, it might seem that `LIFETIME` and
-//! `RESTRICTIONS` should be folded together into one check, but there is
-//! a reason that they are separated. They answer separate concerns.
-//! The rules pertaining to `LIFETIME` exist to ensure that we don't
-//! create a borrowed pointer that outlives the memory it points at. So
-//! `LIFETIME` prevents a function like this:
-//!
-//! ```
-//! fn get_1<'a>() -> &'a int {
-//!     let x = 1;
-//!     &x
-//! }
-//! ```
-//!
-//! Here we would be returning a pointer into the stack. Clearly bad.
-//!
-//! However, the `RESTRICTIONS` rules are more concerned with how memory
-//! is used. The example above doesn't generate an error according to
-//! `RESTRICTIONS` because, for local variables, we don't require that the
-//! loan lifetime be a subset of the local variable lifetime. The idea
-//! here is that we *can* guarantee that `x` is not (e.g.) mutated for the
-//! lifetime `'a`, even though `'a` exceeds the function body and thus
-//! involves unknown code in the caller -- after all, `x` ceases to exist
-//! after we return and hence the remaining code in `'a` cannot possibly
-//! mutate it. This distinction is important for type checking functions
-//! like this one:
-//!
-//! ```
-//! fn inc_and_get<'a>(p: &'a mut Point) -> &'a int {
-//!     p.x += 1;
-//!     &p.x
-//! }
-//! ```
-//!
-//! In this case, we take in a `&mut` and return a frozen borrowed pointer
-//! with the same lifetime. So long as the lifetime of the returned value
-//! doesn't exceed the lifetime of the `&mut` we receive as input, this is
-//! fine, though it may seem surprising at first (it surprised me when I
-//! first worked it through). After all, we're guaranteeing that `*p`
-//! won't be mutated for the lifetime `'a`, even though we can't "see" the
-//! entirety of the code during that lifetime, since some of it occurs in
-//! our caller. But we *do* know that nobody can mutate `*p` except
-//! through `p`. So if we don't mutate `*p` and we don't return `p`, then
-//! we know that the right to mutate `*p` has been lost to our caller --
-//! in terms of capability, the caller passed in the ability to mutate
-//! `*p`, and we never gave it back. (Note that we can't return `p` while
-//! `*p` is borrowed since that would be a move of `p`, as `&mut` pointers
-//! are affine.)
-//!
-//! ### Restrictions for loans of const aliasable referents
-//!
-//! Freeze pointers are read-only. There may be `&mut` or `&` aliases, and
-//! we can not prevent *anything* but moves in that case. So the
-//! `RESTRICTIONS` function is only defined if `ACTIONS` is the empty set.
-//! Because moves from a `&const` lvalue are never legal, it is not
-//! necessary to add any restrictions at all to the final result.
-//!
-//! ```text
-//!     RESTRICTIONS(*LV, LT, []) = []                         // R-Deref-Freeze-Borrowed
-//!       TYPE(LV) = &const Ty
-//! ```
-//!
-//! ### Restrictions for loans of mutable borrowed referents
-//!
-//! Mutable borrowed pointers are guaranteed to be the only way to mutate
-//! their referent. This permits us to take greater license with them; for
-//! example, the referent can be frozen simply be ensuring that we do not
-//! use the original pointer to perform mutate. Similarly, we can allow
-//! the referent to be claimed, so long as the original pointer is unused
-//! while the new claimant is live.
-//!
-//! The rule for mutable borrowed pointers is as follows:
-//!
-//! ```text
-//! RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS)    // R-Deref-Mut-Borrowed
-//!   TYPE(LV) = &LT' mut Ty
-//!   LT <= LT'                                            // (1)
-//!   RESTRICTIONS(LV, LT, ACTIONS) = RS                   // (2)
-//! ```
-//!
-//! Let's examine the two numbered clauses:
-//!
-//! Clause (1) specifies that the lifetime of the loan (`LT`) cannot
-//! exceed the lifetime of the `&mut` pointer (`LT'`). The reason for this
-//! is that the `&mut` pointer is guaranteed to be the only legal way to
-//! mutate its referent -- but only for the lifetime `LT'`.  After that
-//! lifetime, the loan on the referent expires and hence the data may be
-//! modified by its owner again. This implies that we are only able to
-//! guarantee that the referent will not be modified or aliased for a
-//! maximum of `LT'`.
-//!
-//! Here is a concrete example of a bug this rule prevents:
-//!
-//! ```
-//! // Test region-reborrow-from-shorter-mut-ref.rs:
-//! fn copy_pointer<'a,'b,T>(x: &'a mut &'b mut T) -> &'b mut T {
-//!     &mut **p // ERROR due to clause (1)
-//! }
-//! fn main() {
-//!     let mut x = 1;
-//!     let mut y = &mut x; // <-'b-----------------------------+
-//!     //      +-'a--------------------+                       |
-//!     //      v                       v                       |
-//!     let z = copy_borrowed_ptr(&mut y); // y is lent         |
-//!     *y += 1; // Here y==z, so both should not be usable...  |
-//!     *z += 1; // ...and yet they would be, but for clause 1. |
-//! } // <------------------------------------------------------+
-//! ```
-//!
-//! Clause (2) propagates the restrictions on the referent to the pointer
-//! itself. This is the same as with an owned pointer, though the
-//! reasoning is mildly different. The basic goal in all cases is to
-//! prevent the user from establishing another route to the same data. To
-//! see what I mean, let's examine various cases of what can go wrong and
-//! show how it is prevented.
-//!
-//! **Example danger 1: Moving the base pointer.** One of the simplest
-//! ways to violate the rules is to move the base pointer to a new name
-//! and access it via that new name, thus bypassing the restrictions on
-//! the old name. Here is an example:
-//!
-//! ```
-//! // src/test/compile-fail/borrowck-move-mut-base-ptr.rs
-//! fn foo(t0: &mut int) {
-//!     let p: &int = &*t0; // Freezes `*t0`
-//!     let t1 = t0;        //~ ERROR cannot move out of `t0`
-//!     *t1 = 22;           // OK, not a write through `*t0`
-//! }
-//! ```
-//!
-//! Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a
-//! move of `t0` -- or would be, if it were legal. Instead, we get an
-//! error, because clause (2) imposes restrictions on `LV` (`t0`, here),
-//! and any restrictions on a path make it impossible to move from that
-//! path.
-//!
-//! **Example danger 2: Claiming the base pointer.** Another possible
-//! danger is to mutably borrow the base path. This can lead to two bad
-//! scenarios. The most obvious is that the mutable borrow itself becomes
-//! another path to access the same data, as shown here:
-//!
-//! ```
-//! // src/test/compile-fail/borrowck-mut-borrow-of-mut-base-ptr.rs
-//! fn foo<'a>(mut t0: &'a mut int,
-//!            mut t1: &'a mut int) {
-//!     let p: &int = &*t0;     // Freezes `*t0`
-//!     let mut t2 = &mut t0;   //~ ERROR cannot borrow `t0`
-//!     **t2 += 1;              // Mutates `*t0`
-//! }
-//! ```
-//!
-//! In this example, `**t2` is the same memory as `*t0`. Because `t2` is
-//! an `&mut` pointer, `**t2` is a unique path and hence it would be
-//! possible to mutate `**t2` even though that memory was supposed to be
-//! frozen by the creation of `p`. However, an error is reported -- the
-//! reason is that the freeze `&*t0` will restrict claims and mutation
-//! against `*t0` which, by clause 2, in turn prevents claims and mutation
-//! of `t0`. Hence the claim `&mut t0` is illegal.
-//!
-//! Another danger with an `&mut` pointer is that we could swap the `t0`
-//! value away to create a new path:
-//!
-//! ```
-//! // src/test/compile-fail/borrowck-swap-mut-base-ptr.rs
-//! fn foo<'a>(mut t0: &'a mut int,
-//!            mut t1: &'a mut int) {
-//!     let p: &int = &*t0;     // Freezes `*t0`
-//!     swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0`
-//!     *t1 = 22;
-//! }
-//! ```
-//!
-//! This is illegal for the same reason as above. Note that if we added
-//! back a swap operator -- as we used to have -- we would want to be very
-//! careful to ensure this example is still illegal.
-//!
-//! **Example danger 3: Freeze the base pointer.** In the case where the
-//! referent is claimed, even freezing the base pointer can be dangerous,
-//! as shown in the following example:
-//!
-//! ```
-//! // src/test/compile-fail/borrowck-borrow-of-mut-base-ptr.rs
-//! fn foo<'a>(mut t0: &'a mut int,
-//!            mut t1: &'a mut int) {
-//!     let p: &mut int = &mut *t0; // Claims `*t0`
-//!     let mut t2 = &t0;           //~ ERROR cannot borrow `t0`
-//!     let q: &int = &*t2;         // Freezes `*t0` but not through `*p`
-//!     *p += 1;                    // violates type of `*q`
-//! }
-//! ```
-//!
-//! Here the problem is that `*t0` is claimed by `p`, and hence `p` wants
-//! to be the controlling pointer through which mutation or freezes occur.
-//! But `t2` would -- if it were legal -- have the type `& &mut int`, and
-//! hence would be a mutable pointer in an aliasable location, which is
-//! considered frozen (since no one can write to `**t2` as it is not a
-//! unique path). Therefore, we could reasonably create a frozen `&int`
-//! pointer pointing at `*t0` that coexists with the mutable pointer `p`,
-//! which is clearly unsound.
-//!
-//! However, it is not always unsafe to freeze the base pointer. In
-//! particular, if the referent is frozen, there is no harm in it:
-//!
-//! ```
-//! // src/test/run-pass/borrowck-borrow-of-mut-base-ptr-safe.rs
-//! fn foo<'a>(mut t0: &'a mut int,
-//!            mut t1: &'a mut int) {
-//!     let p: &int = &*t0; // Freezes `*t0`
-//!     let mut t2 = &t0;
-//!     let q: &int = &*t2; // Freezes `*t0`, but that's ok...
-//!     let r: &int = &*t0; // ...after all, could do same thing directly.
-//! }
-//! ```
-//!
-//! In this case, creating the alias `t2` of `t0` is safe because the only
-//! thing `t2` can be used for is to further freeze `*t0`, which is
-//! already frozen. In particular, we cannot assign to `*t0` through the
-//! new alias `t2`, as demonstrated in this test case:
-//!
-//! ```
-//! // src/test/run-pass/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs
-//! fn foo(t0: & &mut int) {
-//!     let t1 = t0;
-//!     let p: &int = &**t0;
-//!     **t1 = 22; //~ ERROR cannot assign
-//! }
-//! ```
-//!
-//! This distinction is reflected in the rules. When doing an `&mut`
-//! borrow -- as in the first example -- the set `ACTIONS` will be
-//! `CLAIM|MUTATE|FREEZE`, because claiming the referent implies that it
-//! cannot be claimed, mutated, or frozen by anyone else. These
-//! restrictions are propagated back to the base path and hence the base
-//! path is considered unfreezable.
-//!
-//! In contrast, when the referent is merely frozen -- as in the second
-//! example -- the set `ACTIONS` will be `CLAIM|MUTATE`, because freezing
-//! the referent implies that it cannot be claimed or mutated but permits
-//! others to freeze. Hence when these restrictions are propagated back to
-//! the base path, it will still be considered freezable.
-//!
-//!
-//!
-//! **FIXME #10520: Restrictions against mutating the base pointer.** When
-//! an `&mut` pointer is frozen or claimed, we currently pass along the
-//! restriction against MUTATE to the base pointer. I do not believe this
-//! restriction is needed. It dates from the days when we had a way to
-//! mutate that preserved the value being mutated (i.e., swap). Nowadays
-//! the only form of mutation is assignment, which destroys the pointer
-//! being mutated -- therefore, a mutation cannot create a new path to the
-//! same data. Rather, it removes an existing path. This implies that not
-//! only can we permit mutation, we can have mutation kill restrictions in
-//! the dataflow sense.
-//!
-//! **WARNING:** We do not currently have `const` borrows in the
-//! language. If they are added back in, we must ensure that they are
-//! consistent with all of these examples. The crucial question will be
-//! what sorts of actions are permitted with a `&const &mut` pointer. I
-//! would suggest that an `&mut` referent found in an `&const` location be
-//! prohibited from both freezes and claims. This would avoid the need to
-//! prevent `const` borrows of the base pointer when the referent is
-//! borrowed.
-//!
-//! # Moves and initialization
-//!
-//! The borrow checker is also in charge of ensuring that:
-//!
-//! - all memory which is accessed is initialized
-//! - immutable local variables are assigned at most once.
-//!
-//! These are two separate dataflow analyses built on the same
-//! framework. Let's look at checking that memory is initialized first;
-//! the checking of immutable local variable assignments works in a very
-//! similar way.
-//!
-//! To track the initialization of memory, we actually track all the
-//! points in the program that *create uninitialized memory*, meaning
-//! moves and the declaration of uninitialized variables. For each of
-//! these points, we create a bit in the dataflow set. Assignments to a
-//! variable `x` or path `a.b.c` kill the move/uninitialization bits for
-//! those paths and any subpaths (e.g., `x`, `x.y`, `a.b.c`, `*a.b.c`).
-//! Bits are unioned when two control-flow paths join. Thus, the
-//! presence of a bit indicates that the move may have occurred without an
-//! intervening assignment to the same memory. At each use of a variable,
-//! we examine the bits in scope, and check that none of them are
-//! moves/uninitializations of the variable that is being used.
-//!
-//! Let's look at a simple example:
-//!
-//! ```
-//! fn foo(a: Box<int>) {
-//!     let b: Box<int>;   // Gen bit 0.
-//!
-//!     if cond {          // Bits: 0
-//!         use(&*a);
-//!         b = a;         // Gen bit 1, kill bit 0.
-//!         use(&*b);
-//!     } else {
-//!                        // Bits: 0
-//!     }
-//!                        // Bits: 0,1
-//!     use(&*a);          // Error.
-//!     use(&*b);          // Error.
-//! }
-//!
-//! fn use(a: &int) { }
-//! ```
-//!
-//! In this example, the variable `b` is created uninitialized. In one
-//! branch of an `if`, we then move the variable `a` into `b`. Once we
-//! exit the `if`, therefore, it is an error to use `a` or `b` since both
-//! are only conditionally initialized. I have annotated the dataflow
-//! state using comments. There are two dataflow bits, with bit 0
-//! corresponding to the creation of `b` without an initializer, and bit 1
-//! corresponding to the move of `a`. The assignment `b = a` both
-//! generates bit 1, because it is a move of `a`, and kills bit 0, because
-//! `b` is now initialized. On the else branch, though, `b` is never
-//! initialized, and so bit 0 remains untouched. When the two flows of
-//! control join, we union the bits from both sides, resulting in both
-//! bits 0 and 1 being set. Thus any attempt to use `a` uncovers the bit 1
-//! from the "then" branch, showing that `a` may be moved, and any attempt
-//! to use `b` uncovers bit 0, from the "else" branch, showing that `b`
-//! may not be initialized.
-//!
-//! ## Initialization of immutable variables
-//!
-//! Initialization of immutable variables works in a very similar way,
-//! except that:
-//!
-//! 1. we generate bits for each assignment to a variable;
-//! 2. the bits are never killed except when the variable goes out of scope.
-//!
-//! Thus the presence of an assignment bit indicates that the assignment
-//! may have occurred. Note that assignments are only killed when the
-//! variable goes out of scope, as it is not relevant whether or not there
-//! has been a move in the meantime. Using these bits, we can declare that
-//! an assignment to an immutable variable is legal iff there is no other
-//! assignment bit to that same variable in scope.
-//!
-//! ## Why is the design made this way?
-//!
-//! It may seem surprising that we assign dataflow bits to *each move*
-//! rather than *each path being moved*. This is somewhat less efficient,
-//! since on each use, we must iterate through all moves and check whether
-//! any of them correspond to the path in question. Similar concerns apply
-//! to the analysis for double assignments to immutable variables. The
-//! main reason to do it this way is that it allows us to print better
-//! error messages, because when a use occurs, we can print out the
-//! precise move that may be in scope, rather than simply having to say
-//! "the variable may not be initialized".
-//!
-//! ## Data structures used in the move analysis
-//!
-//! The move analysis maintains several data structures that enable it to
-//! cross-reference moves and assignments to determine when they may be
-//! moving/assigning the same memory. These are all collected into the
-//! `MoveData` and `FlowedMoveData` structs. The former represents the set
-//! of move paths, moves, and assignments, and the latter adds in the
-//! results of a dataflow computation.
-//!
-//! ### Move paths
-//!
-//! The `MovePath` tree tracks every path that is moved or assigned to.
-//! These paths have the same form as the `LoanPath` data structure, which
-//! in turn is the "real world version of the lvalues `LV` that we
-//! introduced earlier. The difference between a `MovePath` and a `LoanPath`
-//! is that move paths are:
-//!
-//! 1. Canonicalized, so that we have exactly one copy of each, and
-//!    we can refer to move paths by index;
-//! 2. Cross-referenced with other paths into a tree, so that given a move
-//!    path we can efficiently find all parent move paths and all
-//!    extensions (e.g., given the `a.b` move path, we can easily find the
-//!    move path `a` and also the move paths `a.b.c`)
-//! 3. Cross-referenced with moves and assignments, so that we can
-//!    easily find all moves and assignments to a given path.
-//!
-//! The mechanism that we use is to create a `MovePath` record for each
-//! move path. These are arranged in an array and are referenced using
-//! `MovePathIndex` values, which are newtype'd indices. The `MovePath`
-//! structs are arranged into a tree, representing using the standard
-//! Knuth representation where each node has a child 'pointer' and a "next
-//! sibling" 'pointer'. In addition, each `MovePath` has a parent
-//! 'pointer'.  In this case, the 'pointers' are just `MovePathIndex`
-//! values.
-//!
-//! In this way, if we want to find all base paths of a given move path,
-//! we can just iterate up the parent pointers (see `each_base_path()` in
-//! the `move_data` module). If we want to find all extensions, we can
-//! iterate through the subtree (see `each_extending_path()`).
-//!
-//! ### Moves and assignments
-//!
-//! There are structs to represent moves (`Move`) and assignments
-//! (`Assignment`), and these are also placed into arrays and referenced
-//! by index. All moves of a particular path are arranged into a linked
-//! lists, beginning with `MovePath.first_move` and continuing through
-//! `Move.next_move`.
-//!
-//! We distinguish between "var" assignments, which are assignments to a
-//! variable like `x = foo`, and "path" assignments (`x.f = foo`).  This
-//! is because we need to assign dataflows to the former, but not the
-//! latter, so as to check for double initialization of immutable
-//! variables.
-//!
-//! ### Gathering and checking moves
-//!
-//! Like loans, we distinguish two phases. The first, gathering, is where
-//! we uncover all the moves and assignments. As with loans, we do some
-//! basic sanity checking in this phase, so we'll report errors if you
-//! attempt to move out of a borrowed pointer etc. Then we do the dataflow
-//! (see `FlowedMoveData::new`). Finally, in the `check_loans.rs` code, we
-//! walk back over, identify all uses, assignments, and captures, and
-//! check that they are legal given the set of dataflow bits we have
-//! computed for that program point.
-//!
-//! # Drop flags and structural fragments
-//!
-//! In addition to the job of enforcing memory safety, the borrow checker
-//! code is also responsible for identifying the *structural fragments* of
-//! data in the function, to support out-of-band dynamic drop flags
-//! allocated on the stack. (For background, see [RFC PR #320].)
-//!
-//! [RFC PR #320]: https://github.com/rust-lang/rfcs/pull/320
-//!
-//! Semantically, each piece of data that has a destructor may need a
-//! boolean flag to indicate whether or not its destructor has been run
-//! yet. However, in many cases there is no need to actually maintain such
-//! a flag: It can be apparent from the code itself that a given path is
-//! always initialized (or always deinitialized) when control reaches the
-//! end of its owner's scope, and thus we can unconditionally emit (or
-//! not) the destructor invocation for that path.
-//!
-//! A simple example of this is the following:
-//!
-//! ```rust
-//! struct D { p: int }
-//! impl D { fn new(x: int) -> D { ... }
-//! impl Drop for D { ... }
-//!
-//! fn foo(a: D, b: D, t: || -> bool) {
-//!     let c: D;
-//!     let d: D;
-//!     if t() { c = b; }
-//! }
-//! ```
-//!
-//! At the end of the body of `foo`, the compiler knows that `a` is
-//! initialized, introducing a drop obligation (deallocating the boxed
-//! integer) for the end of `a`'s scope that is run unconditionally.
-//! Likewise the compiler knows that `d` is not initialized, and thus it
-//! leave out the drop code for `d`.
-//!
-//! The compiler cannot statically know the drop-state of `b` nor `c` at
-//! the end of their scope, since that depends on the value of
-//! `t`. Therefore, we need to insert boolean flags to track whether we
-//! need to drop `b` and `c`.
-//!
-//! However, the matter is not as simple as just mapping local variables
-//! to their corresponding drop flags when necessary. In particular, in
-//! addition to being able to move data out of local variables, Rust
-//! allows one to move values in and out of structured data.
-//!
-//! Consider the following:
-//!
-//! ```rust
-//! struct S { x: D, y: D, z: D }
-//!
-//! fn foo(a: S, mut b: S, t: || -> bool) {
-//!     let mut c: S;
-//!     let d: S;
-//!     let e: S = a.clone();
-//!     if t() {
-//!         c = b;
-//!         b.x = e.y;
-//!     }
-//!     if t() { c.y = D::new(4); }
-//! }
-//! ```
-//!
-//! As before, the drop obligations of `a` and `d` can be statically
-//! determined, and again the state of `b` and `c` depend on dynamic
-//! state. But additionally, the dynamic drop obligations introduced by
-//! `b` and `c` are not just per-local boolean flags. For example, if the
-//! first call to `t` returns `false` and the second call `true`, then at
-//! the end of their scope, `b` will be completely initialized, but only
-//! `c.y` in `c` will be initialized.  If both calls to `t` return `true`,
-//! then at the end of their scope, `c` will be completely initialized,
-//! but only `b.x` will be initialized in `b`, and only `e.x` and `e.z`
-//! will be initialized in `e`.
-//!
-//! Note that we need to cover the `z` field in each case in some way,
-//! since it may (or may not) need to be dropped, even though `z` is never
-//! directly mentioned in the body of the `foo` function. We call a path
-//! like `b.z` a *fragment sibling* of `b.x`, since the field `z` comes
-//! from the same structure `S` that declared the field `x` in `b.x`.
-//!
-//! In general we need to maintain boolean flags that match the
-//! `S`-structure of both `b` and `c`.  In addition, we need to consult
-//! such a flag when doing an assignment (such as `c.y = D::new(4);`
-//! above), in order to know whether or not there is a previous value that
-//! needs to be dropped before we do the assignment.
-//!
-//! So for any given function, we need to determine what flags are needed
-//! to track its drop obligations. Our strategy for determining the set of
-//! flags is to represent the fragmentation of the structure explicitly:
-//! by starting initially from the paths that are explicitly mentioned in
-//! moves and assignments (such as `b.x` and `c.y` above), and then
-//! traversing the structure of the path's type to identify leftover
-//! *unmoved fragments*: assigning into `c.y` means that `c.x` and `c.z`
-//! are leftover unmoved fragments. Each fragment represents a drop
-//! obligation that may need to be tracked. Paths that are only moved or
-//! assigned in their entirety (like `a` and `d`) are treated as a single
-//! drop obligation.
-//!
-//! The fragment construction process works by piggy-backing on the
-//! existing `move_data` module. We already have callbacks that visit each
-//! direct move and assignment; these form the basis for the sets of
-//! moved_leaf_paths and assigned_leaf_paths. From these leaves, we can
-//! walk up their parent chain to identify all of their parent paths.
-//! We need to identify the parents because of cases like the following:
-//!
-//! ```rust
-//! struct Pair<X,Y>{ x: X, y: Y }
-//! fn foo(dd_d_d: Pair<Pair<Pair<D, D>, D>, D>) {
-//!     other_function(dd_d_d.x.y);
-//! }
-//! ```
-//!
-//! In this code, the move of the path `dd_d.x.y` leaves behind not only
-//! the fragment drop-obligation `dd_d.x.x` but also `dd_d.y` as well.
-//!
-//! Once we have identified the directly-referenced leaves and their
-//! parents, we compute the left-over fragments, in the function
-//! `fragments::add_fragment_siblings`. As of this writing this works by
-//! looking at each directly-moved or assigned path P, and blindly
-//! gathering all sibling fields of P (as well as siblings for the parents
-//! of P, etc). After accumulating all such siblings, we filter out the
-//! entries added as siblings of P that turned out to be
-//! directly-referenced paths (or parents of directly referenced paths)
-//! themselves, thus leaving the never-referenced "left-overs" as the only
-//! thing left from the gathering step.
-//!
-//! ## Array structural fragments
-//!
-//! A special case of the structural fragments discussed above are
-//! the elements of an array that has been passed by value, such as
-//! the following:
-//!
-//! ```rust
-//! fn foo(a: [D, ..10], i: uint) -> D {
-//!     a[i]
-//! }
-//! ```
-//!
-//! The above code moves a single element out of the input array `a`.
-//! The remainder of the array still needs to be dropped; i.e., it
-//! is a structural fragment. Note that after performing such a move,
-//! it is not legal to read from the array `a`. There are a number of
-//! ways to deal with this, but the important thing to note is that
-//! the semantics needs to distinguish in some manner between a
-//! fragment that is the *entire* array versus a fragment that represents
-//! all-but-one element of the array.  A place where that distinction
-//! would arise is the following:
-//!
-//! ```rust
-//! fn foo(a: [D, ..10], b: [D, ..10], i: uint, t: bool) -> D {
-//!     if t {
-//!         a[i]
-//!     } else {
-//!         b[i]
-//!     }
-//!
-//!     // When control exits, we will need either to drop all of `a`
-//!     // and all-but-one of `b`, or to drop all of `b` and all-but-one
-//!     // of `a`.
-//! }
-//! ```
-//!
-//! There are a number of ways that the trans backend could choose to
-//! compile this (e.g. a `[bool, ..10]` array for each such moved array;
-//! or an `Option<uint>` for each moved array).  From the viewpoint of the
-//! borrow-checker, the important thing is to record what kind of fragment
-//! is implied by the relevant moves.
-//!
-//! # Future work
-//!
-//! While writing up these docs, I encountered some rules I believe to be
-//! stricter than necessary:
-//!
-//! - I think restricting the `&mut` LV against moves and `ALIAS` is sufficient,
-//!   `MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was
-//!   a built-in operator, but as it is not, it is implied by `CLAIM`,
-//!   and `CLAIM` is implied by `ALIAS`. The only net effect of this is an
-//!   extra error message in some cases, though.
-//! - I have not described how closures interact. Current code is unsound.
-//!   I am working on describing and implementing the fix.
-//! - If we wish, we can easily extend the move checking to allow finer-grained
-//!   tracking of what is initialized and what is not, enabling code like
-//!   this:
-//!
-//!       a = x.f.g; // x.f.g is now uninitialized
-//!       // here, x and x.f are not usable, but x.f.h *is*
-//!       x.f.g = b; // x.f.g is not initialized
-//!       // now x, x.f, x.f.g, x.f.h are all usable
-//!
-//!   What needs to change here, most likely, is that the `moves` module
-//!   should record not only what paths are moved, but what expressions
-//!   are actual *uses*. For example, the reference to `x` in `x.f.g = b`
-//!   is not a true *use* in the sense that it requires `x` to be fully
-//!   initialized. This is in fact why the above code produces an error
-//!   today: the reference to `x` in `x.f.g = b` is considered illegal
-//!   because `x` is not fully initialized.
-//!
-//! There are also some possible refactorings:
-//!
-//! - It might be nice to replace all loan paths with the MovePath mechanism,
-//!   since they allow lightweight comparison using an integer.
diff --git a/src/librustc/middle/borrowck/fragments.rs b/src/librustc/middle/borrowck/fragments.rs
deleted file mode 100644 (file)
index 056d4f9..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Helper routines used for fragmenting structural paths due to moves for
-//! tracking drop obligations. Please see the extensive comments in the
-//! section "Structural fragments" in `doc.rs`.
-
-use self::Fragment::*;
-
-use session::config;
-use middle::borrowck::{LoanPath};
-use middle::borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
-use middle::borrowck::LoanPathElem::{LpDeref, LpInterior};
-use middle::borrowck::move_data::{InvalidMovePathIndex};
-use middle::borrowck::move_data::{MoveData, MovePathIndex};
-use middle::ty;
-use middle::mem_categorization as mc;
-use util::ppaux::{Repr, UserString};
-
-use std::mem;
-use std::rc::Rc;
-use std::slice;
-use syntax::ast;
-use syntax::ast_map;
-use syntax::attr::AttrMetaMethods;
-use syntax::codemap::Span;
-
-#[deriving(PartialEq, Eq, PartialOrd, Ord)]
-enum Fragment {
-    // This represents the path described by the move path index
-    Just(MovePathIndex),
-
-    // This represents the collection of all but one of the elements
-    // from an array at the path described by the move path index.
-    // Note that attached MovePathIndex should have mem_categorization
-    // of InteriorElement (i.e. array dereference `[]`).
-    AllButOneFrom(MovePathIndex),
-}
-
-impl Fragment {
-    fn loan_path_repr<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
-        let repr = |mpi| move_data.path_loan_path(mpi).repr(tcx);
-        match *self {
-            Just(mpi) => repr(mpi),
-            AllButOneFrom(mpi) => format!("$(allbutone {})", repr(mpi)),
-        }
-    }
-
-    fn loan_path_user_string<'tcx>(&self,
-                                   move_data: &MoveData<'tcx>,
-                                   tcx: &ty::ctxt<'tcx>) -> String {
-        let user_string = |mpi| move_data.path_loan_path(mpi).user_string(tcx);
-        match *self {
-            Just(mpi) => user_string(mpi),
-            AllButOneFrom(mpi) => format!("$(allbutone {})", user_string(mpi)),
-        }
-    }
-}
-
-pub struct FragmentSets {
-    /// During move_data construction, `moved_leaf_paths` tracks paths
-    /// that have been used directly by being moved out of.  When
-    /// move_data construction has been completed, `moved_leaf_paths`
-    /// tracks such paths that are *leaf fragments* (e.g. `a.j` if we
-    /// never move out any child like `a.j.x`); any parent paths
-    /// (e.g. `a` for the `a.j` example) are moved over to
-    /// `parents_of_fragments`.
-    moved_leaf_paths: Vec<MovePathIndex>,
-
-    /// `assigned_leaf_paths` tracks paths that have been used
-    /// directly by being overwritten, but is otherwise much like
-    /// `moved_leaf_paths`.
-    assigned_leaf_paths: Vec<MovePathIndex>,
-
-    /// `parents_of_fragments` tracks paths that are definitely
-    /// parents of paths that have been moved.
-    ///
-    /// FIXME(pnkfelix) probably do not want/need
-    /// `parents_of_fragments` at all, if we can avoid it.
-    ///
-    /// Update: I do not see a way to to avoid it.  Maybe just remove
-    /// above fixme, or at least document why doing this may be hard.
-    parents_of_fragments: Vec<MovePathIndex>,
-
-    /// During move_data construction (specifically the
-    /// fixup_fragment_sets call), `unmoved_fragments` tracks paths
-    /// that have been "left behind" after a sibling has been moved or
-    /// assigned.  When move_data construction has been completed,
-    /// `unmoved_fragments` tracks paths that were *only* results of
-    /// being left-behind, and never directly moved themselves.
-    unmoved_fragments: Vec<Fragment>,
-}
-
-impl FragmentSets {
-    pub fn new() -> FragmentSets {
-        FragmentSets {
-            unmoved_fragments: Vec::new(),
-            moved_leaf_paths: Vec::new(),
-            assigned_leaf_paths: Vec::new(),
-            parents_of_fragments: Vec::new(),
-        }
-    }
-
-    pub fn add_move(&mut self, path_index: MovePathIndex) {
-        self.moved_leaf_paths.push(path_index);
-    }
-
-    pub fn add_assignment(&mut self, path_index: MovePathIndex) {
-        self.assigned_leaf_paths.push(path_index);
-    }
-}
-
-pub fn instrument_move_fragments<'tcx>(this: &MoveData<'tcx>,
-                                       tcx: &ty::ctxt<'tcx>,
-                                       sp: Span,
-                                       id: ast::NodeId) {
-    let (span_err, print) = {
-        let attrs : &[ast::Attribute];
-        attrs = match tcx.map.find(id) {
-            Some(ast_map::NodeItem(ref item)) =>
-                item.attrs.as_slice(),
-            Some(ast_map::NodeImplItem(&ast::MethodImplItem(ref m))) =>
-                m.attrs.as_slice(),
-            Some(ast_map::NodeTraitItem(&ast::ProvidedMethod(ref m))) =>
-                m.attrs.as_slice(),
-            _ => [].as_slice(),
-        };
-
-        let span_err =
-            attrs.iter().any(|a| a.check_name("rustc_move_fragments"));
-        let print = tcx.sess.debugging_opt(config::PRINT_MOVE_FRAGMENTS);
-
-        (span_err, print)
-    };
-
-    if !span_err && !print { return; }
-
-    let instrument_all_paths = |kind, vec_rc: &Vec<MovePathIndex>| {
-        for (i, mpi) in vec_rc.iter().enumerate() {
-            let render = || this.path_loan_path(*mpi).user_string(tcx);
-            if span_err {
-                tcx.sess.span_err(sp, format!("{}: `{}`", kind, render()).as_slice());
-            }
-            if print {
-                println!("id:{} {}[{}] `{}`", id, kind, i, render());
-            }
-        }
-    };
-
-    let instrument_all_fragments = |kind, vec_rc: &Vec<Fragment>| {
-        for (i, f) in vec_rc.iter().enumerate() {
-            let render = || f.loan_path_user_string(this, tcx);
-            if span_err {
-                tcx.sess.span_err(sp, format!("{}: `{}`", kind, render()).as_slice());
-            }
-            if print {
-                println!("id:{} {}[{}] `{}`", id, kind, i, render());
-            }
-        }
-    };
-
-    let fragments = this.fragments.borrow();
-    instrument_all_paths("moved_leaf_path", &fragments.moved_leaf_paths);
-    instrument_all_fragments("unmoved_fragment", &fragments.unmoved_fragments);
-    instrument_all_paths("parent_of_fragments", &fragments.parents_of_fragments);
-    instrument_all_paths("assigned_leaf_path", &fragments.assigned_leaf_paths);
-}
-
-/// Normalizes the fragment sets in `this`; i.e., removes duplicate entries, constructs the set of
-/// parents, and constructs the left-over fragments.
-///
-/// Note: "left-over fragments" means paths that were not directly referenced in moves nor
-/// assignments, but must nonetheless be tracked as potential drop obligations.
-pub fn fixup_fragment_sets<'tcx>(this: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) {
-
-    let mut fragments = this.fragments.borrow_mut();
-
-    // Swap out contents of fragments so that we can modify the fields
-    // without borrowing the common fragments.
-    let mut unmoved = mem::replace(&mut fragments.unmoved_fragments, vec![]);
-    let mut parents = mem::replace(&mut fragments.parents_of_fragments, vec![]);
-    let mut moved = mem::replace(&mut fragments.moved_leaf_paths, vec![]);
-    let mut assigned = mem::replace(&mut fragments.assigned_leaf_paths, vec![]);
-
-    let path_lps = |mpis: &[MovePathIndex]| -> Vec<String> {
-        mpis.iter().map(|mpi| this.path_loan_path(*mpi).repr(tcx)).collect()
-    };
-
-    let frag_lps = |fs: &[Fragment]| -> Vec<String> {
-        fs.iter().map(|f| f.loan_path_repr(this, tcx)).collect()
-    };
-
-    // First, filter out duplicates
-    moved.sort();
-    moved.dedup();
-    debug!("fragments 1 moved: {}", path_lps(moved.as_slice()));
-
-    assigned.sort();
-    assigned.dedup();
-    debug!("fragments 1 assigned: {}", path_lps(assigned.as_slice()));
-
-    // Second, build parents from the moved and assigned.
-    for m in moved.iter() {
-        let mut p = this.path_parent(*m);
-        while p != InvalidMovePathIndex {
-            parents.push(p);
-            p = this.path_parent(p);
-        }
-    }
-    for a in assigned.iter() {
-        let mut p = this.path_parent(*a);
-        while p != InvalidMovePathIndex {
-            parents.push(p);
-            p = this.path_parent(p);
-        }
-    }
-
-    parents.sort();
-    parents.dedup();
-    debug!("fragments 2 parents: {}", path_lps(parents.as_slice()));
-
-    // Third, filter the moved and assigned fragments down to just the non-parents
-    moved.retain(|f| non_member(*f, parents.as_slice()));
-    debug!("fragments 3 moved: {}", path_lps(moved.as_slice()));
-
-    assigned.retain(|f| non_member(*f, parents.as_slice()));
-    debug!("fragments 3 assigned: {}", path_lps(assigned.as_slice()));
-
-    // Fourth, build the leftover from the moved, assigned, and parents.
-    for m in moved.iter() {
-        let lp = this.path_loan_path(*m);
-        add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
-    }
-    for a in assigned.iter() {
-        let lp = this.path_loan_path(*a);
-        add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
-    }
-    for p in parents.iter() {
-        let lp = this.path_loan_path(*p);
-        add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
-    }
-
-    unmoved.sort();
-    unmoved.dedup();
-    debug!("fragments 4 unmoved: {}", frag_lps(unmoved.as_slice()));
-
-    // Fifth, filter the leftover fragments down to its core.
-    unmoved.retain(|f| match *f {
-        AllButOneFrom(_) => true,
-        Just(mpi) => non_member(mpi, parents.as_slice()) &&
-            non_member(mpi, moved.as_slice()) &&
-            non_member(mpi, assigned.as_slice())
-    });
-    debug!("fragments 5 unmoved: {}", frag_lps(unmoved.as_slice()));
-
-    // Swap contents back in.
-    fragments.unmoved_fragments = unmoved;
-    fragments.parents_of_fragments = parents;
-    fragments.moved_leaf_paths = moved;
-    fragments.assigned_leaf_paths = assigned;
-
-    return;
-
-    fn non_member(elem: MovePathIndex, set: &[MovePathIndex]) -> bool {
-        match set.binary_search_elem(&elem) {
-            slice::BinarySearchResult::Found(_) => false,
-            slice::BinarySearchResult::NotFound(_) => true,
-        }
-    }
-}
-
-/// Adds all of the precisely-tracked siblings of `lp` as potential move paths of interest. For
-/// example, if `lp` represents `s.x.j`, then adds moves paths for `s.x.i` and `s.x.k`, the
-/// siblings of `s.x.j`.
-fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
-                               tcx: &ty::ctxt<'tcx>,
-                               gathered_fragments: &mut Vec<Fragment>,
-                               lp: Rc<LoanPath<'tcx>>,
-                               origin_id: Option<ast::NodeId>) {
-    match lp.kind {
-        LpVar(_) | LpUpvar(..) => {} // Local variables have no siblings.
-
-        // Consuming a downcast is like consuming the original value, so propage inward.
-        LpDowncast(ref loan_parent, _) => {
-            add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id);
-        }
-
-        // *LV for OwnedPtr consumes the contents of the box (at
-        // least when it is non-copy...), so propagate inward.
-        LpExtend(ref loan_parent, _, LpDeref(mc::OwnedPtr)) => {
-            add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id);
-        }
-
-        // *LV for unsafe and borrowed pointers do not consume their loan path, so stop here.
-        LpExtend(_, _, LpDeref(mc::UnsafePtr(..)))   |
-        LpExtend(_, _, LpDeref(mc::Implicit(..)))    |
-        LpExtend(_, _, LpDeref(mc::BorrowedPtr(..))) => {}
-
-        // FIXME(pnkfelix): LV[j] should be tracked, at least in the
-        // sense of we will track the remaining drop obligation of the
-        // rest of the array.
-        //
-        // LV[j] is not tracked precisely
-        LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
-            let mp = this.move_path(tcx, lp.clone());
-            gathered_fragments.push(AllButOneFrom(mp));
-        }
-
-        // field access LV.x and tuple access LV#k are the cases
-        // we are interested in
-        LpExtend(ref loan_parent, mc,
-                 LpInterior(mc::InteriorField(ref field_name))) => {
-            let enum_variant_info = match loan_parent.kind {
-                LpDowncast(ref loan_parent_2, variant_def_id) =>
-                    Some((variant_def_id, loan_parent_2.clone())),
-                LpExtend(..) | LpVar(..) | LpUpvar(..) =>
-                    None,
-            };
-            add_fragment_siblings_for_extension(
-                this,
-                tcx,
-                gathered_fragments,
-                loan_parent, mc, field_name, &lp, origin_id, enum_variant_info);
-        }
-    }
-}
-
-/// We have determined that `origin_lp` destructures to LpExtend(parent, original_field_name).
-/// Based on this, add move paths for all of the siblings of `origin_lp`.
-fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
-                                             tcx: &ty::ctxt<'tcx>,
-                                             gathered_fragments: &mut Vec<Fragment>,
-                                             parent_lp: &Rc<LoanPath<'tcx>>,
-                                             mc: mc::MutabilityCategory,
-                                             origin_field_name: &mc::FieldName,
-                                             origin_lp: &Rc<LoanPath<'tcx>>,
-                                             origin_id: Option<ast::NodeId>,
-                                             enum_variant_info: Option<(ast::DefId,
-                                                                        Rc<LoanPath<'tcx>>)>) {
-    let parent_ty = parent_lp.to_type();
-
-    let add_fragment_sibling_local = |field_name, variant_did| {
-        add_fragment_sibling_core(
-            this, tcx, gathered_fragments, parent_lp.clone(), mc, field_name, origin_lp,
-            variant_did);
-    };
-
-    match (&parent_ty.sty, enum_variant_info) {
-        (&ty::ty_tup(ref v), None) => {
-            let tuple_idx = match *origin_field_name {
-                mc::PositionalField(tuple_idx) => tuple_idx,
-                mc::NamedField(_) =>
-                    panic!("tuple type {} should not have named fields.",
-                           parent_ty.repr(tcx)),
-            };
-            let tuple_len = v.len();
-            for i in range(0, tuple_len) {
-                if i == tuple_idx { continue }
-                let field_name = mc::PositionalField(i);
-                add_fragment_sibling_local(field_name, None);
-            }
-        }
-
-        (&ty::ty_struct(def_id, ref _substs), None) => {
-            let fields = ty::lookup_struct_fields(tcx, def_id);
-            match *origin_field_name {
-                mc::NamedField(ast_name) => {
-                    for f in fields.iter() {
-                        if f.name == ast_name {
-                            continue;
-                        }
-                        let field_name = mc::NamedField(f.name);
-                        add_fragment_sibling_local(field_name, None);
-                    }
-                }
-                mc::PositionalField(tuple_idx) => {
-                    for (i, _f) in fields.iter().enumerate() {
-                        if i == tuple_idx {
-                            continue
-                        }
-                        let field_name = mc::PositionalField(i);
-                        add_fragment_sibling_local(field_name, None);
-                    }
-                }
-            }
-        }
-
-        (&ty::ty_enum(enum_def_id, ref substs), ref enum_variant_info) => {
-            let variant_info = {
-                let mut variants = ty::substd_enum_variants(tcx, enum_def_id, substs);
-                match *enum_variant_info {
-                    Some((variant_def_id, ref _lp2)) =>
-                        variants.iter()
-                        .find(|variant| variant.id == variant_def_id)
-                        .expect("enum_variant_with_id(): no variant exists with that ID")
-                        .clone(),
-                    None => {
-                        assert_eq!(variants.len(), 1);
-                        variants.pop().unwrap()
-                    }
-                }
-            };
-            match *origin_field_name {
-                mc::NamedField(ast_name) => {
-                    let variant_arg_names = variant_info.arg_names.as_ref().unwrap();
-                    for variant_arg_ident in variant_arg_names.iter() {
-                        if variant_arg_ident.name == ast_name {
-                            continue;
-                        }
-                        let field_name = mc::NamedField(variant_arg_ident.name);
-                        add_fragment_sibling_local(field_name, Some(variant_info.id));
-                    }
-                }
-                mc::PositionalField(tuple_idx) => {
-                    let variant_arg_types = &variant_info.args;
-                    for (i, _variant_arg_ty) in variant_arg_types.iter().enumerate() {
-                        if tuple_idx == i {
-                            continue;
-                        }
-                        let field_name = mc::PositionalField(i);
-                        add_fragment_sibling_local(field_name, None);
-                    }
-                }
-            }
-        }
-
-        ref sty_and_variant_info => {
-            let msg = format!("type {} ({}) is not fragmentable",
-                              parent_ty.repr(tcx), sty_and_variant_info);
-            let opt_span = origin_id.and_then(|id|tcx.map.opt_span(id));
-            tcx.sess.opt_span_bug(opt_span, msg.as_slice())
-        }
-    }
-}
-
-/// Adds the single sibling `LpExtend(parent, new_field_name)` of `origin_lp` (the original
-/// loan-path).
-fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>,
-                                   tcx: &ty::ctxt<'tcx>,
-                                   gathered_fragments: &mut Vec<Fragment>,
-                                   parent: Rc<LoanPath<'tcx>>,
-                                   mc: mc::MutabilityCategory,
-                                   new_field_name: mc::FieldName,
-                                   origin_lp: &Rc<LoanPath<'tcx>>,
-                                   enum_variant_did: Option<ast::DefId>) -> MovePathIndex {
-    let opt_variant_did = match parent.kind {
-        LpDowncast(_, variant_did) => Some(variant_did),
-        LpVar(..) | LpUpvar(..) | LpExtend(..) => enum_variant_did,
-    };
-
-    let loan_path_elem = LpInterior(mc::InteriorField(new_field_name));
-    let new_lp_type = match new_field_name {
-        mc::NamedField(ast_name) =>
-            ty::named_element_ty(tcx, parent.to_type(), ast_name, opt_variant_did),
-        mc::PositionalField(idx) =>
-            ty::positional_element_ty(tcx, parent.to_type(), idx, opt_variant_did),
-    };
-    let new_lp_variant = LpExtend(parent, mc, loan_path_elem);
-    let new_lp = LoanPath::new(new_lp_variant, new_lp_type.unwrap());
-    debug!("add_fragment_sibling_core(new_lp={}, origin_lp={})",
-           new_lp.repr(tcx), origin_lp.repr(tcx));
-    let mp = this.move_path(tcx, Rc::new(new_lp));
-
-    // Do not worry about checking for duplicates here; we will sort
-    // and dedup after all are added.
-    gathered_fragments.push(Just(mp));
-
-    mp
-}
diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs
deleted file mode 100644 (file)
index 6511416..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Computes moves.
-
-use middle::borrowck::*;
-use middle::borrowck::LoanPathKind::*;
-use middle::borrowck::gather_loans::move_error::MoveSpanAndPath;
-use middle::borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
-use middle::borrowck::move_data::*;
-use middle::expr_use_visitor as euv;
-use middle::mem_categorization as mc;
-use middle::ty;
-use syntax::ast;
-use syntax::codemap::Span;
-use util::ppaux::Repr;
-
-use std::rc::Rc;
-
-struct GatherMoveInfo<'tcx> {
-    id: ast::NodeId,
-    kind: MoveKind,
-    cmt: mc::cmt<'tcx>,
-    span_path_opt: Option<MoveSpanAndPath>
-}
-
-pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                             move_data: &MoveData<'tcx>,
-                             decl_id: ast::NodeId,
-                             _decl_span: Span,
-                             var_id: ast::NodeId) {
-    let ty = ty::node_id_to_type(bccx.tcx, var_id);
-    let loan_path = Rc::new(LoanPath::new(LpVar(var_id), ty));
-    move_data.add_move(bccx.tcx, loan_path, decl_id, Declared);
-}
-
-pub fn gather_move_from_expr<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                       move_data: &MoveData<'tcx>,
-                                       move_error_collector: &MoveErrorCollector<'tcx>,
-                                       move_expr_id: ast::NodeId,
-                                       cmt: mc::cmt<'tcx>,
-                                       move_reason: euv::MoveReason) {
-    let kind = match move_reason {
-        euv::DirectRefMove | euv::PatBindingMove => MoveExpr,
-        euv::CaptureMove => Captured
-    };
-    let move_info = GatherMoveInfo {
-        id: move_expr_id,
-        kind: kind,
-        cmt: cmt,
-        span_path_opt: None,
-    };
-    gather_move(bccx, move_data, move_error_collector, move_info);
-}
-
-pub fn gather_match_variant<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                      move_data: &MoveData<'tcx>,
-                                      _move_error_collector: &MoveErrorCollector<'tcx>,
-                                      move_pat: &ast::Pat,
-                                      cmt: mc::cmt<'tcx>,
-                                      mode: euv::MatchMode) {
-    let tcx = bccx.tcx;
-    debug!("gather_match_variant(move_pat={}, cmt={}, mode={})",
-           move_pat.id, cmt.repr(tcx), mode);
-
-    let opt_lp = opt_loan_path(&cmt);
-    match opt_lp {
-        Some(lp) => {
-            match lp.kind {
-                LpDowncast(ref base_lp, _) =>
-                    move_data.add_variant_match(
-                        tcx, lp.clone(), move_pat.id, base_lp.clone(), mode),
-                _ => panic!("should only call gather_match_variant \
-                             for cat_downcast cmt"),
-            }
-        }
-        None => {
-            // We get None when input to match is non-path (e.g.
-            // temporary result like a function call). Since no
-            // loan-path is being matched, no need to record a
-            // downcast.
-            return;
-        }
-    }
-}
-
-pub fn gather_move_from_pat<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                      move_data: &MoveData<'tcx>,
-                                      move_error_collector: &MoveErrorCollector<'tcx>,
-                                      move_pat: &ast::Pat,
-                                      cmt: mc::cmt<'tcx>) {
-    let pat_span_path_opt = match move_pat.node {
-        ast::PatIdent(_, ref path1, _) => {
-            Some(MoveSpanAndPath{span: move_pat.span,
-                                 ident: path1.node})
-        },
-        _ => None,
-    };
-    let move_info = GatherMoveInfo {
-        id: move_pat.id,
-        kind: MovePat,
-        cmt: cmt,
-        span_path_opt: pat_span_path_opt,
-    };
-    gather_move(bccx, move_data, move_error_collector, move_info);
-}
-
-fn gather_move<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                         move_data: &MoveData<'tcx>,
-                         move_error_collector: &MoveErrorCollector<'tcx>,
-                         move_info: GatherMoveInfo<'tcx>) {
-    debug!("gather_move(move_id={}, cmt={})",
-           move_info.id, move_info.cmt.repr(bccx.tcx));
-
-    let potentially_illegal_move =
-                check_and_get_illegal_move_origin(bccx, &move_info.cmt);
-    match potentially_illegal_move {
-        Some(illegal_move_origin) => {
-            debug!("illegal_move_origin={}", illegal_move_origin.repr(bccx.tcx));
-            let error = MoveError::with_move_info(illegal_move_origin,
-                                                  move_info.span_path_opt);
-            move_error_collector.add_error(error);
-            return
-        }
-        None => ()
-    }
-
-    match opt_loan_path(&move_info.cmt) {
-        Some(loan_path) => {
-            move_data.add_move(bccx.tcx, loan_path,
-                               move_info.id, move_info.kind);
-        }
-        None => {
-            // move from rvalue or unsafe pointer, hence ok
-        }
-    }
-}
-
-pub fn gather_assignment<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                   move_data: &MoveData<'tcx>,
-                                   assignment_id: ast::NodeId,
-                                   assignment_span: Span,
-                                   assignee_loan_path: Rc<LoanPath<'tcx>>,
-                                   assignee_id: ast::NodeId,
-                                   mode: euv::MutateMode) {
-    move_data.add_assignment(bccx.tcx,
-                             assignee_loan_path,
-                             assignment_id,
-                             assignment_span,
-                             assignee_id,
-                             mode);
-}
-
-fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                               cmt: &mc::cmt<'tcx>)
-                                               -> Option<mc::cmt<'tcx>> {
-    match cmt.cat {
-        mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
-        mc::cat_deref(_, _, mc::Implicit(..)) |
-        mc::cat_deref(_, _, mc::UnsafePtr(..)) |
-        mc::cat_static_item => {
-            Some(cmt.clone())
-        }
-
-        mc::cat_rvalue(..) |
-        mc::cat_local(..) |
-        mc::cat_upvar(..) => {
-            None
-        }
-
-        mc::cat_downcast(ref b, _) |
-        mc::cat_interior(ref b, _) => {
-            match b.ty.sty {
-                ty::ty_struct(did, _) | ty::ty_enum(did, _) => {
-                    if ty::has_dtor(bccx.tcx, did) {
-                        Some(cmt.clone())
-                    } else {
-                        check_and_get_illegal_move_origin(bccx, b)
-                    }
-                }
-                _ => {
-                    check_and_get_illegal_move_origin(bccx, b)
-                }
-            }
-        }
-
-        mc::cat_deref(ref b, _, mc::OwnedPtr) => {
-            check_and_get_illegal_move_origin(bccx, b)
-        }
-    }
-}
diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
deleted file mode 100644 (file)
index e6a7c15..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! This module implements the check that the lifetime of a borrow
-//! does not exceed the lifetime of the value being borrowed.
-
-use middle::borrowck::*;
-use middle::expr_use_visitor as euv;
-use middle::mem_categorization as mc;
-use middle::region;
-use middle::ty;
-use util::ppaux::Repr;
-use syntax::ast;
-use syntax::codemap::Span;
-
-type R = Result<(),()>;
-
-pub fn guarantee_lifetime<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                    item_scope: region::CodeExtent,
-                                    span: Span,
-                                    cause: euv::LoanCause,
-                                    cmt: mc::cmt<'tcx>,
-                                    loan_region: ty::Region,
-                                    _: ty::BorrowKind)
-                                    -> Result<(),()> {
-    //! Reports error if `loan_region` is larger than S
-    //! where S is `item_scope` if `cmt` is an upvar,
-    //! and is scope of `cmt` otherwise.
-    debug!("guarantee_lifetime(cmt={}, loan_region={})",
-           cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx));
-    let ctxt = GuaranteeLifetimeContext {bccx: bccx,
-                                         item_scope: item_scope,
-                                         span: span,
-                                         cause: cause,
-                                         loan_region: loan_region,
-                                         cmt_original: cmt.clone()};
-    ctxt.check(&cmt, None)
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Private
-
-struct GuaranteeLifetimeContext<'a, 'tcx: 'a> {
-    bccx: &'a BorrowckCtxt<'a, 'tcx>,
-
-    // the scope of the function body for the enclosing item
-    item_scope: region::CodeExtent,
-
-    span: Span,
-    cause: euv::LoanCause,
-    loan_region: ty::Region,
-    cmt_original: mc::cmt<'tcx>
-}
-
-impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
-
-    fn check(&self, cmt: &mc::cmt<'tcx>, discr_scope: Option<ast::NodeId>) -> R {
-        //! Main routine. Walks down `cmt` until we find the
-        //! "guarantor".  Reports an error if `self.loan_region` is
-        //! larger than scope of `cmt`.
-        debug!("guarantee_lifetime.check(cmt={}, loan_region={})",
-               cmt.repr(self.bccx.tcx),
-               self.loan_region.repr(self.bccx.tcx));
-
-        match cmt.cat {
-            mc::cat_rvalue(..) |
-            mc::cat_local(..) |                         // L-Local
-            mc::cat_upvar(..) |
-            mc::cat_deref(_, _, mc::BorrowedPtr(..)) |  // L-Deref-Borrowed
-            mc::cat_deref(_, _, mc::Implicit(..)) |
-            mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
-                self.check_scope(self.scope(cmt))
-            }
-
-            mc::cat_static_item => {
-                Ok(())
-            }
-
-            mc::cat_downcast(ref base, _) |
-            mc::cat_deref(ref base, _, mc::OwnedPtr) |     // L-Deref-Send
-            mc::cat_interior(ref base, _) => {             // L-Field
-                self.check(base, discr_scope)
-            }
-        }
-    }
-
-    fn check_scope(&self, max_scope: ty::Region) -> R {
-        //! Reports an error if `loan_region` is larger than `max_scope`
-
-        if !self.bccx.is_subregion_of(self.loan_region, max_scope) {
-            Err(self.report_error(err_out_of_scope(max_scope, self.loan_region)))
-        } else {
-            Ok(())
-        }
-    }
-
-    fn scope(&self, cmt: &mc::cmt) -> ty::Region {
-        //! Returns the maximal region scope for the which the
-        //! lvalue `cmt` is guaranteed to be valid without any
-        //! rooting etc, and presuming `cmt` is not mutated.
-
-        // See the SCOPE(LV) function in doc.rs
-
-        match cmt.cat {
-            mc::cat_rvalue(temp_scope) => {
-                temp_scope
-            }
-            mc::cat_upvar(..) => {
-                ty::ReScope(self.item_scope)
-            }
-            mc::cat_static_item => {
-                ty::ReStatic
-            }
-            mc::cat_local(local_id) => {
-                ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id))
-            }
-            mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
-                ty::ReStatic
-            }
-            mc::cat_deref(_, _, mc::BorrowedPtr(_, r)) |
-            mc::cat_deref(_, _, mc::Implicit(_, r)) => {
-                r
-            }
-            mc::cat_downcast(ref cmt, _) |
-            mc::cat_deref(ref cmt, _, mc::OwnedPtr) |
-            mc::cat_interior(ref cmt, _) => {
-                self.scope(cmt)
-            }
-        }
-    }
-
-    fn report_error(&self, code: bckerr_code) {
-        self.bccx.report(BckError { cmt: self.cmt_original.clone(),
-                                    span: self.span,
-                                    cause: self.cause,
-                                    code: code });
-    }
-}
diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs
deleted file mode 100644 (file)
index ca9d4b5..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ----------------------------------------------------------------------
-// Gathering loans
-//
-// The borrow check proceeds in two phases. In phase one, we gather the full
-// set of loans that are required at any point.  These are sorted according to
-// their associated scopes.  In phase two, checking loans, we will then make
-// sure that all of these loans are honored.
-
-use middle::borrowck::*;
-use middle::borrowck::LoanPathKind::*;
-use middle::borrowck::move_data::MoveData;
-use middle::expr_use_visitor as euv;
-use middle::mem_categorization as mc;
-use middle::region;
-use middle::ty::ParameterEnvironment;
-use middle::ty;
-use util::ppaux::{Repr};
-
-use syntax::ast;
-use syntax::codemap::Span;
-use syntax::visit;
-use syntax::visit::Visitor;
-use syntax::ast::{Expr, FnDecl, Block, NodeId, Pat};
-
-mod lifetime;
-mod restrictions;
-mod gather_moves;
-mod move_error;
-
-pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                    fn_id: NodeId,
-                                    decl: &ast::FnDecl,
-                                    body: &ast::Block)
-                                    -> (Vec<Loan<'tcx>>,
-                                        move_data::MoveData<'tcx>) {
-    let mut glcx = GatherLoanCtxt {
-        bccx: bccx,
-        all_loans: Vec::new(),
-        item_ub: region::CodeExtent::from_node_id(body.id),
-        move_data: MoveData::new(),
-        move_error_collector: move_error::MoveErrorCollector::new(),
-    };
-
-    let param_env = ParameterEnvironment::for_item(bccx.tcx, fn_id);
-
-    {
-        let mut euv = euv::ExprUseVisitor::new(&mut glcx,
-                                               bccx.tcx,
-                                               param_env);
-        euv.walk_fn(decl, body);
-    }
-
-    glcx.report_potential_errors();
-    let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
-    (all_loans, move_data)
-}
-
-struct GatherLoanCtxt<'a, 'tcx: 'a> {
-    bccx: &'a BorrowckCtxt<'a, 'tcx>,
-    move_data: move_data::MoveData<'tcx>,
-    move_error_collector: move_error::MoveErrorCollector<'tcx>,
-    all_loans: Vec<Loan<'tcx>>,
-    /// `item_ub` is used as an upper-bound on the lifetime whenever we
-    /// ask for the scope of an expression categorized as an upvar.
-    item_ub: region::CodeExtent,
-}
-
-impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
-    fn consume(&mut self,
-               consume_id: ast::NodeId,
-               _consume_span: Span,
-               cmt: mc::cmt<'tcx>,
-               mode: euv::ConsumeMode) {
-        debug!("consume(consume_id={}, cmt={}, mode={})",
-               consume_id, cmt.repr(self.tcx()), mode);
-
-        match mode {
-            euv::Move(move_reason) => {
-                gather_moves::gather_move_from_expr(
-                    self.bccx, &self.move_data, &self.move_error_collector,
-                    consume_id, cmt, move_reason);
-            }
-            euv::Copy => { }
-        }
-    }
-
-    fn matched_pat(&mut self,
-                   matched_pat: &ast::Pat,
-                   cmt: mc::cmt<'tcx>,
-                   mode: euv::MatchMode) {
-        debug!("matched_pat(matched_pat={}, cmt={}, mode={})",
-               matched_pat.repr(self.tcx()),
-               cmt.repr(self.tcx()),
-               mode);
-
-        if let mc::cat_downcast(..) = cmt.cat {
-            gather_moves::gather_match_variant(
-                self.bccx, &self.move_data, &self.move_error_collector,
-                matched_pat, cmt, mode);
-        }
-    }
-
-    fn consume_pat(&mut self,
-                   consume_pat: &ast::Pat,
-                   cmt: mc::cmt<'tcx>,
-                   mode: euv::ConsumeMode) {
-        debug!("consume_pat(consume_pat={}, cmt={}, mode={})",
-               consume_pat.repr(self.tcx()),
-               cmt.repr(self.tcx()),
-               mode);
-
-        match mode {
-            euv::Copy => { return; }
-            euv::Move(_) => { }
-        }
-
-        gather_moves::gather_move_from_pat(
-            self.bccx, &self.move_data, &self.move_error_collector,
-            consume_pat, cmt);
-    }
-
-    fn borrow(&mut self,
-              borrow_id: ast::NodeId,
-              borrow_span: Span,
-              cmt: mc::cmt<'tcx>,
-              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);
-
-        self.guarantee_valid(borrow_id,
-                             borrow_span,
-                             cmt,
-                             bk,
-                             loan_region,
-                             loan_cause);
-    }
-
-    fn mutate(&mut self,
-              assignment_id: ast::NodeId,
-              assignment_span: Span,
-              assignee_cmt: mc::cmt<'tcx>,
-              mode: euv::MutateMode)
-    {
-        debug!("mutate(assignment_id={}, assignee_cmt={})",
-               assignment_id, assignee_cmt.repr(self.tcx()));
-
-        match opt_loan_path(&assignee_cmt) {
-            Some(lp) => {
-                gather_moves::gather_assignment(self.bccx, &self.move_data,
-                                                assignment_id, assignment_span,
-                                                lp, assignee_cmt.id, mode);
-            }
-            None => {
-                // This can occur with e.g. `*foo() = 5`.  In such
-                // cases, there is no need to check for conflicts
-                // with moves etc, just ignore.
-            }
-        }
-    }
-
-    fn decl_without_init(&mut self, id: ast::NodeId, span: Span) {
-        gather_moves::gather_decl(self.bccx, &self.move_data, id, span, id);
-    }
-}
-
-/// Implements the A-* rules in doc.rs.
-fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                borrow_span: Span,
-                                loan_cause: euv::LoanCause,
-                                cmt: mc::cmt<'tcx>,
-                                req_kind: ty::BorrowKind)
-                                -> Result<(),()> {
-
-    match (cmt.freely_aliasable(bccx.tcx), req_kind) {
-        (None, _) => {
-            /* Uniquely accessible path -- OK for `&` and `&mut` */
-            Ok(())
-        }
-        (Some(mc::AliasableStatic(safety)), ty::ImmBorrow) => {
-            // Borrow of an immutable static item:
-            match safety {
-                mc::InteriorUnsafe => {
-                    // If the static item contains an Unsafe<T>, it has interior
-                    // mutability.  In such cases, another phase of the compiler
-                    // will ensure that the type is `Sync` and then trans will
-                    // not put it in rodata, so this is ok to allow.
-                    Ok(())
-                }
-                mc::InteriorSafe => {
-                    // Immutable static can be borrowed, no problem.
-                    Ok(())
-                }
-            }
-        }
-        (Some(mc::AliasableStaticMut(..)), _) => {
-            // Even touching a static mut is considered unsafe. We assume the
-            // user knows what they're doing in these cases.
-            Ok(())
-        }
-        (Some(alias_cause), ty::UniqueImmBorrow) |
-        (Some(alias_cause), ty::MutBorrow) => {
-            bccx.report_aliasability_violation(
-                        borrow_span,
-                        BorrowViolation(loan_cause),
-                        alias_cause);
-            Err(())
-        }
-        (_, _) => {
-            Ok(())
-        }
-    }
-}
-
-impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
-    pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
-
-    /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
-    /// reports an error.  This may entail taking out loans, which will be added to the
-    /// `req_loan_map`.
-    fn guarantee_valid(&mut self,
-                       borrow_id: ast::NodeId,
-                       borrow_span: Span,
-                       cmt: mc::cmt<'tcx>,
-                       req_kind: ty::BorrowKind,
-                       loan_region: ty::Region,
-                       cause: euv::LoanCause) {
-        debug!("guarantee_valid(borrow_id={}, cmt={}, \
-                req_mutbl={}, loan_region={})",
-               borrow_id,
-               cmt.repr(self.tcx()),
-               req_kind,
-               loan_region);
-
-        // a loan for the empty region can never be dereferenced, so
-        // it is always safe
-        if loan_region == ty::ReEmpty {
-            return;
-        }
-
-        // Check that the lifetime of the borrow does not exceed
-        // the lifetime of the data being borrowed.
-        if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
-                                        borrow_span, cause, cmt.clone(), loan_region,
-                                        req_kind).is_err() {
-            return; // reported an error, no sense in reporting more.
-        }
-
-        // Check that we don't allow mutable borrows of non-mutable data.
-        if check_mutability(self.bccx, borrow_span, cause,
-                            cmt.clone(), req_kind).is_err() {
-            return; // reported an error, no sense in reporting more.
-        }
-
-        // Check that we don't allow mutable borrows of aliasable data.
-        if check_aliasability(self.bccx, borrow_span, cause,
-                              cmt.clone(), req_kind).is_err() {
-            return; // reported an error, no sense in reporting more.
-        }
-
-        // Compute the restrictions that are required to enforce the
-        // loan is safe.
-        let restr = restrictions::compute_restrictions(
-            self.bccx, borrow_span, cause,
-            cmt.clone(), loan_region);
-
-        debug!("guarantee_valid(): restrictions={}", restr);
-
-        // Create the loan record (if needed).
-        let loan = match restr {
-            restrictions::Safe => {
-                // No restrictions---no loan record necessary
-                return;
-            }
-
-            restrictions::SafeIf(loan_path, restricted_paths) => {
-                let loan_scope = match loan_region {
-                    ty::ReScope(scope) => scope,
-
-                    ty::ReFree(ref fr) => fr.scope,
-
-                    ty::ReStatic => {
-                        // If we get here, an error must have been
-                        // reported in
-                        // `lifetime::guarantee_lifetime()`, because
-                        // the only legal ways to have a borrow with a
-                        // static lifetime should not require
-                        // restrictions. To avoid reporting derived
-                        // errors, we just return here without adding
-                        // any loans.
-                        return;
-                    }
-
-                    ty::ReEmpty |
-                    ty::ReLateBound(..) |
-                    ty::ReEarlyBound(..) |
-                    ty::ReInfer(..) => {
-                        self.tcx().sess.span_bug(
-                            cmt.span,
-                            format!("invalid borrow lifetime: {}",
-                                    loan_region).as_slice());
-                    }
-                };
-                debug!("loan_scope = {}", loan_scope);
-
-                let borrow_scope = region::CodeExtent::from_node_id(borrow_id);
-                let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
-                debug!("gen_scope = {}", gen_scope);
-
-                let kill_scope = self.compute_kill_scope(loan_scope, &*loan_path);
-                debug!("kill_scope = {}", kill_scope);
-
-                if req_kind == ty::MutBorrow {
-                    self.mark_loan_path_as_mutated(&*loan_path);
-                }
-
-                Loan {
-                    index: self.all_loans.len(),
-                    loan_path: loan_path,
-                    kind: req_kind,
-                    gen_scope: gen_scope,
-                    kill_scope: kill_scope,
-                    span: borrow_span,
-                    restricted_paths: restricted_paths,
-                    cause: cause,
-                }
-            }
-        };
-
-        debug!("guarantee_valid(borrow_id={}), loan={}",
-               borrow_id, loan.repr(self.tcx()));
-
-        // let loan_path = loan.loan_path;
-        // let loan_gen_scope = loan.gen_scope;
-        // let loan_kill_scope = loan.kill_scope;
-        self.all_loans.push(loan);
-
-        // if loan_gen_scope != borrow_id {
-            // FIXME(#6268) Nested method calls
-            //
-            // Typically, the scope of the loan includes the point at
-            // which the loan is originated. This
-            // This is a subtle case. See the test case
-            // <compile-fail/borrowck-bad-nested-calls-free.rs>
-            // to see what we are guarding against.
-
-            //let restr = restrictions::compute_restrictions(
-            //    self.bccx, borrow_span, cmt, RESTR_EMPTY);
-            //let loan = {
-            //    let all_loans = &mut *self.all_loans; // FIXME(#5074)
-            //    Loan {
-            //        index: all_loans.len(),
-            //        loan_path: loan_path,
-            //        cmt: cmt,
-            //        mutbl: ConstMutability,
-            //        gen_scope: borrow_id,
-            //        kill_scope: kill_scope,
-            //        span: borrow_span,
-            //        restrictions: restrictions
-            //    }
-        // }
-
-        fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                      borrow_span: Span,
-                                      cause: euv::LoanCause,
-                                      cmt: mc::cmt<'tcx>,
-                                      req_kind: ty::BorrowKind)
-                                      -> Result<(),()> {
-            //! Implements the M-* rules in doc.rs.
-
-            match req_kind {
-                ty::UniqueImmBorrow | ty::ImmBorrow => {
-                    match cmt.mutbl {
-                        // I am intentionally leaving this here to help
-                        // refactoring if, in the future, we should add new
-                        // kinds of mutability.
-                        mc::McImmutable | mc::McDeclared | mc::McInherited => {
-                            // both imm and mut data can be lent as imm;
-                            // for mutable data, this is a freeze
-                            Ok(())
-                        }
-                    }
-                }
-
-                ty::MutBorrow => {
-                    // Only mutable data can be lent as mutable.
-                    if !cmt.mutbl.is_mutable() {
-                        Err(bccx.report(BckError { span: borrow_span,
-                                                   cause: cause,
-                                                   cmt: cmt,
-                                                   code: err_mutbl }))
-                    } else {
-                        Ok(())
-                    }
-                }
-            }
-        }
-    }
-
-    pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
-        //! For mutable loans of content whose mutability derives
-        //! from a local variable, mark the mutability decl as necessary.
-
-        match loan_path.kind {
-            LpVar(local_id) |
-            LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
-                self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
-            }
-            LpDowncast(ref base, _) |
-            LpExtend(ref base, mc::McInherited, _) |
-            LpExtend(ref base, mc::McDeclared, _) => {
-                self.mark_loan_path_as_mutated(&**base);
-            }
-            LpExtend(_, mc::McImmutable, _) => {
-                // Nothing to do.
-            }
-        }
-    }
-
-    pub fn compute_gen_scope(&self,
-                             borrow_scope: region::CodeExtent,
-                             loan_scope: region::CodeExtent)
-                             -> region::CodeExtent {
-        //! Determine when to introduce the loan. Typically the loan
-        //! is introduced at the point of the borrow, but in some cases,
-        //! notably method arguments, the loan may be introduced only
-        //! later, once it comes into scope.
-
-        if self.bccx.tcx.region_maps.is_subscope_of(borrow_scope, loan_scope) {
-            borrow_scope
-        } else {
-            loan_scope
-        }
-    }
-
-    pub fn compute_kill_scope(&self, loan_scope: region::CodeExtent, lp: &LoanPath<'tcx>)
-                              -> region::CodeExtent {
-        //! Determine when the loan restrictions go out of scope.
-        //! This is either when the lifetime expires or when the
-        //! local variable which roots the loan-path goes out of scope,
-        //! whichever happens faster.
-        //!
-        //! It may seem surprising that we might have a loan region
-        //! larger than the variable which roots the loan-path; this can
-        //! come about when variables of `&mut` type are re-borrowed,
-        //! as in this example:
-        //!
-        //!     fn counter<'a>(v: &'a mut Foo) -> &'a mut uint {
-        //!         &mut v.counter
-        //!     }
-        //!
-        //! In this case, the reference (`'a`) outlives the
-        //! variable `v` that hosts it. Note that this doesn't come up
-        //! with immutable `&` pointers, because borrows of such pointers
-        //! do not require restrictions and hence do not cause a loan.
-
-        let lexical_scope = lp.kill_scope(self.bccx.tcx);
-        let rm = &self.bccx.tcx.region_maps;
-        if rm.is_subscope_of(lexical_scope, loan_scope) {
-            lexical_scope
-        } else {
-            assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope));
-            loan_scope
-        }
-    }
-
-    pub fn report_potential_errors(&self) {
-        self.move_error_collector.report_potential_errors(self.bccx);
-    }
-}
-
-/// Context used while gathering loans on static initializers
-///
-/// This visitor walks static initializer's expressions and makes
-/// sure the loans being taken are sound.
-struct StaticInitializerCtxt<'a, 'tcx: 'a> {
-    bccx: &'a BorrowckCtxt<'a, 'tcx>
-}
-
-impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
-    fn visit_expr(&mut self, ex: &Expr) {
-        if let ast::ExprAddrOf(mutbl, ref base) = ex.node {
-            let base_cmt = self.bccx.cat_expr(&**base);
-            let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
-            // Check that we don't allow borrows of unsafe static items.
-            if check_aliasability(self.bccx, ex.span, euv::AddrOf,
-                                  base_cmt, borrow_kind).is_err() {
-                return; // reported an error, no sense in reporting more.
-            }
-        }
-
-        visit::walk_expr(self, ex);
-    }
-}
-
-pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &ast::Expr) {
-
-    debug!("gather_loans_in_static_initializer(expr={})", expr.repr(bccx.tcx));
-
-    let mut sicx = StaticInitializerCtxt {
-        bccx: bccx
-    };
-
-    sicx.visit_expr(expr);
-}
diff --git a/src/librustc/middle/borrowck/gather_loans/move_error.rs b/src/librustc/middle/borrowck/gather_loans/move_error.rs
deleted file mode 100644 (file)
index aaa0fa8..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use middle::mem_categorization as mc;
-use middle::borrowck::BorrowckCtxt;
-use middle::ty;
-
-use std::cell::RefCell;
-use syntax::ast;
-use syntax::codemap;
-use syntax::print::pprust;
-use util::ppaux::UserString;
-
-pub struct MoveErrorCollector<'tcx> {
-    errors: RefCell<Vec<MoveError<'tcx>>>
-}
-
-impl<'tcx> MoveErrorCollector<'tcx> {
-    pub fn new() -> MoveErrorCollector<'tcx> {
-        MoveErrorCollector {
-            errors: RefCell::new(Vec::new())
-        }
-    }
-
-    pub fn add_error(&self, error: MoveError<'tcx>) {
-        self.errors.borrow_mut().push(error);
-    }
-
-    pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
-        report_move_errors(bccx, self.errors.borrow().deref())
-    }
-}
-
-pub struct MoveError<'tcx> {
-    move_from: mc::cmt<'tcx>,
-    move_to: Option<MoveSpanAndPath>
-}
-
-impl<'tcx> MoveError<'tcx> {
-    pub fn with_move_info(move_from: mc::cmt<'tcx>,
-                          move_to: Option<MoveSpanAndPath>)
-                          -> MoveError<'tcx> {
-        MoveError {
-            move_from: move_from,
-            move_to: move_to,
-        }
-    }
-}
-
-#[deriving(Clone)]
-pub struct MoveSpanAndPath {
-    pub span: codemap::Span,
-    pub ident: ast::Ident
-}
-
-pub struct GroupedMoveErrors<'tcx> {
-    move_from: mc::cmt<'tcx>,
-    move_to_places: Vec<MoveSpanAndPath>
-}
-
-fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                errors: &Vec<MoveError<'tcx>>) {
-    let grouped_errors = group_errors_with_same_origin(errors);
-    for error in grouped_errors.iter() {
-        report_cannot_move_out_of(bccx, error.move_from.clone());
-        let mut is_first_note = true;
-        for move_to in error.move_to_places.iter() {
-            note_move_destination(bccx, move_to.span,
-                                  &move_to.ident, is_first_note);
-            is_first_note = false;
-        }
-    }
-}
-
-fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
-                                       -> Vec<GroupedMoveErrors<'tcx>> {
-    let mut grouped_errors = Vec::new();
-    for error in errors.iter() {
-        append_to_grouped_errors(&mut grouped_errors, error)
-    }
-    return grouped_errors;
-
-    fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>,
-                                      error: &MoveError<'tcx>) {
-        let move_from_id = error.move_from.id;
-        debug!("append_to_grouped_errors(move_from_id={})", move_from_id);
-        let move_to = if error.move_to.is_some() {
-            vec!(error.move_to.clone().unwrap())
-        } else {
-            Vec::new()
-        };
-        for ge in grouped_errors.iter_mut() {
-            if move_from_id == ge.move_from.id && error.move_to.is_some() {
-                debug!("appending move_to to list");
-                ge.move_to_places.extend(move_to.into_iter());
-                return
-            }
-        }
-        debug!("found a new move from location");
-        grouped_errors.push(GroupedMoveErrors {
-            move_from: error.move_from.clone(),
-            move_to_places: move_to
-        })
-    }
-}
-
-fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                       move_from: mc::cmt<'tcx>) {
-    match move_from.cat {
-        mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
-        mc::cat_deref(_, _, mc::Implicit(..)) |
-        mc::cat_deref(_, _, mc::UnsafePtr(..)) |
-        mc::cat_static_item => {
-            bccx.span_err(
-                move_from.span,
-                format!("cannot move out of {}",
-                        bccx.cmt_to_string(&*move_from)).as_slice());
-        }
-
-        mc::cat_downcast(ref b, _) |
-        mc::cat_interior(ref b, _) => {
-            match b.ty.sty {
-                ty::ty_struct(did, _)
-                | ty::ty_enum(did, _) if ty::has_dtor(bccx.tcx, did) => {
-                    bccx.span_err(
-                        move_from.span,
-                        format!("cannot move out of type `{}`, \
-                                 which defines the `Drop` trait",
-                                b.ty.user_string(bccx.tcx)).as_slice());
-                },
-                _ => panic!("this path should not cause illegal move")
-            }
-        }
-        _ => panic!("this path should not cause illegal move")
-    }
-}
-
-fn note_move_destination(bccx: &BorrowckCtxt,
-                         move_to_span: codemap::Span,
-                         pat_ident: &ast::Ident,
-                         is_first_note: bool) {
-    let pat_name = pprust::ident_to_string(pat_ident);
-    if is_first_note {
-        bccx.span_note(
-            move_to_span,
-            "attempting to move value to here");
-        bccx.span_help(
-            move_to_span,
-            format!("to prevent the move, \
-                     use `ref {0}` or `ref mut {0}` to capture value by \
-                     reference",
-                    pat_name).as_slice());
-    } else {
-        bccx.span_note(move_to_span,
-                       format!("and here (use `ref {0}` or `ref mut {0}`)",
-                               pat_name).as_slice());
-    }
-}
diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs
deleted file mode 100644 (file)
index bd9cf8f..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Computes the restrictions that result from a borrow.
-
-pub use self::RestrictionResult::*;
-
-use middle::borrowck::*;
-use middle::borrowck::LoanPathElem::*;
-use middle::borrowck::LoanPathKind::*;
-use middle::expr_use_visitor as euv;
-use middle::mem_categorization as mc;
-use middle::ty;
-use syntax::codemap::Span;
-use util::ppaux::Repr;
-
-use std::rc::Rc;
-
-#[deriving(Show)]
-pub enum RestrictionResult<'tcx> {
-    Safe,
-    SafeIf(Rc<LoanPath<'tcx>>, Vec<Rc<LoanPath<'tcx>>>)
-}
-
-pub fn compute_restrictions<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                      span: Span,
-                                      cause: euv::LoanCause,
-                                      cmt: mc::cmt<'tcx>,
-                                      loan_region: ty::Region)
-                                      -> RestrictionResult<'tcx> {
-    let ctxt = RestrictionsContext {
-        bccx: bccx,
-        span: span,
-        cause: cause,
-        loan_region: loan_region,
-    };
-
-    ctxt.restrict(cmt)
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Private
-
-struct RestrictionsContext<'a, 'tcx: 'a> {
-    bccx: &'a BorrowckCtxt<'a, 'tcx>,
-    span: Span,
-    loan_region: ty::Region,
-    cause: euv::LoanCause,
-}
-
-impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
-    fn restrict(&self,
-                cmt: mc::cmt<'tcx>) -> RestrictionResult<'tcx> {
-        debug!("restrict(cmt={})", cmt.repr(self.bccx.tcx));
-
-        let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
-
-        match cmt.cat.clone() {
-            mc::cat_rvalue(..) => {
-                // Effectively, rvalues are stored into a
-                // non-aliasable temporary on the stack. Since they
-                // are inherently non-aliasable, they can only be
-                // accessed later through the borrow itself and hence
-                // must inherently comply with its terms.
-                Safe
-            }
-
-            mc::cat_local(local_id) => {
-                // R-Variable, locally declared
-                let lp = new_lp(LpVar(local_id));
-                SafeIf(lp.clone(), vec![lp])
-            }
-
-            mc::cat_upvar(mc::Upvar { id, .. }) => {
-                // R-Variable, captured into closure
-                let lp = new_lp(LpUpvar(id));
-                SafeIf(lp.clone(), vec![lp])
-            }
-
-            mc::cat_downcast(cmt_base, _) => {
-                // When we borrow the interior of an enum, we have to
-                // ensure the enum itself is not mutated, because that
-                // could cause the type of the memory to change.
-                self.restrict(cmt_base)
-            }
-
-            mc::cat_interior(cmt_base, i) => {
-                // R-Field
-                //
-                // Overwriting the base would not change the type of
-                // the memory, so no additional restrictions are
-                // needed.
-                let result = self.restrict(cmt_base);
-                self.extend(result, &cmt, LpInterior(i))
-            }
-
-            mc::cat_static_item(..) => {
-                Safe
-            }
-
-            mc::cat_deref(cmt_base, _, pk) => {
-                match pk {
-                    mc::OwnedPtr => {
-                        // R-Deref-Send-Pointer
-                        //
-                        // When we borrow the interior of an owned pointer, we
-                        // cannot permit the base to be mutated, because that
-                        // would cause the unique pointer to be freed.
-                        //
-                        // Eventually we should make these non-special and
-                        // just rely on Deref<T> implementation.
-                        let result = self.restrict(cmt_base);
-                        self.extend(result, &cmt, LpDeref(pk))
-                    }
-                    mc::Implicit(bk, lt) | mc::BorrowedPtr(bk, lt) => {
-                        // R-Deref-[Mut-]Borrowed
-                        if !self.bccx.is_subregion_of(self.loan_region, lt) {
-                            self.bccx.report(
-                                BckError {
-                                    span: self.span,
-                                    cause: self.cause,
-                                    cmt: cmt_base,
-                                    code: err_borrowed_pointer_too_short(
-                                        self.loan_region, lt)});
-                            return Safe;
-                        }
-
-                        match bk {
-                            ty::ImmBorrow => Safe,
-                            ty::MutBorrow | ty::UniqueImmBorrow => {
-                                // R-Deref-Mut-Borrowed
-                                //
-                                // The referent can be aliased after the
-                                // references lifetime ends (by a newly-unfrozen
-                                // borrow).
-                                let result = self.restrict(cmt_base);
-                                self.extend(result, &cmt, LpDeref(pk))
-                            }
-                        }
-                    }
-                    // Borrowck is not relevant for unsafe pointers
-                    mc::UnsafePtr(..) => Safe
-                }
-            }
-        }
-    }
-
-    fn extend(&self,
-              result: RestrictionResult<'tcx>,
-              cmt: &mc::cmt<'tcx>,
-              elem: LoanPathElem) -> RestrictionResult<'tcx> {
-        match result {
-            Safe => Safe,
-            SafeIf(base_lp, mut base_vec) => {
-                let v = LpExtend(base_lp, cmt.mutbl, elem);
-                let lp = Rc::new(LoanPath::new(v, cmt.ty));
-                base_vec.push(lp.clone());
-                SafeIf(lp, base_vec)
-            }
-        }
-    }
-}
diff --git a/src/librustc/middle/borrowck/graphviz.rs b/src/librustc/middle/borrowck/graphviz.rs
deleted file mode 100644 (file)
index 32fa5f8..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! This module provides linkage between rustc::middle::graph and
-//! libgraphviz traits, specialized to attaching borrowck analysis
-//! data to rendered labels.
-
-pub use self::Variant::*;
-
-/// For clarity, rename the graphviz crate locally to dot.
-use graphviz as dot;
-pub use middle::cfg::graphviz::{Node, Edge};
-use middle::cfg::graphviz as cfg_dot;
-
-use middle::borrowck;
-use middle::borrowck::{BorrowckCtxt, LoanPath};
-use middle::cfg::{CFGIndex};
-use middle::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit};
-use middle::dataflow;
-
-use std::rc::Rc;
-
-#[deriving(Show)]
-pub enum Variant {
-    Loans,
-    Moves,
-    Assigns,
-}
-
-impl Copy for Variant {}
-
-impl Variant {
-    pub fn short_name(&self) -> &'static str {
-        match *self {
-            Loans   => "loans",
-            Moves   => "moves",
-            Assigns => "assigns",
-        }
-    }
-}
-
-pub struct DataflowLabeller<'a, 'tcx: 'a> {
-    pub inner: cfg_dot::LabelledCFG<'a, 'tcx>,
-    pub variants: Vec<Variant>,
-    pub borrowck_ctxt: &'a BorrowckCtxt<'a, 'tcx>,
-    pub analysis_data: &'a borrowck::AnalysisData<'a, 'tcx>,
-}
-
-impl<'a, 'tcx> DataflowLabeller<'a, 'tcx> {
-    fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String {
-        let id = n.val1().data.id;
-        debug!("dataflow_for({}, id={}) {}", e, id, self.variants);
-        let mut sets = "".to_string();
-        let mut seen_one = false;
-        for &variant in self.variants.iter() {
-            if seen_one { sets.push_str(" "); } else { seen_one = true; }
-            sets.push_str(variant.short_name());
-            sets.push_str(": ");
-            sets.push_str(self.dataflow_for_variant(e, n, variant).as_slice());
-        }
-        sets
-    }
-
-    fn dataflow_for_variant(&self, e: EntryOrExit, n: &Node, v: Variant) -> String {
-        let cfgidx = n.val0();
-        match v {
-            Loans   => self.dataflow_loans_for(e, cfgidx),
-            Moves   => self.dataflow_moves_for(e, cfgidx),
-            Assigns => self.dataflow_assigns_for(e, cfgidx),
-        }
-    }
-
-    fn build_set<O:DataFlowOperator>(&self,
-                                     e: EntryOrExit,
-                                     cfgidx: CFGIndex,
-                                     dfcx: &DataFlowContext<'a, 'tcx, O>,
-                                     to_lp: |uint| -> Rc<LoanPath<'tcx>>) -> String {
-        let mut saw_some = false;
-        let mut set = "{".to_string();
-        dfcx.each_bit_for_node(e, cfgidx, |index| {
-            let lp = to_lp(index);
-            if saw_some {
-                set.push_str(", ");
-            }
-            let loan_str = self.borrowck_ctxt.loan_path_to_string(&*lp);
-            set.push_str(loan_str.as_slice());
-            saw_some = true;
-            true
-        });
-        set.push_str("}");
-        set
-    }
-
-    fn dataflow_loans_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
-        let dfcx = &self.analysis_data.loans;
-        let loan_index_to_path = |loan_index| {
-            let all_loans = &self.analysis_data.all_loans;
-            all_loans[loan_index].loan_path()
-        };
-        self.build_set(e, cfgidx, dfcx, loan_index_to_path)
-    }
-
-    fn dataflow_moves_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
-        let dfcx = &self.analysis_data.move_data.dfcx_moves;
-        let move_index_to_path = |move_index| {
-            let move_data = &self.analysis_data.move_data.move_data;
-            let moves = move_data.moves.borrow();
-            let the_move = &(*moves)[move_index];
-            move_data.path_loan_path(the_move.path)
-        };
-        self.build_set(e, cfgidx, dfcx, move_index_to_path)
-    }
-
-    fn dataflow_assigns_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
-        let dfcx = &self.analysis_data.move_data.dfcx_assign;
-        let assign_index_to_path = |assign_index| {
-            let move_data = &self.analysis_data.move_data.move_data;
-            let assignments = move_data.var_assignments.borrow();
-            let assignment = &(*assignments)[assign_index];
-            move_data.path_loan_path(assignment.path)
-        };
-        self.build_set(e, cfgidx, dfcx, assign_index_to_path)
-    }
-}
-
-impl<'a, 'tcx> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 'tcx> {
-    fn graph_id(&'a self) -> dot::Id<'a> { self.inner.graph_id() }
-    fn node_id(&'a self, n: &Node<'a>) -> dot::Id<'a> { self.inner.node_id(n) }
-    fn node_label(&'a self, n: &Node<'a>) -> dot::LabelText<'a> {
-        let prefix = self.dataflow_for(dataflow::Entry, n);
-        let suffix = self.dataflow_for(dataflow::Exit, n);
-        let inner_label = self.inner.node_label(n);
-        inner_label
-            .prefix_line(dot::LabelStr(prefix.into_cow()))
-            .suffix_line(dot::LabelStr(suffix.into_cow()))
-    }
-    fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) }
-}
-
-impl<'a, 'tcx> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 'tcx> {
-    fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() }
-    fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() }
-    fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) }
-    fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) }
-}
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
deleted file mode 100644 (file)
index e90de1b..0000000
+++ /dev/null
@@ -1,1126 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! See doc.rs for a thorough explanation of the borrow checker
-
-#![allow(non_camel_case_types)]
-
-pub use self::LoanPathKind::*;
-pub use self::LoanPathElem::*;
-pub use self::bckerr_code::*;
-pub use self::AliasableViolationKind::*;
-pub use self::MovedValueUseKind::*;
-
-use middle::cfg;
-use middle::dataflow::DataFlowContext;
-use middle::dataflow::BitwiseOperator;
-use middle::dataflow::DataFlowOperator;
-use middle::expr_use_visitor as euv;
-use middle::mem_categorization as mc;
-use middle::region;
-use middle::ty::{mod, ParameterEnvironment, Ty};
-use util::ppaux::{note_and_explain_region, Repr, UserString};
-
-use std::rc::Rc;
-use std::string::String;
-use syntax::ast;
-use syntax::ast_map;
-use syntax::ast_map::blocks::{FnLikeNode, FnParts};
-use syntax::ast_util;
-use syntax::codemap::Span;
-use syntax::parse::token;
-use syntax::visit;
-use syntax::visit::{Visitor, FnKind};
-use syntax::ast::{FnDecl, Block, NodeId};
-
-macro_rules! if_ok(
-    ($inp: expr) => (
-        match $inp {
-            Ok(v) => { v }
-            Err(e) => { return Err(e); }
-        }
-    )
-)
-
-pub mod doc;
-
-pub mod check_loans;
-
-pub mod gather_loans;
-
-pub mod graphviz;
-
-pub mod move_data;
-
-#[deriving(Clone)]
-pub struct LoanDataFlowOperator;
-
-impl Copy for LoanDataFlowOperator {}
-
-pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
-
-impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
-    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
-                b: &'v Block, s: Span, id: ast::NodeId) {
-        borrowck_fn(self, fk, fd, b, s, id);
-    }
-
-    fn visit_item(&mut self, item: &ast::Item) {
-        borrowck_item(self, item);
-    }
-}
-
-pub fn check_crate(tcx: &ty::ctxt) {
-    let mut bccx = BorrowckCtxt {
-        tcx: tcx,
-        stats: BorrowStats {
-            loaned_paths_same: 0,
-            loaned_paths_imm: 0,
-            stable_paths: 0,
-            guaranteed_paths: 0
-        }
-    };
-
-    visit::walk_crate(&mut bccx, tcx.map.krate());
-
-    if tcx.sess.borrowck_stats() {
-        println!("--- borrowck stats ---");
-        println!("paths requiring guarantees: {}",
-                 bccx.stats.guaranteed_paths);
-        println!("paths requiring loans     : {}",
-                 make_stat(&bccx, bccx.stats.loaned_paths_same));
-        println!("paths requiring imm loans : {}",
-                 make_stat(&bccx, bccx.stats.loaned_paths_imm));
-        println!("stable paths              : {}",
-                 make_stat(&bccx, bccx.stats.stable_paths));
-    }
-
-    fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> String {
-        let total = bccx.stats.guaranteed_paths as f64;
-        let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
-        format!("{} ({:.0}%)", stat, perc)
-    }
-}
-
-fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
-    // Gather loans for items. Note that we don't need
-    // to check loans for single expressions. The check
-    // loan step is intended for things that have a data
-    // flow dependent conditions.
-    match item.node {
-        ast::ItemStatic(_, _, ref ex) |
-        ast::ItemConst(_, ref ex) => {
-            gather_loans::gather_loans_in_static_initializer(this, &**ex);
-        }
-        _ => {
-            visit::walk_item(this, item);
-        }
-    }
-}
-
-/// Collection of conclusions determined via borrow checker analyses.
-pub struct AnalysisData<'a, 'tcx: 'a> {
-    pub all_loans: Vec<Loan<'tcx>>,
-    pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
-    pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
-}
-
-fn borrowck_fn(this: &mut BorrowckCtxt,
-               fk: FnKind,
-               decl: &ast::FnDecl,
-               body: &ast::Block,
-               sp: Span,
-               id: ast::NodeId) {
-    debug!("borrowck_fn(id={})", id);
-    let cfg = cfg::CFG::new(this.tcx, body);
-    let AnalysisData { all_loans,
-                       loans: loan_dfcx,
-                       move_data:flowed_moves } =
-        build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
-
-    move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
-                                                    this.tcx, sp, id);
-
-    check_loans::check_loans(this,
-                             &loan_dfcx,
-                             flowed_moves,
-                             all_loans.as_slice(),
-                             id,
-                             decl,
-                             body);
-
-    visit::walk_fn(this, fk, decl, body, sp);
-}
-
-fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
-                                          fk: FnKind,
-                                          decl: &ast::FnDecl,
-                                          cfg: &cfg::CFG,
-                                          body: &ast::Block,
-                                          sp: Span,
-                                          id: ast::NodeId) -> AnalysisData<'a, 'tcx> {
-    // Check the body of fn items.
-    let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
-    let (all_loans, move_data) =
-        gather_loans::gather_loans_in_fn(this, id, decl, body);
-
-    let mut loan_dfcx =
-        DataFlowContext::new(this.tcx,
-                             "borrowck",
-                             Some(decl),
-                             cfg,
-                             LoanDataFlowOperator,
-                             id_range,
-                             all_loans.len());
-    for (loan_idx, loan) in all_loans.iter().enumerate() {
-        loan_dfcx.add_gen(loan.gen_scope.node_id(), loan_idx);
-        loan_dfcx.add_kill(loan.kill_scope.node_id(), loan_idx);
-    }
-    loan_dfcx.add_kills_from_flow_exits(cfg);
-    loan_dfcx.propagate(cfg, body);
-
-    let flowed_moves = move_data::FlowedMoveData::new(move_data,
-                                                      this.tcx,
-                                                      cfg,
-                                                      id_range,
-                                                      decl,
-                                                      body);
-
-    AnalysisData { all_loans: all_loans,
-                   loans: loan_dfcx,
-                   move_data:flowed_moves }
-}
-
-/// This and a `ty::ctxt` is all you need to run the dataflow analyses
-/// used in the borrow checker.
-pub struct FnPartsWithCFG<'a> {
-    pub fn_parts: FnParts<'a>,
-    pub cfg:  &'a cfg::CFG,
-}
-
-impl<'a> FnPartsWithCFG<'a> {
-    pub fn from_fn_like(f: &'a FnLikeNode,
-                        g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
-        FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
-    }
-}
-
-/// Accessor for introspective clients inspecting `AnalysisData` and
-/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
-pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
-    tcx: &'a ty::ctxt<'tcx>,
-    input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) {
-
-    let mut bccx = BorrowckCtxt {
-        tcx: tcx,
-        stats: BorrowStats {
-            loaned_paths_same: 0,
-            loaned_paths_imm: 0,
-            stable_paths: 0,
-            guaranteed_paths: 0
-        }
-    };
-
-    let p = input.fn_parts;
-
-    let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
-                                                     p.kind,
-                                                     &*p.decl,
-                                                     input.cfg,
-                                                     &*p.body,
-                                                     p.span,
-                                                     p.id);
-
-    (bccx, dataflow_data)
-}
-
-// ----------------------------------------------------------------------
-// Type definitions
-
-pub struct BorrowckCtxt<'a, 'tcx: 'a> {
-    tcx: &'a ty::ctxt<'tcx>,
-
-    // Statistics:
-    stats: BorrowStats
-}
-
-struct BorrowStats {
-    loaned_paths_same: uint,
-    loaned_paths_imm: uint,
-    stable_paths: uint,
-    guaranteed_paths: uint
-}
-
-pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
-
-///////////////////////////////////////////////////////////////////////////
-// Loans and loan paths
-
-/// Record of a loan that was issued.
-pub struct Loan<'tcx> {
-    index: uint,
-    loan_path: Rc<LoanPath<'tcx>>,
-    kind: ty::BorrowKind,
-    restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
-
-    /// gen_scope indicates where loan is introduced. Typically the
-    /// loan is introduced at the point of the borrow, but in some
-    /// cases, notably method arguments, the loan may be introduced
-    /// only later, once it comes into scope.  See also
-    /// `GatherLoanCtxt::compute_gen_scope`.
-    gen_scope: region::CodeExtent,
-
-    /// kill_scope indicates when the loan goes out of scope.  This is
-    /// either when the lifetime expires or when the local variable
-    /// which roots the loan-path goes out of scope, whichever happens
-    /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
-    kill_scope: region::CodeExtent,
-    span: Span,
-    cause: euv::LoanCause,
-}
-
-impl<'tcx> Loan<'tcx> {
-    pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
-        self.loan_path.clone()
-    }
-}
-
-#[deriving(Eq, Hash, Show)]
-pub struct LoanPath<'tcx> {
-    kind: LoanPathKind<'tcx>,
-    ty: ty::Ty<'tcx>,
-}
-
-impl<'tcx> LoanPath<'tcx> {
-    pub fn eq_debug(&self, that: &LoanPath<'tcx>, tcx: &ty::ctxt<'tcx>) -> bool {
-        let r = self.kind == that.kind;
-        if r && self.ty != that.ty {
-            panic!("eq variants ineq types: {} == {}, {} != {}",
-                   self.repr(tcx), that.repr(tcx),
-                   self.ty.repr(tcx), that.ty.repr(tcx));
-        }
-        r
-    }
-}
-
-impl<'tcx> PartialEq for LoanPath<'tcx> {
-    fn eq(&self, that: &LoanPath<'tcx>) -> bool {
-        let r = self.kind == that.kind;
-        debug_assert!(self.ty == that.ty || !r,
-                      "Somehow loan paths are equal though their tys are not.");
-        r
-    }
-}
-
-#[deriving(PartialEq, Eq, Hash, Show)]
-pub enum LoanPathKind<'tcx> {
-    LpVar(ast::NodeId),                         // `x` in doc.rs
-    LpUpvar(ty::UpvarId),                       // `x` captured by-value into closure
-    LpDowncast(Rc<LoanPath<'tcx>>, ast::DefId), // `x` downcast to particular enum variant
-    LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
-}
-
-impl<'tcx> LoanPath<'tcx> {
-    fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
-        LoanPath { kind: kind, ty: ty }
-    }
-
-    fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
-}
-
-// FIXME (pnkfelix): See discussion here
-// https://github.com/pnkfelix/rust/commit/
-//     b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
-static DOWNCAST_PRINTED_OPERATOR : &'static str = " as ";
-
-#[deriving(PartialEq, Eq, Hash, Show)]
-pub enum LoanPathElem {
-    LpDeref(mc::PointerKind),    // `*LV` in doc.rs
-    LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
-}
-
-impl Copy for LoanPathElem {}
-
-pub fn closure_to_block(closure_id: ast::NodeId,
-                        tcx: &ty::ctxt) -> ast::NodeId {
-    match tcx.map.get(closure_id) {
-        ast_map::NodeExpr(expr) => match expr.node {
-            ast::ExprProc(_, ref block) |
-            ast::ExprClosure(_, _, _, ref block) => {
-                block.id
-            }
-            _ => {
-                panic!("encountered non-closure id: {}", closure_id)
-            }
-        },
-        _ => panic!("encountered non-expr id: {}", closure_id)
-    }
-}
-
-impl<'tcx> LoanPath<'tcx> {
-    pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
-        match self.kind {
-            LpVar(local_id) => tcx.region_maps.var_scope(local_id),
-            LpUpvar(upvar_id) => {
-                let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
-                region::CodeExtent::from_node_id(block_id)
-            }
-            LpDowncast(ref base, _) |
-            LpExtend(ref base, _, _) => base.kill_scope(tcx),
-        }
-    }
-
-    fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
-        match (&self.kind, &other.kind) {
-            (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
-                if id == id2 {
-                    base.has_fork(&**base2)
-                } else {
-                    true
-                },
-            (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
-            (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
-            _ => false,
-        }
-    }
-
-    fn depth(&self) -> uint {
-        match self.kind {
-            LpExtend(ref base, _, LpDeref(_)) => base.depth(),
-            LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
-            _ => 0,
-        }
-    }
-
-    fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
-        match (&self.kind, &other.kind) {
-            (&LpExtend(ref base, a, LpInterior(id)),
-             &LpExtend(ref base2, _, LpInterior(id2))) => {
-                if id == id2 {
-                    base.common(&**base2).map(|x| {
-                        let xd = x.depth();
-                        if base.depth() == xd && base2.depth() == xd {
-                            assert_eq!(base.ty, base2.ty);
-                            assert_eq!(self.ty, other.ty);
-                            LoanPath {
-                                kind: LpExtend(Rc::new(x), a, LpInterior(id)),
-                                ty: self.ty,
-                            }
-                        } else {
-                            x
-                        }
-                    })
-                } else {
-                    base.common(&**base2)
-                }
-            }
-            (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
-            (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
-            (&LpVar(id), &LpVar(id2)) => {
-                if id == id2 {
-                    assert_eq!(self.ty, other.ty);
-                    Some(LoanPath { kind: LpVar(id), ty: self.ty })
-                } else {
-                    None
-                }
-            }
-            (&LpUpvar(id), &LpUpvar(id2)) => {
-                if id == id2 {
-                    assert_eq!(self.ty, other.ty);
-                    Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        }
-    }
-}
-
-pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
-    //! Computes the `LoanPath` (if any) for a `cmt`.
-    //! Note that this logic is somewhat duplicated in
-    //! the method `compute()` found in `gather_loans::restrictions`,
-    //! which allows it to share common loan path pieces as it
-    //! traverses the CMT.
-
-    let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
-
-    match cmt.cat {
-        mc::cat_rvalue(..) |
-        mc::cat_static_item => {
-            None
-        }
-
-        mc::cat_local(id) => {
-            Some(new_lp(LpVar(id)))
-        }
-
-        mc::cat_upvar(mc::Upvar { id, .. }) => {
-            Some(new_lp(LpUpvar(id)))
-        }
-
-        mc::cat_deref(ref cmt_base, _, pk) => {
-            opt_loan_path(cmt_base).map(|lp| {
-                new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
-            })
-        }
-
-        mc::cat_interior(ref cmt_base, ik) => {
-            opt_loan_path(cmt_base).map(|lp| {
-                new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
-            })
-        }
-
-        mc::cat_downcast(ref cmt_base, variant_def_id) =>
-            opt_loan_path(cmt_base)
-            .map(|lp| {
-                new_lp(LpDowncast(lp, variant_def_id))
-            }),
-
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Errors
-
-// Errors that can occur
-#[deriving(PartialEq)]
-#[allow(missing_copy_implementations)]
-pub enum bckerr_code {
-    err_mutbl,
-    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
-    err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
-}
-
-// Combination of an error code and the categorization of the expression
-// that caused it
-#[deriving(PartialEq)]
-pub struct BckError<'tcx> {
-    span: Span,
-    cause: euv::LoanCause,
-    cmt: mc::cmt<'tcx>,
-    code: bckerr_code
-}
-
-pub enum AliasableViolationKind {
-    MutabilityViolation,
-    BorrowViolation(euv::LoanCause)
-}
-
-impl Copy for AliasableViolationKind {}
-
-#[deriving(Show)]
-pub enum MovedValueUseKind {
-    MovedInUse,
-    MovedInCapture,
-}
-
-impl Copy for MovedValueUseKind {}
-
-///////////////////////////////////////////////////////////////////////////
-// Misc
-
-impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
-    pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
-                           -> bool {
-        self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
-    }
-
-    pub fn mc(&self) -> mc::MemCategorizationContext<'a, ty::ctxt<'tcx>> {
-        mc::MemCategorizationContext::new(self.tcx)
-    }
-
-    pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt<'tcx> {
-        match self.mc().cat_expr(expr) {
-            Ok(c) => c,
-            Err(()) => {
-                self.tcx.sess.span_bug(expr.span, "error in mem categorization");
-            }
-        }
-    }
-
-    pub fn report(&self, err: BckError<'tcx>) {
-        self.span_err(
-            err.span,
-            self.bckerr_to_string(&err).as_slice());
-        self.note_and_explain_bckerr(err);
-    }
-
-    pub fn report_use_of_moved_value(&self,
-                                     use_span: Span,
-                                     use_kind: MovedValueUseKind,
-                                     lp: &LoanPath<'tcx>,
-                                     the_move: &move_data::Move,
-                                     moved_lp: &LoanPath<'tcx>,
-                                     param_env: &ParameterEnvironment<'tcx>) {
-        let verb = match use_kind {
-            MovedInUse => "use",
-            MovedInCapture => "capture",
-        };
-
-        let (ol, moved_lp_msg) = match the_move.kind {
-            move_data::Declared => {
-                self.tcx.sess.span_err(
-                    use_span,
-                    format!("{} of possibly uninitialized variable: `{}`",
-                            verb,
-                            self.loan_path_to_string(lp)).as_slice());
-                (self.loan_path_to_string(moved_lp),
-                 String::new())
-            }
-            _ => {
-                // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
-                // normally generate a rather confusing message:
-                //
-                //     error: use of moved value: `x.b`
-                //     note: `x.a` moved here...
-                //
-                // What we want to do instead is get the 'common ancestor' of the two moves and
-                // use that for most of the message instead, giving is something like this:
-                //
-                //     error: use of moved value: `x`
-                //     note: `x` moved here (through moving `x.a`)...
-
-                let common = moved_lp.common(lp);
-                let has_common = common.is_some();
-                let has_fork = moved_lp.has_fork(lp);
-                let (nl, ol, moved_lp_msg) =
-                    if has_fork && has_common {
-                        let nl = self.loan_path_to_string(&common.unwrap());
-                        let ol = nl.clone();
-                        let moved_lp_msg = format!(" (through moving `{}`)",
-                                                   self.loan_path_to_string(moved_lp));
-                        (nl, ol, moved_lp_msg)
-                    } else {
-                        (self.loan_path_to_string(lp),
-                         self.loan_path_to_string(moved_lp),
-                         String::new())
-                    };
-
-                let partial = moved_lp.depth() > lp.depth();
-                let msg = if !has_fork && partial { "partially " }
-                          else if has_fork && !has_common { "collaterally "}
-                          else { "" };
-                self.tcx.sess.span_err(
-                    use_span,
-                    format!("{} of {}moved value: `{}`",
-                            verb,
-                            msg,
-                            nl).as_slice());
-                (ol, moved_lp_msg)
-            }
-        };
-
-        match the_move.kind {
-            move_data::Declared => {}
-
-            move_data::MoveExpr => {
-                let (expr_ty, expr_span) = match self.tcx
-                                                     .map
-                                                     .find(the_move.id) {
-                    Some(ast_map::NodeExpr(expr)) => {
-                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
-                    }
-                    r => {
-                        self.tcx.sess.bug(format!("MoveExpr({}) maps to \
-                                                   {}, not Expr",
-                                                  the_move.id,
-                                                  r).as_slice())
-                    }
-                };
-                let (suggestion, _) = move_suggestion(self.tcx, param_env, expr_ty,
-                        ("moved by default", ""));
-                self.tcx.sess.span_note(
-                    expr_span,
-                    format!("`{}` moved here{} because it has type `{}`, which is {}",
-                            ol,
-                            moved_lp_msg,
-                            expr_ty.user_string(self.tcx),
-                            suggestion).as_slice());
-            }
-
-            move_data::MovePat => {
-                let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
-                let span = self.tcx.map.span(the_move.id);
-                self.tcx.sess.span_note(span,
-                    format!("`{}` moved here{} because it has type `{}`, \
-                             which is moved by default",
-                            ol,
-                            moved_lp_msg,
-                            pat_ty.user_string(self.tcx)).as_slice());
-                self.tcx.sess.span_help(span,
-                    "use `ref` to override");
-            }
-
-            move_data::Captured => {
-                let (expr_ty, expr_span) = match self.tcx
-                                                     .map
-                                                     .find(the_move.id) {
-                    Some(ast_map::NodeExpr(expr)) => {
-                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
-                    }
-                    r => {
-                        self.tcx.sess.bug(format!("Captured({}) maps to \
-                                                   {}, not Expr",
-                                                  the_move.id,
-                                                  r).as_slice())
-                    }
-                };
-                let (suggestion, help) = move_suggestion(self.tcx,
-                                                         param_env,
-                                                         expr_ty,
-                        ("moved by default", "make a copy and \
-                         capture that instead to override"));
-                self.tcx.sess.span_note(
-                    expr_span,
-                    format!("`{}` moved into closure environment here{} because it \
-                            has type `{}`, which is {}",
-                            ol,
-                            moved_lp_msg,
-                            expr_ty.user_string(self.tcx),
-                            suggestion).as_slice());
-                self.tcx.sess.span_help(expr_span, help);
-            }
-        }
-
-        fn move_suggestion<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                 param_env: &ty::ParameterEnvironment<'tcx>,
-                                 ty: Ty<'tcx>,
-                                 default_msgs: (&'static str, &'static str))
-                                 -> (&'static str, &'static str) {
-            match ty.sty {
-                ty::ty_closure(box ty::ClosureTy {
-                        store: ty::RegionTraitStore(..),
-                        ..
-                    }) =>
-                    ("a non-copyable stack closure",
-                     "capture it in a new closure, e.g. `|x| f(x)`, to override"),
-                _ if ty::type_moves_by_default(tcx, ty, param_env) =>
-                    ("non-copyable",
-                     "perhaps you meant to use `clone()`?"),
-                _ => default_msgs,
-            }
-        }
-    }
-
-    pub fn report_reassigned_immutable_variable(&self,
-                                                span: Span,
-                                                lp: &LoanPath<'tcx>,
-                                                assign:
-                                                &move_data::Assignment) {
-        self.tcx.sess.span_err(
-            span,
-            format!("re-assignment of immutable variable `{}`",
-                    self.loan_path_to_string(lp)).as_slice());
-        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
-    }
-
-    pub fn span_err(&self, s: Span, m: &str) {
-        self.tcx.sess.span_err(s, m);
-    }
-
-    pub fn span_note(&self, s: Span, m: &str) {
-        self.tcx.sess.span_note(s, m);
-    }
-
-    pub fn span_end_note(&self, s: Span, m: &str) {
-        self.tcx.sess.span_end_note(s, m);
-    }
-
-    pub fn span_help(&self, s: Span, m: &str) {
-        self.tcx.sess.span_help(s, m);
-    }
-
-    pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
-        match err.code {
-            err_mutbl => {
-                let descr = match err.cmt.note {
-                    mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
-                        self.cmt_to_string(&*err.cmt)
-                    }
-                    _ => match opt_loan_path(&err.cmt) {
-                        None => {
-                            format!("{} {}",
-                                    err.cmt.mutbl.to_user_str(),
-                                    self.cmt_to_string(&*err.cmt))
-                        }
-                        Some(lp) => {
-                            format!("{} {} `{}`",
-                                    err.cmt.mutbl.to_user_str(),
-                                    self.cmt_to_string(&*err.cmt),
-                                    self.loan_path_to_string(&*lp))
-                        }
-                    }
-                };
-
-                match err.cause {
-                    euv::ClosureCapture(_) => {
-                        format!("closure cannot assign to {}", descr)
-                    }
-                    euv::OverloadedOperator |
-                    euv::AddrOf |
-                    euv::RefBinding |
-                    euv::AutoRef |
-                    euv::ForLoop |
-                    euv::MatchDiscriminant => {
-                        format!("cannot borrow {} as mutable", descr)
-                    }
-                    euv::ClosureInvocation => {
-                        self.tcx.sess.span_bug(err.span,
-                            "err_mutbl with a closure invocation");
-                    }
-                }
-            }
-            err_out_of_scope(..) => {
-                let msg = match opt_loan_path(&err.cmt) {
-                    None => "borrowed value".to_string(),
-                    Some(lp) => {
-                        format!("`{}`", self.loan_path_to_string(&*lp))
-                    }
-                };
-                format!("{} does not live long enough", msg)
-            }
-            err_borrowed_pointer_too_short(..) => {
-                let descr = match opt_loan_path(&err.cmt) {
-                    Some(lp) => {
-                        format!("`{}`", self.loan_path_to_string(&*lp))
-                    }
-                    None => self.cmt_to_string(&*err.cmt),
-                };
-
-                format!("lifetime of {} is too short to guarantee \
-                                its contents can be safely reborrowed",
-                               descr)
-            }
-        }
-    }
-
-    pub fn report_aliasability_violation(&self,
-                                         span: Span,
-                                         kind: AliasableViolationKind,
-                                         cause: mc::AliasableReason) {
-        let mut is_closure = false;
-        let prefix = match kind {
-            MutabilityViolation => {
-                "cannot assign to data"
-            }
-            BorrowViolation(euv::ClosureCapture(_)) => {
-                // I don't think we can get aliasability violations
-                // with closure captures, so no need to come up with a
-                // good error message. The reason this cannot happen
-                // is because we only capture local variables in
-                // closures, and those are never aliasable.
-                self.tcx.sess.span_bug(
-                    span,
-                    "aliasability violation with closure");
-            }
-            BorrowViolation(euv::OverloadedOperator) |
-            BorrowViolation(euv::AddrOf) |
-            BorrowViolation(euv::AutoRef) |
-            BorrowViolation(euv::RefBinding) |
-            BorrowViolation(euv::MatchDiscriminant) => {
-                "cannot borrow data mutably"
-            }
-
-            BorrowViolation(euv::ClosureInvocation) => {
-                is_closure = true;
-                "closure invocation"
-            }
-
-            BorrowViolation(euv::ForLoop) => {
-                "`for` loop"
-            }
-        };
-
-        match cause {
-            mc::AliasableOther => {
-                self.tcx.sess.span_err(
-                    span,
-                    format!("{} in an aliasable location",
-                             prefix).as_slice());
-            }
-            mc::AliasableClosure(id) => {
-                self.tcx.sess.span_err(span,
-                                       format!("{} in a captured outer \
-                                               variable in an `Fn` closure", prefix).as_slice());
-                span_help!(self.tcx.sess, self.tcx.map.span(id),
-                           "consider changing this closure to take self by mutable reference");
-            }
-            mc::AliasableStatic(..) |
-            mc::AliasableStaticMut(..) => {
-                self.tcx.sess.span_err(
-                    span,
-                    format!("{} in a static location", prefix).as_slice());
-            }
-            mc::AliasableBorrowed => {
-                self.tcx.sess.span_err(
-                    span,
-                    format!("{} in a `&` reference", prefix).as_slice());
-            }
-        }
-
-        if is_closure {
-            self.tcx.sess.span_help(
-                span,
-                "closures behind references must be called via `&mut`");
-        }
-    }
-
-    pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
-        let code = err.code;
-        match code {
-            err_mutbl(..) => {
-                match err.cmt.note {
-                    mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
-                        // If this is an `Fn` closure, it simply can't mutate upvars.
-                        // If it's an `FnMut` closure, the original variable was declared immutable.
-                        // We need to determine which is the case here.
-                        let kind = match err.cmt.upvar().unwrap().cat {
-                            mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
-                            _ => unreachable!()
-                        };
-                        if kind == ty::FnUnboxedClosureKind {
-                            self.tcx.sess.span_help(
-                                self.tcx.map.span(upvar_id.closure_expr_id),
-                                "consider changing this closure to take \
-                                 self by mutable reference");
-                        }
-                    }
-                    _ => {}
-                }
-            }
-
-            err_out_of_scope(super_scope, sub_scope) => {
-                note_and_explain_region(
-                    self.tcx,
-                    "reference must be valid for ",
-                    sub_scope,
-                    "...");
-                let suggestion = if is_statement_scope(self.tcx, super_scope) {
-                    Some("consider using a `let` binding to increase its lifetime")
-                } else {
-                    None
-                };
-                let span = note_and_explain_region(
-                    self.tcx,
-                    "...but borrowed value is only valid for ",
-                    super_scope,
-                    "");
-                match (span, suggestion) {
-                    (_, None) => {},
-                    (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
-                    (None, Some(msg)) => self.tcx.sess.help(msg),
-                }
-            }
-
-            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
-                let descr = match opt_loan_path(&err.cmt) {
-                    Some(lp) => {
-                        format!("`{}`", self.loan_path_to_string(&*lp))
-                    }
-                    None => self.cmt_to_string(&*err.cmt),
-                };
-                note_and_explain_region(
-                    self.tcx,
-                    format!("{} would have to be valid for ",
-                            descr).as_slice(),
-                    loan_scope,
-                    "...");
-                note_and_explain_region(
-                    self.tcx,
-                    format!("...but {} is only valid for ", descr).as_slice(),
-                    ptr_scope,
-                    "");
-            }
-        }
-    }
-
-    pub fn append_loan_path_to_string(&self,
-                                      loan_path: &LoanPath<'tcx>,
-                                      out: &mut String) {
-        match loan_path.kind {
-            LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
-            LpVar(id) => {
-                out.push_str(ty::local_var_name_str(self.tcx, id).get());
-            }
-
-            LpDowncast(ref lp_base, variant_def_id) => {
-                out.push('(');
-                self.append_loan_path_to_string(&**lp_base, out);
-                out.push_str(DOWNCAST_PRINTED_OPERATOR);
-                out.push_str(ty::item_path_str(self.tcx, variant_def_id).as_slice());
-                out.push(')');
-            }
-
-
-            LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
-                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
-                match fname {
-                    mc::NamedField(fname) => {
-                        out.push('.');
-                        out.push_str(token::get_name(fname).get());
-                    }
-                    mc::PositionalField(idx) => {
-                        out.push('.');
-                        out.push_str(idx.to_string().as_slice());
-                    }
-                }
-            }
-
-            LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
-                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
-                out.push_str("[..]");
-            }
-
-            LpExtend(ref lp_base, _, LpDeref(_)) => {
-                out.push('*');
-                self.append_loan_path_to_string(&**lp_base, out);
-            }
-        }
-    }
-
-    pub fn append_autoderefd_loan_path_to_string(&self,
-                                                 loan_path: &LoanPath<'tcx>,
-                                                 out: &mut String) {
-        match loan_path.kind {
-            LpExtend(ref lp_base, _, LpDeref(_)) => {
-                // For a path like `(*x).f` or `(*x)[3]`, autoderef
-                // rules would normally allow users to omit the `*x`.
-                // So just serialize such paths to `x.f` or x[3]` respectively.
-                self.append_autoderefd_loan_path_to_string(&**lp_base, out)
-            }
-
-            LpDowncast(ref lp_base, variant_def_id) => {
-                out.push('(');
-                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
-                out.push(':');
-                out.push_str(ty::item_path_str(self.tcx, variant_def_id).as_slice());
-                out.push(')');
-            }
-
-            LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
-                self.append_loan_path_to_string(loan_path, out)
-            }
-        }
-    }
-
-    pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
-        let mut result = String::new();
-        self.append_loan_path_to_string(loan_path, &mut result);
-        result
-    }
-
-    pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
-        self.mc().cmt_to_string(cmt)
-    }
-}
-
-fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
-     match region {
-         ty::ReScope(scope) => {
-             match tcx.map.find(scope.node_id()) {
-                 Some(ast_map::NodeStmt(_)) => true,
-                 _ => false
-             }
-         }
-         _ => false
-     }
-}
-
-impl BitwiseOperator for LoanDataFlowOperator {
-    #[inline]
-    fn join(&self, succ: uint, pred: uint) -> uint {
-        succ | pred // loans from both preds are in scope
-    }
-}
-
-impl DataFlowOperator for LoanDataFlowOperator {
-    #[inline]
-    fn initial_value(&self) -> bool {
-        false // no loans in scope by default
-    }
-}
-
-impl<'tcx> Repr<'tcx> for Loan<'tcx> {
-    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
-        format!("Loan_{}({}, {}, {}-{}, {})",
-                 self.index,
-                 self.loan_path.repr(tcx),
-                 self.kind,
-                 self.gen_scope,
-                 self.kill_scope,
-                 self.restricted_paths.repr(tcx))
-    }
-}
-
-impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
-    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
-        match self.kind {
-            LpVar(id) => {
-                format!("$({})", tcx.map.node_to_string(id))
-            }
-
-            LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
-                let s = tcx.map.node_to_string(var_id);
-                format!("$({} captured by id={})", s, closure_expr_id)
-            }
-
-            LpDowncast(ref lp, variant_def_id) => {
-                let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
-                    ty::item_path_str(tcx, variant_def_id)
-                } else {
-                    variant_def_id.repr(tcx)
-                };
-                format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
-            }
-
-            LpExtend(ref lp, _, LpDeref(_)) => {
-                format!("{}.*", lp.repr(tcx))
-            }
-
-            LpExtend(ref lp, _, LpInterior(ref interior)) => {
-                format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
-            }
-        }
-    }
-}
-
-impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
-    fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
-        match self.kind {
-            LpVar(id) => {
-                format!("$({})", tcx.map.node_to_user_string(id))
-            }
-
-            LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
-                let s = tcx.map.node_to_user_string(var_id);
-                format!("$({} captured by closure)", s)
-            }
-
-            LpDowncast(ref lp, variant_def_id) => {
-                let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
-                    ty::item_path_str(tcx, variant_def_id)
-                } else {
-                    variant_def_id.repr(tcx)
-                };
-                format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
-            }
-
-            LpExtend(ref lp, _, LpDeref(_)) => {
-                format!("{}.*", lp.user_string(tcx))
-            }
-
-            LpExtend(ref lp, _, LpInterior(ref interior)) => {
-                format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))
-            }
-        }
-    }
-}
diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs
deleted file mode 100644 (file)
index 3bb6145..0000000
+++ /dev/null
@@ -1,802 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Data structures used for tracking moves. Please see the extensive
-//! comments in the section "Moves and initialization" in `doc.rs`.
-
-pub use self::MoveKind::*;
-
-use std::cell::RefCell;
-use std::rc::Rc;
-use std::uint;
-use middle::borrowck::*;
-use middle::borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
-use middle::borrowck::LoanPathElem::{LpInterior};
-use middle::cfg;
-use middle::dataflow::DataFlowContext;
-use middle::dataflow::BitwiseOperator;
-use middle::dataflow::DataFlowOperator;
-use middle::expr_use_visitor as euv;
-use middle::mem_categorization as mc;
-use middle::ty;
-use syntax::ast;
-use syntax::ast_util;
-use syntax::codemap::Span;
-use util::nodemap::{FnvHashMap, NodeSet};
-use util::ppaux::Repr;
-
-#[path="fragments.rs"]
-pub mod fragments;
-
-pub struct MoveData<'tcx> {
-    /// Move paths. See section "Move paths" in `doc.rs`.
-    pub paths: RefCell<Vec<MovePath<'tcx>>>,
-
-    /// Cache of loan path to move path index, for easy lookup.
-    pub path_map: RefCell<FnvHashMap<Rc<LoanPath<'tcx>>, MovePathIndex>>,
-
-    /// Each move or uninitialized variable gets an entry here.
-    pub moves: RefCell<Vec<Move>>,
-
-    /// Assignments to a variable, like `x = foo`. These are assigned
-    /// bits for dataflow, since we must track them to ensure that
-    /// immutable variables are assigned at most once along each path.
-    pub var_assignments: RefCell<Vec<Assignment>>,
-
-    /// Assignments to a path, like `x.f = foo`. These are not
-    /// assigned dataflow bits, but we track them because they still
-    /// kill move bits.
-    pub path_assignments: RefCell<Vec<Assignment>>,
-
-    /// Enum variant matched within a pattern on some match arm, like
-    /// `SomeStruct{ f: Variant1(x, y) } => ...`
-    pub variant_matches: RefCell<Vec<VariantMatch>>,
-
-    /// Assignments to a variable or path, like `x = foo`, but not `x += foo`.
-    pub assignee_ids: RefCell<NodeSet>,
-
-    /// Path-fragments from moves in to or out of parts of structured data.
-    pub fragments: RefCell<fragments::FragmentSets>,
-}
-
-pub struct FlowedMoveData<'a, 'tcx: 'a> {
-    pub move_data: MoveData<'tcx>,
-
-    pub dfcx_moves: MoveDataFlow<'a, 'tcx>,
-
-    // We could (and maybe should, for efficiency) combine both move
-    // and assign data flow into one, but this way it's easier to
-    // distinguish the bits that correspond to moves and assignments.
-    pub dfcx_assign: AssignDataFlow<'a, 'tcx>
-}
-
-/// Index into `MoveData.paths`, used like a pointer
-#[deriving(PartialEq, Eq, PartialOrd, Ord, Show)]
-pub struct MovePathIndex(uint);
-
-impl Copy for MovePathIndex {}
-
-impl MovePathIndex {
-    fn get(&self) -> uint {
-        let MovePathIndex(v) = *self; v
-    }
-}
-
-impl Clone for MovePathIndex {
-    fn clone(&self) -> MovePathIndex {
-        MovePathIndex(self.get())
-    }
-}
-
-#[allow(non_upper_case_globals)]
-static InvalidMovePathIndex: MovePathIndex =
-    MovePathIndex(uint::MAX);
-
-/// Index into `MoveData.moves`, used like a pointer
-#[deriving(PartialEq)]
-pub struct MoveIndex(uint);
-
-impl Copy for MoveIndex {}
-
-impl MoveIndex {
-    fn get(&self) -> uint {
-        let MoveIndex(v) = *self; v
-    }
-}
-
-#[allow(non_upper_case_globals)]
-static InvalidMoveIndex: MoveIndex =
-    MoveIndex(uint::MAX);
-
-pub struct MovePath<'tcx> {
-    /// Loan path corresponding to this move path
-    pub loan_path: Rc<LoanPath<'tcx>>,
-
-    /// Parent pointer, `InvalidMovePathIndex` if root
-    pub parent: MovePathIndex,
-
-    /// Head of linked list of moves to this path,
-    /// `InvalidMoveIndex` if not moved
-    pub first_move: MoveIndex,
-
-    /// First node in linked list of children, `InvalidMovePathIndex` if leaf
-    pub first_child: MovePathIndex,
-
-    /// Next node in linked list of parent's children (siblings),
-    /// `InvalidMovePathIndex` if none.
-    pub next_sibling: MovePathIndex,
-}
-
-#[deriving(PartialEq, Show)]
-pub enum MoveKind {
-    Declared,   // When declared, variables start out "moved".
-    MoveExpr,   // Expression or binding that moves a variable
-    MovePat,    // By-move binding
-    Captured    // Closure creation that moves a value
-}
-
-impl Copy for MoveKind {}
-
-pub struct Move {
-    /// Path being moved.
-    pub path: MovePathIndex,
-
-    /// id of node that is doing the move.
-    pub id: ast::NodeId,
-
-    /// Kind of move, for error messages.
-    pub kind: MoveKind,
-
-    /// Next node in linked list of moves from `path`, or `InvalidMoveIndex`
-    pub next_move: MoveIndex
-}
-
-impl Copy for Move {}
-
-pub struct Assignment {
-    /// Path being assigned.
-    pub path: MovePathIndex,
-
-    /// id where assignment occurs
-    pub id: ast::NodeId,
-
-    /// span of node where assignment occurs
-    pub span: Span,
-}
-
-impl Copy for Assignment {}
-
-pub struct VariantMatch {
-    /// downcast to the variant.
-    pub path: MovePathIndex,
-
-    /// path being downcast to the variant.
-    pub base_path: MovePathIndex,
-
-    /// id where variant's pattern occurs
-    pub id: ast::NodeId,
-
-    /// says if variant established by move (and why), by copy, or by borrow.
-    pub mode: euv::MatchMode
-}
-
-impl Copy for VariantMatch {}
-
-#[deriving(Clone)]
-pub struct MoveDataFlowOperator;
-
-impl Copy for MoveDataFlowOperator {}
-
-pub type MoveDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, MoveDataFlowOperator>;
-
-#[deriving(Clone)]
-pub struct AssignDataFlowOperator;
-
-impl Copy for AssignDataFlowOperator {}
-
-pub type AssignDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, AssignDataFlowOperator>;
-
-fn loan_path_is_precise(loan_path: &LoanPath) -> bool {
-    match loan_path.kind {
-        LpVar(_) | LpUpvar(_) => {
-            true
-        }
-        LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
-            // Paths involving element accesses do not refer to a unique
-            // location, as there is no accurate tracking of the indices.
-            false
-        }
-        LpDowncast(ref lp_base, _) |
-        LpExtend(ref lp_base, _, _) => {
-            loan_path_is_precise(&**lp_base)
-        }
-    }
-}
-
-impl Move {
-    pub fn to_string<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
-        format!("Move{} path: {}, id: {}, kind: {} {}",
-                "{",
-                move_data.path_loan_path(self.path).repr(tcx),
-                self.id,
-                self.kind,
-                "}")
-    }
-}
-
-impl Assignment {
-    pub fn to_string<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
-        format!("Assignment{} path: {}, id: {} {}",
-                "{",
-                move_data.path_loan_path(self.path).repr(tcx),
-                self.id,
-                "}")
-    }
-}
-
-impl VariantMatch {
-    pub fn to_string<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
-        format!("VariantMatch{} path: {}, id: {} {}",
-                "{",
-                move_data.path_loan_path(self.path).repr(tcx),
-                self.id,
-                "}")
-    }
-}
-
-impl<'tcx> MoveData<'tcx> {
-    pub fn new() -> MoveData<'tcx> {
-        MoveData {
-            paths: RefCell::new(Vec::new()),
-            path_map: RefCell::new(FnvHashMap::new()),
-            moves: RefCell::new(Vec::new()),
-            path_assignments: RefCell::new(Vec::new()),
-            var_assignments: RefCell::new(Vec::new()),
-            variant_matches: RefCell::new(Vec::new()),
-            assignee_ids: RefCell::new(NodeSet::new()),
-            fragments: RefCell::new(fragments::FragmentSets::new()),
-        }
-    }
-
-    pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath<'tcx>> {
-        (*self.paths.borrow())[index.get()].loan_path.clone()
-    }
-
-    fn path_parent(&self, index: MovePathIndex) -> MovePathIndex {
-        (*self.paths.borrow())[index.get()].parent
-    }
-
-    fn path_first_move(&self, index: MovePathIndex) -> MoveIndex {
-        (*self.paths.borrow())[index.get()].first_move
-    }
-
-    /// Returns the index of first child, or `InvalidMovePathIndex` if
-    /// `index` is leaf.
-    fn path_first_child(&self, index: MovePathIndex) -> MovePathIndex {
-        (*self.paths.borrow())[index.get()].first_child
-    }
-
-    fn path_next_sibling(&self, index: MovePathIndex) -> MovePathIndex {
-        (*self.paths.borrow())[index.get()].next_sibling
-    }
-
-    fn set_path_first_move(&self,
-                           index: MovePathIndex,
-                           first_move: MoveIndex) {
-        (*self.paths.borrow_mut())[index.get()].first_move = first_move
-    }
-
-    fn set_path_first_child(&self,
-                            index: MovePathIndex,
-                            first_child: MovePathIndex) {
-        (*self.paths.borrow_mut())[index.get()].first_child = first_child
-    }
-
-    fn move_next_move(&self, index: MoveIndex) -> MoveIndex {
-        //! Type safe indexing operator
-        (*self.moves.borrow())[index.get()].next_move
-    }
-
-    fn is_var_path(&self, index: MovePathIndex) -> bool {
-        //! True if `index` refers to a variable
-        self.path_parent(index) == InvalidMovePathIndex
-    }
-
-    /// Returns the existing move path index for `lp`, if any, and otherwise adds a new index for
-    /// `lp` and any of its base paths that do not yet have an index.
-    pub fn move_path(&self,
-                     tcx: &ty::ctxt<'tcx>,
-                     lp: Rc<LoanPath<'tcx>>) -> MovePathIndex {
-        match self.path_map.borrow().get(&lp) {
-            Some(&index) => {
-                return index;
-            }
-            None => {}
-        }
-
-        let index = match lp.kind {
-            LpVar(..) | LpUpvar(..) => {
-                let index = MovePathIndex(self.paths.borrow().len());
-
-                self.paths.borrow_mut().push(MovePath {
-                    loan_path: lp.clone(),
-                    parent: InvalidMovePathIndex,
-                    first_move: InvalidMoveIndex,
-                    first_child: InvalidMovePathIndex,
-                    next_sibling: InvalidMovePathIndex,
-                });
-
-                index
-            }
-
-            LpDowncast(ref base, _) |
-            LpExtend(ref base, _, _) => {
-                let parent_index = self.move_path(tcx, base.clone());
-
-                let index = MovePathIndex(self.paths.borrow().len());
-
-                let next_sibling = self.path_first_child(parent_index);
-                self.set_path_first_child(parent_index, index);
-
-                self.paths.borrow_mut().push(MovePath {
-                    loan_path: lp.clone(),
-                    parent: parent_index,
-                    first_move: InvalidMoveIndex,
-                    first_child: InvalidMovePathIndex,
-                    next_sibling: next_sibling,
-                });
-
-                index
-            }
-        };
-
-        debug!("move_path(lp={}, index={})",
-               lp.repr(tcx),
-               index);
-
-        assert_eq!(index.get(), self.paths.borrow().len() - 1);
-        self.path_map.borrow_mut().insert(lp, index);
-        return index;
-    }
-
-    fn existing_move_path(&self, lp: &Rc<LoanPath<'tcx>>)
-                          -> Option<MovePathIndex> {
-        self.path_map.borrow().get(lp).cloned()
-    }
-
-    fn existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>)
-                           -> Vec<MovePathIndex> {
-        let mut result = vec!();
-        self.add_existing_base_paths(lp, &mut result);
-        result
-    }
-
-    /// Adds any existing move path indices for `lp` and any base paths of `lp` to `result`, but
-    /// does not add new move paths
-    fn add_existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>,
-                               result: &mut Vec<MovePathIndex>) {
-        match self.path_map.borrow().get(lp).cloned() {
-            Some(index) => {
-                self.each_base_path(index, |p| {
-                    result.push(p);
-                    true
-                });
-            }
-            None => {
-                match lp.kind {
-                    LpVar(..) | LpUpvar(..) => { }
-                    LpDowncast(ref b, _) |
-                    LpExtend(ref b, _, _) => {
-                        self.add_existing_base_paths(b, result);
-                    }
-                }
-            }
-        }
-
-    }
-
-    /// Adds a new move entry for a move of `lp` that occurs at location `id` with kind `kind`.
-    pub fn add_move(&self,
-                    tcx: &ty::ctxt<'tcx>,
-                    lp: Rc<LoanPath<'tcx>>,
-                    id: ast::NodeId,
-                    kind: MoveKind) {
-        debug!("add_move(lp={}, id={}, kind={})",
-               lp.repr(tcx),
-               id,
-               kind);
-
-        let path_index = self.move_path(tcx, lp.clone());
-        let move_index = MoveIndex(self.moves.borrow().len());
-
-        self.fragments.borrow_mut().add_move(path_index);
-
-        let next_move = self.path_first_move(path_index);
-        self.set_path_first_move(path_index, move_index);
-
-        self.moves.borrow_mut().push(Move {
-            path: path_index,
-            id: id,
-            kind: kind,
-            next_move: next_move
-        });
-    }
-
-    /// Adds a new record for an assignment to `lp` that occurs at location `id` with the given
-    /// `span`.
-    pub fn add_assignment(&self,
-                          tcx: &ty::ctxt<'tcx>,
-                          lp: Rc<LoanPath<'tcx>>,
-                          assign_id: ast::NodeId,
-                          span: Span,
-                          assignee_id: ast::NodeId,
-                          mode: euv::MutateMode) {
-        debug!("add_assignment(lp={}, assign_id={}, assignee_id={}",
-               lp.repr(tcx), assign_id, assignee_id);
-
-        let path_index = self.move_path(tcx, lp.clone());
-
-        self.fragments.borrow_mut().add_assignment(path_index);
-
-        match mode {
-            euv::Init | euv::JustWrite => {
-                self.assignee_ids.borrow_mut().insert(assignee_id);
-            }
-            euv::WriteAndRead => { }
-        }
-
-        let assignment = Assignment {
-            path: path_index,
-            id: assign_id,
-            span: span,
-        };
-
-        if self.is_var_path(path_index) {
-            debug!("add_assignment[var](lp={}, assignment={}, path_index={})",
-                   lp.repr(tcx), self.var_assignments.borrow().len(), path_index);
-
-            self.var_assignments.borrow_mut().push(assignment);
-        } else {
-            debug!("add_assignment[path](lp={}, path_index={})",
-                   lp.repr(tcx), path_index);
-
-            self.path_assignments.borrow_mut().push(assignment);
-        }
-    }
-
-    /// Adds a new record for a match of `base_lp`, downcast to
-    /// variant `lp`, that occurs at location `pattern_id`.  (One
-    /// should be able to recover the span info from the
-    /// `pattern_id` and the ast_map, I think.)
-    pub fn add_variant_match(&self,
-                             tcx: &ty::ctxt<'tcx>,
-                             lp: Rc<LoanPath<'tcx>>,
-                             pattern_id: ast::NodeId,
-                             base_lp: Rc<LoanPath<'tcx>>,
-                             mode: euv::MatchMode) {
-        debug!("add_variant_match(lp={}, pattern_id={})",
-               lp.repr(tcx), pattern_id);
-
-        let path_index = self.move_path(tcx, lp.clone());
-        let base_path_index = self.move_path(tcx, base_lp.clone());
-
-        self.fragments.borrow_mut().add_assignment(path_index);
-
-        let variant_match = VariantMatch {
-            path: path_index,
-            base_path: base_path_index,
-            id: pattern_id,
-            mode: mode,
-        };
-
-        self.variant_matches.borrow_mut().push(variant_match);
-    }
-
-    fn fixup_fragment_sets(&self, tcx: &ty::ctxt<'tcx>) {
-        fragments::fixup_fragment_sets(self, tcx)
-    }
-
-    /// Adds the gen/kills for the various moves and
-    /// assignments into the provided data flow contexts.
-    /// Moves are generated by moves and killed by assignments and
-    /// scoping. Assignments are generated by assignment to variables and
-    /// killed by scoping. See `doc.rs` for more details.
-    fn add_gen_kills(&self,
-                     tcx: &ty::ctxt<'tcx>,
-                     dfcx_moves: &mut MoveDataFlow,
-                     dfcx_assign: &mut AssignDataFlow) {
-        for (i, the_move) in self.moves.borrow().iter().enumerate() {
-            dfcx_moves.add_gen(the_move.id, i);
-        }
-
-        for (i, assignment) in self.var_assignments.borrow().iter().enumerate() {
-            dfcx_assign.add_gen(assignment.id, i);
-            self.kill_moves(assignment.path, assignment.id, dfcx_moves);
-        }
-
-        for assignment in self.path_assignments.borrow().iter() {
-            self.kill_moves(assignment.path, assignment.id, dfcx_moves);
-        }
-
-        // Kill all moves related to a variable `x` when
-        // it goes out of scope:
-        for path in self.paths.borrow().iter() {
-            match path.loan_path.kind {
-                LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
-                    let kill_scope = path.loan_path.kill_scope(tcx);
-                    let path = self.path_map.borrow()[path.loan_path];
-                    self.kill_moves(path, kill_scope.node_id(), dfcx_moves);
-                }
-                LpExtend(..) => {}
-            }
-        }
-
-        // Kill all assignments when the variable goes out of scope:
-        for (assignment_index, assignment) in
-                self.var_assignments.borrow().iter().enumerate() {
-            let lp = self.path_loan_path(assignment.path);
-            match lp.kind {
-                LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
-                    let kill_scope = lp.kill_scope(tcx);
-                    dfcx_assign.add_kill(kill_scope.node_id(), assignment_index);
-                }
-                LpExtend(..) => {
-                    tcx.sess.bug("var assignment for non var path");
-                }
-            }
-        }
-    }
-
-    fn each_base_path(&self, index: MovePathIndex, f: |MovePathIndex| -> bool)
-                      -> bool {
-        let mut p = index;
-        while p != InvalidMovePathIndex {
-            if !f(p) {
-                return false;
-            }
-            p = self.path_parent(p);
-        }
-        return true;
-    }
-
-    fn each_extending_path(&self,
-                           index: MovePathIndex,
-                           f: |MovePathIndex| -> bool)
-                           -> bool {
-        if !f(index) {
-            return false;
-        }
-
-        let mut p = self.path_first_child(index);
-        while p != InvalidMovePathIndex {
-            if !self.each_extending_path(p, |x| f(x)) {
-                return false;
-            }
-            p = self.path_next_sibling(p);
-        }
-
-        return true;
-    }
-
-    fn each_applicable_move(&self,
-                            index0: MovePathIndex,
-                            f: |MoveIndex| -> bool)
-                            -> bool {
-        let mut ret = true;
-        self.each_extending_path(index0, |index| {
-            let mut p = self.path_first_move(index);
-            while p != InvalidMoveIndex {
-                if !f(p) {
-                    ret = false;
-                    break;
-                }
-                p = self.move_next_move(p);
-            }
-            ret
-        });
-        ret
-    }
-
-    fn kill_moves(&self,
-                  path: MovePathIndex,
-                  kill_id: ast::NodeId,
-                  dfcx_moves: &mut MoveDataFlow) {
-        // We can only perform kills for paths that refer to a unique location,
-        // since otherwise we may kill a move from one location with an
-        // assignment referring to another location.
-
-        let loan_path = self.path_loan_path(path);
-        if loan_path_is_precise(&*loan_path) {
-            self.each_applicable_move(path, |move_index| {
-                dfcx_moves.add_kill(kill_id, move_index.get());
-                true
-            });
-        }
-    }
-}
-
-impl<'a, 'tcx> FlowedMoveData<'a, 'tcx> {
-    pub fn new(move_data: MoveData<'tcx>,
-               tcx: &'a ty::ctxt<'tcx>,
-               cfg: &cfg::CFG,
-               id_range: ast_util::IdRange,
-               decl: &ast::FnDecl,
-               body: &ast::Block)
-               -> FlowedMoveData<'a, 'tcx> {
-        let mut dfcx_moves =
-            DataFlowContext::new(tcx,
-                                 "flowed_move_data_moves",
-                                 Some(decl),
-                                 cfg,
-                                 MoveDataFlowOperator,
-                                 id_range,
-                                 move_data.moves.borrow().len());
-        let mut dfcx_assign =
-            DataFlowContext::new(tcx,
-                                 "flowed_move_data_assigns",
-                                 Some(decl),
-                                 cfg,
-                                 AssignDataFlowOperator,
-                                 id_range,
-                                 move_data.var_assignments.borrow().len());
-
-        move_data.fixup_fragment_sets(tcx);
-
-        move_data.add_gen_kills(tcx,
-                                &mut dfcx_moves,
-                                &mut dfcx_assign);
-
-        dfcx_moves.add_kills_from_flow_exits(cfg);
-        dfcx_assign.add_kills_from_flow_exits(cfg);
-
-        dfcx_moves.propagate(cfg, body);
-        dfcx_assign.propagate(cfg, body);
-
-        FlowedMoveData {
-            move_data: move_data,
-            dfcx_moves: dfcx_moves,
-            dfcx_assign: dfcx_assign,
-        }
-    }
-
-    pub fn kind_of_move_of_path(&self,
-                                id: ast::NodeId,
-                                loan_path: &Rc<LoanPath<'tcx>>)
-                                -> Option<MoveKind> {
-        //! Returns the kind of a move of `loan_path` by `id`, if one exists.
-
-        let mut ret = None;
-        for loan_path_index in self.move_data.path_map.borrow().get(&*loan_path).iter() {
-            self.dfcx_moves.each_gen_bit(id, |move_index| {
-                let the_move = self.move_data.moves.borrow();
-                let the_move = (*the_move)[move_index];
-                if the_move.path == **loan_path_index {
-                    ret = Some(the_move.kind);
-                    false
-                } else {
-                    true
-                }
-            });
-        }
-        ret
-    }
-
-    /// Iterates through each move of `loan_path` (or some base path of `loan_path`) that *may*
-    /// have occurred on entry to `id` without an intervening assignment. In other words, any moves
-    /// that would invalidate a reference to `loan_path` at location `id`.
-    pub fn each_move_of(&self,
-                        id: ast::NodeId,
-                        loan_path: &Rc<LoanPath<'tcx>>,
-                        f: |&Move, &LoanPath<'tcx>| -> bool)
-                        -> bool {
-        // Bad scenarios:
-        //
-        // 1. Move of `a.b.c`, use of `a.b.c`
-        // 2. Move of `a.b.c`, use of `a.b.c.d`
-        // 3. Move of `a.b.c`, use of `a` or `a.b`
-        //
-        // OK scenario:
-        //
-        // 4. move of `a.b.c`, use of `a.b.d`
-
-        let base_indices = self.move_data.existing_base_paths(loan_path);
-        if base_indices.is_empty() {
-            return true;
-        }
-
-        let opt_loan_path_index = self.move_data.existing_move_path(loan_path);
-
-        let mut ret = true;
-
-        self.dfcx_moves.each_bit_on_entry(id, |index| {
-            let the_move = self.move_data.moves.borrow();
-            let the_move = &(*the_move)[index];
-            let moved_path = the_move.path;
-            if base_indices.iter().any(|x| x == &moved_path) {
-                // Scenario 1 or 2: `loan_path` or some base path of
-                // `loan_path` was moved.
-                if !f(the_move, &*self.move_data.path_loan_path(moved_path)) {
-                    ret = false;
-                }
-            } else {
-                for &loan_path_index in opt_loan_path_index.iter() {
-                    let cont = self.move_data.each_base_path(moved_path, |p| {
-                        if p == loan_path_index {
-                            // Scenario 3: some extension of `loan_path`
-                            // was moved
-                            f(the_move,
-                              &*self.move_data.path_loan_path(moved_path))
-                        } else {
-                            true
-                        }
-                    });
-                    if !cont { ret = false; break }
-                }
-            }
-            ret
-        })
-    }
-
-    /// Iterates through every assignment to `loan_path` that may have occurred on entry to `id`.
-    /// `loan_path` must be a single variable.
-    pub fn each_assignment_of(&self,
-                              id: ast::NodeId,
-                              loan_path: &Rc<LoanPath<'tcx>>,
-                              f: |&Assignment| -> bool)
-                              -> bool {
-        let loan_path_index = {
-            match self.move_data.existing_move_path(loan_path) {
-                Some(i) => i,
-                None => {
-                    // if there were any assignments, it'd have an index
-                    return true;
-                }
-            }
-        };
-
-        self.dfcx_assign.each_bit_on_entry(id, |index| {
-            let assignment = self.move_data.var_assignments.borrow();
-            let assignment = &(*assignment)[index];
-            if assignment.path == loan_path_index && !f(assignment) {
-                false
-            } else {
-                true
-            }
-        })
-    }
-}
-
-impl BitwiseOperator for MoveDataFlowOperator {
-    #[inline]
-    fn join(&self, succ: uint, pred: uint) -> uint {
-        succ | pred // moves from both preds are in scope
-    }
-}
-
-impl DataFlowOperator for MoveDataFlowOperator {
-    #[inline]
-    fn initial_value(&self) -> bool {
-        false // no loans in scope by default
-    }
-}
-
-impl BitwiseOperator for AssignDataFlowOperator {
-    #[inline]
-    fn join(&self, succ: uint, pred: uint) -> uint {
-        succ | pred // moves from both preds are in scope
-    }
-}
-
-impl DataFlowOperator for AssignDataFlowOperator {
-    #[inline]
-    fn initial_value(&self) -> bool {
-        false // no assignments in scope by default
-    }
-}
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
new file mode 100644 (file)
index 0000000..4244cec
--- /dev/null
@@ -0,0 +1,967 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ----------------------------------------------------------------------
+// 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
+// 4. moves do not affect things loaned out in any way
+use self::UseError::*;
+
+use borrowck::*;
+use borrowck::LoanPathElem::*;
+use borrowck::LoanPathKind::*;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::region;
+use rustc::middle::ty;
+use rustc::util::ppaux::Repr;
+use syntax::ast;
+use syntax::codemap::Span;
+
+use std::rc::Rc;
+
+// FIXME (#16118): These functions are intended to allow the borrow checker to
+// be less precise in its handling of Box while still allowing moves out of a
+// Box. They should be removed when OwnedPtr is removed from LoanPath.
+
+fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
+    //! Returns the base of the leftmost dereference of an OwnedPtr in
+    //! `loan_path`. If there is no dereference of an OwnedPtr in `loan_path`,
+    //! then it just returns `loan_path` itself.
+
+    return match helper(loan_path) {
+        Some(new_loan_path) => new_loan_path,
+        None => loan_path.clone()
+    };
+
+    fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
+        match loan_path.kind {
+            LpVar(_) | LpUpvar(_) => None,
+            LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
+                match helper(&**lp_base) {
+                    v @ Some(_) => v,
+                    None => Some(&**lp_base)
+                }
+            }
+            LpDowncast(ref lp_base, _) |
+            LpExtend(ref lp_base, _, _) => helper(&**lp_base)
+        }
+    }
+}
+
+fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
+    //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
+    //! a &LoanPath.
+
+    return match helper(loan_path) {
+        Some(new_loan_path) => new_loan_path,
+        None => loan_path.clone()
+    };
+
+    fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
+        match loan_path.kind {
+            LpVar(_) | LpUpvar(_) => None,
+            LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
+                match helper(lp_base) {
+                    v @ Some(_) => v,
+                    None => Some(lp_base.clone())
+                }
+            }
+            LpDowncast(ref lp_base, _) |
+            LpExtend(ref lp_base, _, _) => helper(lp_base)
+        }
+    }
+}
+
+struct CheckLoanCtxt<'a, 'tcx: 'a> {
+    bccx: &'a BorrowckCtxt<'a, 'tcx>,
+    dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
+    move_data: move_data::FlowedMoveData<'a, 'tcx>,
+    all_loans: &'a [Loan<'tcx>],
+    param_env: &'a ty::ParameterEnvironment<'tcx>,
+}
+
+impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
+    fn consume(&mut self,
+               consume_id: ast::NodeId,
+               consume_span: Span,
+               cmt: mc::cmt<'tcx>,
+               mode: euv::ConsumeMode) {
+        debug!("consume(consume_id={}, cmt={}, mode={})",
+               consume_id, cmt.repr(self.tcx()), mode);
+
+        self.consume_common(consume_id, consume_span, cmt, mode);
+    }
+
+    fn matched_pat(&mut self,
+                   _matched_pat: &ast::Pat,
+                   _cmt: mc::cmt,
+                   _mode: euv::MatchMode) { }
+
+    fn consume_pat(&mut self,
+                   consume_pat: &ast::Pat,
+                   cmt: mc::cmt<'tcx>,
+                   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);
+    }
+
+    fn borrow(&mut self,
+              borrow_id: ast::NodeId,
+              borrow_span: Span,
+              cmt: mc::cmt<'tcx>,
+              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(region::CodeExtent::from_node_id(borrow_id));
+    }
+
+    fn mutate(&mut self,
+              assignment_id: ast::NodeId,
+              assignment_span: Span,
+              assignee_cmt: mc::cmt<'tcx>,
+              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);
+    }
+
+    fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
+}
+
+pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                     dfcx_loans: &LoanDataFlow<'b, 'tcx>,
+                                     move_data: move_data::FlowedMoveData<'c, 'tcx>,
+                                     all_loans: &[Loan<'tcx>],
+                                     fn_id: ast::NodeId,
+                                     decl: &ast::FnDecl,
+                                     body: &ast::Block) {
+    debug!("check_loans(body id={})", body.id);
+
+    let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
+
+    let mut clcx = CheckLoanCtxt {
+        bccx: bccx,
+        dfcx_loans: dfcx_loans,
+        move_data: move_data,
+        all_loans: all_loans,
+        param_env: &param_env,
+    };
+
+    {
+        let mut euv = euv::ExprUseVisitor::new(&mut clcx,
+                                               bccx.tcx,
+                                               param_env.clone());
+        euv.walk_fn(decl, body);
+    }
+}
+
+#[deriving(PartialEq)]
+enum UseError<'tcx> {
+    UseOk,
+    UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
+}
+
+fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
+                           borrow_kind2: ty::BorrowKind)
+                           -> bool {
+    borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
+}
+
+impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
+    pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
+
+    pub fn each_issued_loan(&self, scope: region::CodeExtent, op: |&Loan<'tcx>| -> bool)
+                            -> bool {
+        //! Iterates over each loan that has been issued
+        //! on entrance to `scope`, 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.
+
+        self.dfcx_loans.each_bit_on_entry(scope.node_id(), |loan_index| {
+            let loan = &self.all_loans[loan_index];
+            op(loan)
+        })
+    }
+
+    pub fn each_in_scope_loan(&self,
+                              scope: region::CodeExtent,
+                              op: |&Loan<'tcx>| -> bool)
+                              -> bool {
+        //! Like `each_issued_loan()`, but only considers loans that are
+        //! currently in scope.
+
+        let tcx = self.tcx();
+        self.each_issued_loan(scope, |loan| {
+            if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
+                op(loan)
+            } else {
+                true
+            }
+        })
+    }
+
+    fn each_in_scope_loan_affecting_path(&self,
+                                         scope: region::CodeExtent,
+                                         loan_path: &LoanPath<'tcx>,
+                                         op: |&Loan<'tcx>| -> 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 loan_path = owned_ptr_base_path(loan_path);
+        let cont = self.each_in_scope_loan(scope, |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.kind {
+                LpVar(_) | LpUpvar(_) => {
+                    break;
+                }
+                LpDowncast(ref lp_base, _) |
+                LpExtend(ref lp_base, _, _) => {
+                    loan_path = &**lp_base;
+                }
+            }
+
+            let cont = self.each_in_scope_loan(scope, |loan| {
+                if *loan.loan_path == *loan_path {
+                    op(loan)
+                } else {
+                    true
+                }
+            });
+
+            if !cont {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    pub fn loans_generated_by(&self, scope: region::CodeExtent) -> Vec<uint> {
+        //! Returns a vector of the loans that are generated as
+        //! we enter `scope`.
+
+        let mut result = Vec::new();
+        self.dfcx_loans.each_gen_bit(scope.node_id(), |loan_index| {
+            result.push(loan_index);
+            true
+        });
+        return result;
+    }
+
+    pub fn check_for_conflicting_loans(&self, scope: region::CodeExtent) {
+        //! Checks to see whether any of the loans that are issued
+        //! on entrance to `scope` conflict with loans that have already been
+        //! issued when we enter `scope` (for example, we do not
+        //! permit two `&mut` borrows of the same variable).
+        //!
+        //! (Note that some loans can be *issued* without necessarily
+        //! taking effect yet.)
+
+        debug!("check_for_conflicting_loans(scope={})", scope);
+
+        let new_loan_indices = self.loans_generated_by(scope);
+        debug!("new_loan_indices = {}", new_loan_indices);
+
+        self.each_issued_loan(scope, |issued_loan| {
+            for &new_loan_index in new_loan_indices.iter() {
+                let new_loan = &self.all_loans[new_loan_index];
+                self.report_error_if_loans_conflict(issued_loan, new_loan);
+            }
+            true
+        });
+
+        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];
+                self.report_error_if_loans_conflict(old_loan, new_loan);
+            }
+        }
+    }
+
+    pub fn report_error_if_loans_conflict(&self,
+                                          old_loan: &Loan<'tcx>,
+                                          new_loan: &Loan<'tcx>) {
+        //! Checks whether `old_loan` and `new_loan` can safely be issued
+        //! simultaneously.
+
+        debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
+               old_loan.repr(self.tcx()),
+               new_loan.repr(self.tcx()));
+
+        // Should only be called for loans that are in scope at the same time.
+        assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
+                                                        new_loan.kill_scope));
+
+        self.report_error_if_loan_conflicts_with_restriction(
+            old_loan, new_loan, old_loan, new_loan) &&
+        self.report_error_if_loan_conflicts_with_restriction(
+            new_loan, old_loan, old_loan, new_loan);
+    }
+
+    pub fn report_error_if_loan_conflicts_with_restriction(&self,
+                                                           loan1: &Loan<'tcx>,
+                                                           loan2: &Loan<'tcx>,
+                                                           old_loan: &Loan<'tcx>,
+                                                           new_loan: &Loan<'tcx>)
+                                                           -> bool {
+        //! Checks whether the restrictions introduced by `loan1` would
+        //! prohibit `loan2`. Returns false if an error is reported.
+
+        debug!("report_error_if_loan_conflicts_with_restriction(\
+                loan1={}, loan2={})",
+               loan1.repr(self.tcx()),
+               loan2.repr(self.tcx()));
+
+        if compatible_borrow_kinds(loan1.kind, loan2.kind) {
+            return true;
+        }
+
+        let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
+        for restr_path in loan1.restricted_paths.iter() {
+            if *restr_path != loan2_base_path { continue; }
+
+            // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
+            // normally generate a rather confusing message (in this case, for multiple mutable
+            // borrows):
+            //
+            //     error: cannot borrow `x.b` as mutable more than once at a time
+            //     note: previous borrow of `x.a` occurs here; the mutable borrow prevents
+            //     subsequent moves, borrows, or modification of `x.a` until the borrow ends
+            //
+            // What we want to do instead is get the 'common ancestor' of the two borrow paths and
+            // use that for most of the message instead, giving is something like this:
+            //
+            //     error: cannot borrow `x` as mutable more than once at a time
+            //     note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
+            //     borrow prevents subsequent moves, borrows, or modification of `x` until the
+            //     borrow ends
+
+            let common = new_loan.loan_path.common(&*old_loan.loan_path);
+            let (nl, ol, new_loan_msg, old_loan_msg) =
+                if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
+                    let nl = self.bccx.loan_path_to_string(&common.unwrap());
+                    let ol = nl.clone();
+                    let new_loan_msg = format!(" (here through borrowing `{}`)",
+                                               self.bccx.loan_path_to_string(
+                                                   &*new_loan.loan_path));
+                    let old_loan_msg = format!(" (through borrowing `{}`)",
+                                               self.bccx.loan_path_to_string(
+                                                   &*old_loan.loan_path));
+                    (nl, ol, new_loan_msg, old_loan_msg)
+                } else {
+                    (self.bccx.loan_path_to_string(&*new_loan.loan_path),
+                     self.bccx.loan_path_to_string(&*old_loan.loan_path),
+                     String::new(), String::new())
+                };
+
+            let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
+                "it".to_string()
+            } else {
+                format!("`{}`", ol)
+            };
+
+            match (new_loan.kind, old_loan.kind) {
+                (ty::MutBorrow, ty::MutBorrow) => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        format!("cannot borrow `{}`{} as mutable \
+                                more than once at a time",
+                                nl, new_loan_msg).as_slice())
+                }
+
+                (ty::UniqueImmBorrow, _) => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        format!("closure requires unique access to `{}` \
+                                but {} is already borrowed{}",
+                                nl, ol_pronoun, old_loan_msg).as_slice());
+                }
+
+                (_, ty::UniqueImmBorrow) => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        format!("cannot borrow `{}`{} as {} because \
+                                previous closure requires unique access",
+                                nl, new_loan_msg, new_loan.kind.to_user_str()).as_slice());
+                }
+
+                (_, _) => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        format!("cannot borrow `{}`{} as {} because \
+                                {} is also borrowed as {}{}",
+                                nl,
+                                new_loan_msg,
+                                new_loan.kind.to_user_str(),
+                                ol_pronoun,
+                                old_loan.kind.to_user_str(),
+                                old_loan_msg).as_slice());
+                }
+            }
+
+            match new_loan.cause {
+                euv::ClosureCapture(span) => {
+                    self.bccx.span_note(
+                        span,
+                        format!("borrow occurs due to use of `{}` in closure",
+                                nl).as_slice());
+                }
+                _ => { }
+            }
+
+            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",
+                            ol)
+                }
+
+                ty::ImmBorrow => {
+                    format!("the immutable borrow prevents subsequent \
+                            moves or mutable borrows of `{0}` \
+                            until the borrow ends",
+                            ol)
+                }
+
+                ty::UniqueImmBorrow => {
+                    format!("the unique capture prevents subsequent \
+                            moves or borrows of `{0}` \
+                            until the borrow ends",
+                            ol)
+                }
+            };
+
+            let borrow_summary = match old_loan.cause {
+                euv::ClosureCapture(_) => {
+                    format!("previous borrow of `{}` occurs here{} due to \
+                            use in closure",
+                            ol, old_loan_msg)
+                }
+
+                euv::OverloadedOperator(..) |
+                euv::AddrOf(..) |
+                euv::AutoRef(..) |
+                euv::ClosureInvocation(..) |
+                euv::ForLoop(..) |
+                euv::RefBinding(..) |
+                euv::MatchDiscriminant(..) => {
+                    format!("previous borrow of `{}` occurs here{}",
+                            ol, old_loan_msg)
+                }
+            };
+
+            self.bccx.span_note(
+                old_loan.span,
+                format!("{}; {}", borrow_summary, rule_summary).as_slice());
+
+            let old_loan_span = self.tcx().map.span(old_loan.kill_scope.node_id());
+            self.bccx.span_end_note(old_loan_span,
+                                    "previous borrow ends here");
+
+            return false;
+        }
+
+        true
+    }
+
+    fn is_local_variable_or_arg(&self, cmt: mc::cmt<'tcx>) -> bool {
+        match cmt.cat {
+          mc::cat_local(_) => true,
+          _ => false
+        }
+    }
+
+    fn consume_common(&self,
+                      id: ast::NodeId,
+                      span: Span,
+                      cmt: mc::cmt<'tcx>,
+                      mode: euv::ConsumeMode) {
+        match opt_loan_path(&cmt) {
+            Some(lp) => {
+                let moved_value_use_kind = match mode {
+                    euv::Copy => {
+                        self.check_for_copy_of_frozen_path(id, span, &*lp);
+                        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,
+                                                                     &*lp, move_kind);
+                                if move_kind == move_data::Captured {
+                                    MovedInCapture
+                                } else {
+                                    MovedInUse
+                                }
+                            }
+                        }
+                    }
+                };
+
+                self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
+            }
+            None => { }
+        }
+    }
+
+    fn check_for_copy_of_frozen_path(&self,
+                                     id: ast::NodeId,
+                                     span: Span,
+                                     copy_path: &LoanPath<'tcx>) {
+        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",
+                            self.bccx.loan_path_to_string(copy_path).as_slice())
+                    .as_slice());
+                self.bccx.span_note(
+                    loan_span,
+                    format!("borrow of `{}` occurs here",
+                            self.bccx.loan_path_to_string(&*loan_path).as_slice())
+                    .as_slice());
+            }
+        }
+    }
+
+    fn check_for_move_of_borrowed_path(&self,
+                                       id: ast::NodeId,
+                                       span: Span,
+                                       move_path: &LoanPath<'tcx>,
+                                       move_kind: move_data::MoveKind) {
+        // 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.
+        match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
+            UseOk => { }
+            UseWhileBorrowed(loan_path, loan_span) => {
+                let err_message = match move_kind {
+                    move_data::Captured =>
+                        format!("cannot move `{}` into closure because it is borrowed",
+                                self.bccx.loan_path_to_string(move_path).as_slice()),
+                    move_data::Declared |
+                    move_data::MoveExpr |
+                    move_data::MovePat =>
+                        format!("cannot move out of `{}` because it is borrowed",
+                                self.bccx.loan_path_to_string(move_path).as_slice())
+                };
+
+                self.bccx.span_err(span, err_message.as_slice());
+                self.bccx.span_note(
+                    loan_span,
+                    format!("borrow of `{}` occurs here",
+                            self.bccx.loan_path_to_string(&*loan_path).as_slice())
+                    .as_slice());
+            }
+        }
+    }
+
+    pub fn analyze_restrictions_on_use(&self,
+                                       expr_id: ast::NodeId,
+                                       use_path: &LoanPath<'tcx>,
+                                       borrow_kind: ty::BorrowKind)
+                                       -> UseError<'tcx> {
+        debug!("analyze_restrictions_on_use(expr_id={}, use_path={})",
+               self.tcx().map.node_to_string(expr_id),
+               use_path.repr(self.tcx()));
+
+        let mut ret = UseOk;
+
+        self.each_in_scope_loan_affecting_path(
+            region::CodeExtent::from_node_id(expr_id), use_path, |loan| {
+            if !compatible_borrow_kinds(loan.kind, borrow_kind) {
+                ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
+                false
+            } else {
+                true
+            }
+        });
+
+        return ret;
+    }
+
+    /// Reports an error if `expr` (which should be a path)
+    /// is using a moved/uninitialized value
+    fn check_if_path_is_moved(&self,
+                              id: ast::NodeId,
+                              span: Span,
+                              use_kind: MovedValueUseKind,
+                              lp: &Rc<LoanPath<'tcx>>) {
+        debug!("check_if_path_is_moved(id={}, use_kind={}, lp={})",
+               id, use_kind, lp.repr(self.bccx.tcx));
+        let base_lp = owned_ptr_base_path_rc(lp);
+        self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
+            self.bccx.report_use_of_moved_value(
+                span,
+                use_kind,
+                &**lp,
+                the_move,
+                moved_lp,
+                self.param_env);
+            false
+        });
+    }
+
+    /// 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
+    /// ```
+    fn check_if_assigned_path_is_moved(&self,
+                                       id: ast::NodeId,
+                                       span: Span,
+                                       use_kind: MovedValueUseKind,
+                                       lp: &Rc<LoanPath<'tcx>>)
+    {
+        match lp.kind {
+            LpVar(_) | LpUpvar(_) => {
+                // assigning to `x` does not require that `x` is initialized
+            }
+            LpDowncast(ref lp_base, _) => {
+                // assigning to `(P->Variant).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, _, 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);
+            }
+        }
+    }
+
+    fn check_assignment(&self,
+                        assignment_id: ast::NodeId,
+                        assignment_span: Span,
+                        assignee_cmt: mc::cmt<'tcx>,
+                        mode: euv::MutateMode) {
+        debug!("check_assignment(assignee_cmt={})", assignee_cmt.repr(self.tcx()));
+
+        // Mutable values can be assigned, as long as they obey loans
+        // and aliasing restrictions:
+        if assignee_cmt.mutbl.is_mutable() {
+            if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) {
+                if mode != euv::Init {
+                    check_for_assignment_to_borrowed_path(
+                        self, assignment_id, assignment_span, assignee_cmt.clone());
+                    mark_variable_as_used_mut(self, assignee_cmt);
+                }
+            }
+            return;
+        }
+
+        // Initializations are OK.
+        if mode == euv::Init {
+            return
+        }
+
+        // For immutable local variables, assignments are legal
+        // if they cannot already have been assigned
+        if self.is_local_variable_or_arg(assignee_cmt.clone()) {
+            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| {
+                self.bccx.report_reassigned_immutable_variable(
+                    assignment_span,
+                    &*lp,
+                    assign);
+                false
+            });
+            return;
+        }
+
+        // Otherwise, just a plain error.
+        match assignee_cmt.note {
+            mc::NoteClosureEnv(upvar_id) => {
+                // If this is an `Fn` closure, it simply can't mutate upvars.
+                // If it's an `FnMut` closure, the original variable was declared immutable.
+                // We need to determine which is the case here.
+                let kind = match assignee_cmt.upvar().unwrap().cat {
+                    mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
+                    _ => unreachable!()
+                };
+                if kind == ty::FnUnboxedClosureKind {
+                    self.bccx.span_err(
+                        assignment_span,
+                        format!("cannot assign to {}",
+                                self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
+                    self.bccx.span_help(
+                        self.tcx().map.span(upvar_id.closure_expr_id),
+                        "consider changing this closure to take self by mutable reference");
+                } else {
+                    self.bccx.span_err(
+                        assignment_span,
+                        format!("cannot assign to {} {}",
+                                assignee_cmt.mutbl.to_user_str(),
+                                self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
+                }
+            }
+            _ => match opt_loan_path(&assignee_cmt) {
+                Some(lp) => {
+                    self.bccx.span_err(
+                        assignment_span,
+                        format!("cannot assign to {} {} `{}`",
+                                assignee_cmt.mutbl.to_user_str(),
+                                self.bccx.cmt_to_string(&*assignee_cmt),
+                                self.bccx.loan_path_to_string(&*lp)).as_slice());
+                }
+                None => {
+                    self.bccx.span_err(
+                        assignment_span,
+                        format!("cannot assign to {} {}",
+                                assignee_cmt.mutbl.to_user_str(),
+                                self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
+                }
+            }
+        }
+        return;
+
+        fn mark_variable_as_used_mut<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
+                                               mut cmt: mc::cmt<'tcx>) {
+            //! If the mutability of the `cmt` being written is inherited
+            //! from a local variable, liveness will
+            //! not have been able to detect that this variable's mutability
+            //! is important, so we must add the variable to the
+            //! `used_mut_nodes` table here.
+
+            loop {
+                debug!("mark_variable_as_used_mut(cmt={})", cmt.repr(this.tcx()));
+                match cmt.cat.clone() {
+                    mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: id, .. }, .. }) |
+                    mc::cat_local(id) => {
+                        this.tcx().used_mut_nodes.borrow_mut().insert(id);
+                        return;
+                    }
+
+                    mc::cat_rvalue(..) |
+                    mc::cat_static_item |
+                    mc::cat_deref(_, _, mc::UnsafePtr(..)) |
+                    mc::cat_deref(_, _, mc::Implicit(..)) => {
+                        assert_eq!(cmt.mutbl, mc::McDeclared);
+                        return;
+                    }
+
+                    mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
+                        assert_eq!(cmt.mutbl, mc::McDeclared);
+                        // We need to drill down to upvar if applicable
+                        match cmt.upvar() {
+                            Some(b) => cmt = b,
+                            None => return
+                        }
+                    }
+
+                    mc::cat_deref(b, _, mc::OwnedPtr) => {
+                        assert_eq!(cmt.mutbl, mc::McInherited);
+                        cmt = b;
+                    }
+
+                    mc::cat_downcast(b, _) |
+                    mc::cat_interior(b, _) => {
+                        assert_eq!(cmt.mutbl, mc::McInherited);
+                        cmt = b;
+                    }
+                }
+            }
+        }
+
+        fn check_for_aliasable_mutable_writes<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
+                                                        span: Span,
+                                                        cmt: mc::cmt<'tcx>) -> bool {
+            //! Safety checks related to writes to aliasable, mutable locations
+
+            let guarantor = cmt.guarantor();
+            debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
+                   cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
+            if let mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) = guarantor.cat {
+                // Statically prohibit writes to `&mut` when aliasable
+                check_for_aliasability_violation(this, span, b.clone());
+            }
+
+            return true; // no errors reported
+        }
+
+        fn check_for_aliasability_violation<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
+                                                      span: Span,
+                                                      cmt: mc::cmt<'tcx>)
+                                                      -> bool {
+            match cmt.freely_aliasable(this.tcx()) {
+                None => {
+                    return true;
+                }
+                Some(mc::AliasableStaticMut(..)) => {
+                    return true;
+                }
+                Some(cause) => {
+                    this.bccx.report_aliasability_violation(
+                        span,
+                        MutabilityViolation,
+                        cause);
+                    return false;
+                }
+            }
+        }
+
+        fn check_for_assignment_to_borrowed_path<'a, 'tcx>(
+            this: &CheckLoanCtxt<'a, 'tcx>,
+            assignment_id: ast::NodeId,
+            assignment_span: Span,
+            assignee_cmt: mc::cmt<'tcx>)
+        {
+            //! Check for assignments that violate the terms of an
+            //! outstanding loan.
+
+            let loan_path = match opt_loan_path(&assignee_cmt) {
+                Some(lp) => lp,
+                None => { return; /* no loan path, can't be any loans */ }
+            };
+
+            let scope = region::CodeExtent::from_node_id(assignment_id);
+            this.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
+                this.report_illegal_mutation(assignment_span, &*loan_path, loan);
+                false
+            });
+        }
+    }
+
+    pub fn report_illegal_mutation(&self,
+                                   span: Span,
+                                   loan_path: &LoanPath<'tcx>,
+                                   loan: &Loan) {
+        self.bccx.span_err(
+            span,
+            format!("cannot assign to `{}` because it is borrowed",
+                    self.bccx.loan_path_to_string(loan_path)).as_slice());
+        self.bccx.span_note(
+            loan.span,
+            format!("borrow of `{}` occurs here",
+                    self.bccx.loan_path_to_string(loan_path)).as_slice());
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/doc.rs b/src/librustc_borrowck/borrowck/doc.rs
new file mode 100644 (file)
index 0000000..c6db534
--- /dev/null
@@ -0,0 +1,1222 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! # The Borrow Checker
+//!
+//! This pass has the job of enforcing memory safety. This is a subtle
+//! topic. This docs aim to explain both the practice and the theory
+//! behind the borrow checker. They start with a high-level overview of
+//! how it works, and then proceed to dive into the theoretical
+//! background. Finally, they go into detail on some of the more subtle
+//! aspects.
+//!
+//! # Table of contents
+//!
+//! These docs are long. Search for the section you are interested in.
+//!
+//! - Overview
+//! - Formal model
+//! - Borrowing and loans
+//! - Moves and initialization
+//! - Drop flags and structural fragments
+//! - Future work
+//!
+//! # Overview
+//!
+//! The borrow checker checks one function at a time. It operates in two
+//! passes. The first pass, called `gather_loans`, walks over the function
+//! and identifies all of the places where borrows (e.g., `&` expressions
+//! and `ref` bindings) and moves (copies or captures of a linear value)
+//! occur. It also tracks initialization sites. For each borrow and move,
+//! it checks various basic safety conditions at this time (for example,
+//! that the lifetime of the borrow doesn't exceed the lifetime of the
+//! value being borrowed, or that there is no move out of an `&T`
+//! referent).
+//!
+//! It then uses the dataflow module to propagate which of those borrows
+//! may be in scope at each point in the procedure. A loan is considered
+//! to come into scope at the expression that caused it and to go out of
+//! scope when the lifetime of the resulting reference expires.
+//!
+//! Once the in-scope loans are known for each point in the program, the
+//! borrow checker walks the IR again in a second pass called
+//! `check_loans`. This pass examines each statement and makes sure that
+//! it is safe with respect to the in-scope loans.
+//!
+//! # Formal model
+//!
+//! Throughout the docs we'll consider a simple subset of Rust in which
+//! you can only borrow from lvalues, defined like so:
+//!
+//! ```text
+//! LV = x | LV.f | *LV
+//! ```
+//!
+//! Here `x` represents some variable, `LV.f` is a field reference,
+//! and `*LV` is a pointer dereference. There is no auto-deref or other
+//! niceties. This means that if you have a type like:
+//!
+//! ```text
+//! struct S { f: uint }
+//! ```
+//!
+//! and a variable `a: Box<S>`, then the rust expression `a.f` would correspond
+//! to an `LV` of `(*a).f`.
+//!
+//! Here is the formal grammar for the types we'll consider:
+//!
+//! ```text
+//! TY = () | S<'LT...> | Box<TY> | & 'LT MQ TY
+//! MQ = mut | imm | const
+//! ```
+//!
+//! Most of these types should be pretty self explanatory. Here `S` is a
+//! struct name and we assume structs are declared like so:
+//!
+//! ```text
+//! SD = struct S<'LT...> { (f: TY)... }
+//! ```
+//!
+//! # Borrowing and loans
+//!
+//! ## An intuitive explanation
+//!
+//! ### Issuing loans
+//!
+//! Now, imagine we had a program like this:
+//!
+//! ```text
+//! struct Foo { f: uint, g: uint }
+//! ...
+//! 'a: {
+//!   let mut x: Box<Foo> = ...;
+//!   let y = &mut (*x).f;
+//!   x = ...;
+//! }
+//! ```
+//!
+//! This is of course dangerous because mutating `x` will free the old
+//! value and hence invalidate `y`. The borrow checker aims to prevent
+//! this sort of thing.
+//!
+//! #### Loans and restrictions
+//!
+//! The way the borrow checker works is that it analyzes each borrow
+//! expression (in our simple model, that's stuff like `&LV`, though in
+//! real life there are a few other cases to consider). For each borrow
+//! expression, it computes a `Loan`, which is a data structure that
+//! records (1) the value being borrowed, (2) the mutability and scope of
+//! the borrow, and (3) a set of restrictions. In the code, `Loan` is a
+//! struct defined in `middle::borrowck`. Formally, we define `LOAN` as
+//! follows:
+//!
+//! ```text
+//! LOAN = (LV, LT, MQ, RESTRICTION*)
+//! RESTRICTION = (LV, ACTION*)
+//! ACTION = MUTATE | CLAIM | FREEZE
+//! ```
+//!
+//! Here the `LOAN` tuple defines the lvalue `LV` being borrowed; the
+//! lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a
+//! list of restrictions. The restrictions indicate actions which, if
+//! taken, could invalidate the loan and lead to type safety violations.
+//!
+//! Each `RESTRICTION` is a pair of a restrictive lvalue `LV` (which will
+//! either be the path that was borrowed or some prefix of the path that
+//! was borrowed) and a set of restricted actions.  There are three kinds
+//! of actions that may be restricted for the path `LV`:
+//!
+//! - `MUTATE` means that `LV` cannot be assigned to;
+//! - `CLAIM` means that the `LV` cannot be borrowed mutably;
+//! - `FREEZE` means that the `LV` cannot be borrowed immutably;
+//!
+//! Finally, it is never possible to move from an lvalue that appears in a
+//! restriction. This implies that the "empty restriction" `(LV, [])`,
+//! which contains an empty set of actions, still has a purpose---it
+//! prevents moves from `LV`. I chose not to make `MOVE` a fourth kind of
+//! action because that would imply that sometimes moves are permitted
+//! from restrictived values, which is not the case.
+//!
+//! #### Example
+//!
+//! To give you a better feeling for what kind of restrictions derived
+//! from a loan, let's look at the loan `L` that would be issued as a
+//! result of the borrow `&mut (*x).f` in the example above:
+//!
+//! ```text
+//! L = ((*x).f, 'a, mut, RS) where
+//!     RS = [((*x).f, [MUTATE, CLAIM, FREEZE]),
+//!           (*x, [MUTATE, CLAIM, FREEZE]),
+//!           (x, [MUTATE, CLAIM, FREEZE])]
+//! ```
+//!
+//! The loan states that the expression `(*x).f` has been loaned as
+//! mutable for the lifetime `'a`. Because the loan is mutable, that means
+//! that the value `(*x).f` may be mutated via the newly created reference
+//! (and *only* via that pointer). This is reflected in the
+//! restrictions `RS` that accompany the loan.
+//!
+//! The first restriction `((*x).f, [MUTATE, CLAIM, FREEZE])` states that
+//! the lender may not mutate, freeze, nor alias `(*x).f`. Mutation is
+//! illegal because `(*x).f` is only supposed to be mutated via the new
+//! reference, not by mutating the original path `(*x).f`. Freezing is
+//! illegal because the path now has an `&mut` alias; so even if we the
+//! lender were to consider `(*x).f` to be immutable, it might be mutated
+//! via this alias. They will be enforced for the lifetime `'a` of the
+//! loan. After the loan expires, the restrictions no longer apply.
+//!
+//! The second restriction on `*x` is interesting because it does not
+//! apply to the path that was lent (`(*x).f`) but rather to a prefix of
+//! the borrowed path. This is due to the rules of inherited mutability:
+//! if the user were to assign to (or freeze) `*x`, they would indirectly
+//! overwrite (or freeze) `(*x).f`, and thus invalidate the reference
+//! that was created. In general it holds that when a path is
+//! lent, restrictions are issued for all the owning prefixes of that
+//! path. In this case, the path `*x` owns the path `(*x).f` and,
+//! because `x` is an owned pointer, the path `x` owns the path `*x`.
+//! Therefore, borrowing `(*x).f` yields restrictions on both
+//! `*x` and `x`.
+//!
+//! ### Checking for illegal assignments, moves, and reborrows
+//!
+//! Once we have computed the loans introduced by each borrow, the borrow
+//! checker uses a data flow propagation to compute the full set of loans
+//! in scope at each expression and then uses that set to decide whether
+//! that expression is legal.  Remember that the scope of loan is defined
+//! by its lifetime LT.  We sometimes say that a loan which is in-scope at
+//! a particular point is an "outstanding loan", and the set of
+//! restrictions included in those loans as the "outstanding
+//! restrictions".
+//!
+//! The kinds of expressions which in-scope loans can render illegal are:
+//! - *assignments* (`lv = v`): illegal if there is an in-scope restriction
+//!   against mutating `lv`;
+//! - *moves*: illegal if there is any in-scope restriction on `lv` at all;
+//! - *mutable borrows* (`&mut lv`): illegal there is an in-scope restriction
+//!   against claiming `lv`;
+//! - *immutable borrows* (`&lv`): illegal there is an in-scope restriction
+//!   against freezing `lv`.
+//!
+//! ## Formal rules
+//!
+//! Now that we hopefully have some kind of intuitive feeling for how the
+//! borrow checker works, let's look a bit more closely now at the precise
+//! conditions that it uses. For simplicity I will ignore const loans.
+//!
+//! I will present the rules in a modified form of standard inference
+//! rules, which looks as follows:
+//!
+//! ```text
+//! PREDICATE(X, Y, Z)                  // Rule-Name
+//!   Condition 1
+//!   Condition 2
+//!   Condition 3
+//! ```
+//!
+//! The initial line states the predicate that is to be satisfied.  The
+//! indented lines indicate the conditions that must be met for the
+//! predicate to be satisfied. The right-justified comment states the name
+//! of this rule: there are comments in the borrowck source referencing
+//! these names, so that you can cross reference to find the actual code
+//! that corresponds to the formal rule.
+//!
+//! ### Invariants
+//!
+//! I want to collect, at a high-level, the invariants the borrow checker
+//! maintains. I will give them names and refer to them throughout the
+//! text. Together these invariants are crucial for the overall soundness
+//! of the system.
+//!
+//! **Mutability requires uniqueness.** To mutate a path
+//!
+//! **Unique mutability.** There is only one *usable* mutable path to any
+//! given memory at any given time. This implies that when claiming memory
+//! with an expression like `p = &mut x`, the compiler must guarantee that
+//! the borrowed value `x` can no longer be mutated so long as `p` is
+//! live. (This is done via restrictions, read on.)
+//!
+//! **.**
+//!
+//!
+//! ### The `gather_loans` pass
+//!
+//! We start with the `gather_loans` pass, which walks the AST looking for
+//! borrows.  For each borrow, there are three bits of information: the
+//! lvalue `LV` being borrowed and the mutability `MQ` and lifetime `LT`
+//! of the resulting pointer. Given those, `gather_loans` applies four
+//! validity tests:
+//!
+//! 1. `MUTABILITY(LV, MQ)`: The mutability of the reference is
+//! compatible with the mutability of `LV` (i.e., not borrowing immutable
+//! data as mutable).
+//!
+//! 2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is
+//! compatible with the aliasability of `LV`. The goal is to prevent
+//! `&mut` borrows of aliasability data.
+//!
+//! 3. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
+//! the lifetime of the value being borrowed.
+//!
+//! 4. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
+//! restrictions to maintain memory safety. These are the restrictions
+//! that will go into the final loan. We'll discuss in more detail below.
+//!
+//! ## Checking mutability
+//!
+//! Checking mutability is fairly straightforward. We just want to prevent
+//! immutable data from being borrowed as mutable. Note that it is ok to
+//! borrow mutable data as immutable, since that is simply a
+//! freeze. Formally we define a predicate `MUTABLE(LV, MQ)` which, if
+//! defined, means that "borrowing `LV` with mutability `MQ` is ok. The
+//! Rust code corresponding to this predicate is the function
+//! `check_mutability` in `middle::borrowck::gather_loans`.
+//!
+//! ### Checking mutability of variables
+//!
+//! *Code pointer:* Function `check_mutability()` in `gather_loans/mod.rs`,
+//! but also the code in `mem_categorization`.
+//!
+//! Let's begin with the rules for variables, which state that if a
+//! variable is declared as mutable, it may be borrowed any which way, but
+//! otherwise the variable must be borrowed as immutable or const:
+//!
+//! ```text
+//! MUTABILITY(X, MQ)                   // M-Var-Mut
+//!   DECL(X) = mut
+//!
+//! MUTABILITY(X, MQ)                   // M-Var-Imm
+//!   DECL(X) = imm
+//!   MQ = imm | const
+//! ```
+//!
+//! ### Checking mutability of owned content
+//!
+//! Fields and owned pointers inherit their mutability from
+//! their base expressions, so both of their rules basically
+//! delegate the check to the base expression `LV`:
+//!
+//! ```text
+//! MUTABILITY(LV.f, MQ)                // M-Field
+//!   MUTABILITY(LV, MQ)
+//!
+//! MUTABILITY(*LV, MQ)                 // M-Deref-Unique
+//!   TYPE(LV) = Box<Ty>
+//!   MUTABILITY(LV, MQ)
+//! ```
+//!
+//! ### Checking mutability of immutable pointer types
+//!
+//! Immutable pointer types like `&T` can only
+//! be borrowed if MQ is immutable or const:
+//!
+//! ```text
+//! MUTABILITY(*LV, MQ)                // M-Deref-Borrowed-Imm
+//!   TYPE(LV) = &Ty
+//!   MQ == imm | const
+//! ```
+//!
+//! ### Checking mutability of mutable pointer types
+//!
+//! `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
+//!
+//! ```text
+//! MUTABILITY(*LV, MQ)                 // M-Deref-Borrowed-Mut
+//!   TYPE(LV) = &mut Ty
+//! ```
+//!
+//! ## Checking aliasability
+//!
+//! The goal of the aliasability check is to ensure that we never permit
+//! `&mut` borrows of aliasable data. Formally we define a predicate
+//! `ALIASABLE(LV, MQ)` which if defined means that
+//! "borrowing `LV` with mutability `MQ` is ok". The
+//! Rust code corresponding to this predicate is the function
+//! `check_aliasability()` in `middle::borrowck::gather_loans`.
+//!
+//! ### Checking aliasability of variables
+//!
+//! Local variables are never aliasable as they are accessible only within
+//! the stack frame.
+//!
+//! ```text
+//!     ALIASABLE(X, MQ)                   // M-Var-Mut
+//! ```
+//!
+//! ### Checking aliasable of owned content
+//!
+//! Owned content is aliasable if it is found in an aliasable location:
+//!
+//! ```text
+//! ALIASABLE(LV.f, MQ)                // M-Field
+//!   ALIASABLE(LV, MQ)
+//!
+//! ALIASABLE(*LV, MQ)                 // M-Deref-Unique
+//!   ALIASABLE(LV, MQ)
+//! ```
+//!
+//! ### Checking mutability of immutable pointer types
+//!
+//! Immutable pointer types like `&T` are aliasable, and hence can only be
+//! borrowed immutably:
+//!
+//! ```text
+//! ALIASABLE(*LV, imm)                // M-Deref-Borrowed-Imm
+//!   TYPE(LV) = &Ty
+//! ```
+//!
+//! ### Checking mutability of mutable pointer types
+//!
+//! `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
+//!
+//! ```text
+//! ALIASABLE(*LV, MQ)                 // M-Deref-Borrowed-Mut
+//!   TYPE(LV) = &mut Ty
+//! ```
+//!
+//! ## Checking lifetime
+//!
+//! These rules aim to ensure that no data is borrowed for a scope that exceeds
+//! its lifetime. These two computations wind up being intimately related.
+//! Formally, we define a predicate `LIFETIME(LV, LT, MQ)`, which states that
+//! "the lvalue `LV` can be safely borrowed for the lifetime `LT` with mutability
+//! `MQ`". The Rust code corresponding to this predicate is the module
+//! `middle::borrowck::gather_loans::lifetime`.
+//!
+//! ### The Scope function
+//!
+//! Several of the rules refer to a helper function `SCOPE(LV)=LT`.  The
+//! `SCOPE(LV)` yields the lifetime `LT` for which the lvalue `LV` is
+//! guaranteed to exist, presuming that no mutations occur.
+//!
+//! The scope of a local variable is the block where it is declared:
+//!
+//! ```text
+//!   SCOPE(X) = block where X is declared
+//! ```
+//!
+//! The scope of a field is the scope of the struct:
+//!
+//! ```text
+//!   SCOPE(LV.f) = SCOPE(LV)
+//! ```
+//!
+//! The scope of a unique referent is the scope of the pointer, since
+//! (barring mutation or moves) the pointer will not be freed until
+//! the pointer itself `LV` goes out of scope:
+//!
+//! ```text
+//!   SCOPE(*LV) = SCOPE(LV) if LV has type Box<T>
+//! ```
+//!
+//! The scope of a borrowed referent is the scope associated with the
+//! pointer.  This is a conservative approximation, since the data that
+//! the pointer points at may actually live longer:
+//!
+//! ```text
+//!   SCOPE(*LV) = LT if LV has type &'LT T or &'LT mut T
+//! ```
+//!
+//! ### Checking lifetime of variables
+//!
+//! The rule for variables states that a variable can only be borrowed a
+//! lifetime `LT` that is a subregion of the variable's scope:
+//!
+//! ```text
+//! LIFETIME(X, LT, MQ)                 // L-Local
+//!   LT <= SCOPE(X)
+//! ```
+//!
+//! ### Checking lifetime for owned content
+//!
+//! The lifetime of a field or owned pointer is the same as the lifetime
+//! of its owner:
+//!
+//! ```text
+//! LIFETIME(LV.f, LT, MQ)              // L-Field
+//!   LIFETIME(LV, LT, MQ)
+//!
+//! LIFETIME(*LV, LT, MQ)               // L-Deref-Send
+//!   TYPE(LV) = Box<Ty>
+//!   LIFETIME(LV, LT, MQ)
+//! ```
+//!
+//! ### Checking lifetime for derefs of references
+//!
+//! References have a lifetime `LT'` associated with them.  The
+//! data they point at has been guaranteed to be valid for at least this
+//! lifetime. Therefore, the borrow is valid so long as the lifetime `LT`
+//! of the borrow is shorter than the lifetime `LT'` of the pointer
+//! itself:
+//!
+//! ```text
+//! LIFETIME(*LV, LT, MQ)               // L-Deref-Borrowed
+//!   TYPE(LV) = &LT' Ty OR &LT' mut Ty
+//!   LT <= LT'
+//! ```
+//!
+//! ## Computing the restrictions
+//!
+//! The final rules govern the computation of *restrictions*, meaning that
+//! we compute the set of actions that will be illegal for the life of the
+//! loan. The predicate is written `RESTRICTIONS(LV, LT, ACTIONS) =
+//! RESTRICTION*`, which can be read "in order to prevent `ACTIONS` from
+//! occurring on `LV`, the restrictions `RESTRICTION*` must be respected
+//! for the lifetime of the loan".
+//!
+//! Note that there is an initial set of restrictions: these restrictions
+//! are computed based on the kind of borrow:
+//!
+//! ```text
+//! &mut LV =>   RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE)
+//! &LV =>       RESTRICTIONS(LV, LT, MUTATE|CLAIM)
+//! &const LV => RESTRICTIONS(LV, LT, [])
+//! ```
+//!
+//! The reasoning here is that a mutable borrow must be the only writer,
+//! therefore it prevents other writes (`MUTATE`), mutable borrows
+//! (`CLAIM`), and immutable borrows (`FREEZE`). An immutable borrow
+//! permits other immutable borrows but forbids writes and mutable borrows.
+//! Finally, a const borrow just wants to be sure that the value is not
+//! moved out from under it, so no actions are forbidden.
+//!
+//! ### Restrictions for loans of a local variable
+//!
+//! The simplest case is a borrow of a local variable `X`:
+//!
+//! ```text
+//! RESTRICTIONS(X, LT, ACTIONS) = (X, ACTIONS)            // R-Variable
+//! ```
+//!
+//! In such cases we just record the actions that are not permitted.
+//!
+//! ### Restrictions for loans of fields
+//!
+//! Restricting a field is the same as restricting the owner of that
+//! field:
+//!
+//! ```text
+//! RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS)  // R-Field
+//!   RESTRICTIONS(LV, LT, ACTIONS) = RS
+//! ```
+//!
+//! The reasoning here is as follows. If the field must not be mutated,
+//! then you must not mutate the owner of the field either, since that
+//! would indirectly modify the field. Similarly, if the field cannot be
+//! frozen or aliased, we cannot allow the owner to be frozen or aliased,
+//! since doing so indirectly freezes/aliases the field. This is the
+//! origin of inherited mutability.
+//!
+//! ### Restrictions for loans of owned referents
+//!
+//! Because the mutability of owned referents is inherited, restricting an
+//! owned referent is similar to restricting a field, in that it implies
+//! restrictions on the pointer. However, owned pointers have an important
+//! twist: if the owner `LV` is mutated, that causes the owned referent
+//! `*LV` to be freed! So whenever an owned referent `*LV` is borrowed, we
+//! must prevent the owned pointer `LV` from being mutated, which means
+//! that we always add `MUTATE` and `CLAIM` to the restriction set imposed
+//! on `LV`:
+//!
+//! ```text
+//! RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS)    // R-Deref-Send-Pointer
+//!   TYPE(LV) = Box<Ty>
+//!   RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS
+//! ```
+//!
+//! ### Restrictions for loans of immutable borrowed referents
+//!
+//! Immutable borrowed referents are freely aliasable, meaning that
+//! the compiler does not prevent you from copying the pointer.  This
+//! implies that issuing restrictions is useless. We might prevent the
+//! user from acting on `*LV` itself, but there could be another path
+//! `*LV1` that refers to the exact same memory, and we would not be
+//! restricting that path. Therefore, the rule for `&Ty` pointers
+//! always returns an empty set of restrictions, and it only permits
+//! restricting `MUTATE` and `CLAIM` actions:
+//!
+//! ```text
+//! RESTRICTIONS(*LV, LT, ACTIONS) = []                    // R-Deref-Imm-Borrowed
+//!   TYPE(LV) = &LT' Ty
+//!   LT <= LT'                                            // (1)
+//!   ACTIONS subset of [MUTATE, CLAIM]
+//! ```
+//!
+//! The reason that we can restrict `MUTATE` and `CLAIM` actions even
+//! without a restrictions list is that it is never legal to mutate nor to
+//! borrow mutably the contents of a `&Ty` pointer. In other words,
+//! those restrictions are already inherent in the type.
+//!
+//! Clause (1) in the rule for `&Ty` deserves mention. Here I
+//! specify that the lifetime of the loan must be less than the lifetime
+//! of the `&Ty` pointer. In simple cases, this clause is redundant, since
+//! the `LIFETIME()` function will already enforce the required rule:
+//!
+//! ```
+//! fn foo(point: &'a Point) -> &'static f32 {
+//!     &point.x // Error
+//! }
+//! ```
+//!
+//! The above example fails to compile both because of clause (1) above
+//! but also by the basic `LIFETIME()` check. However, in more advanced
+//! examples involving multiple nested pointers, clause (1) is needed:
+//!
+//! ```
+//! fn foo(point: &'a &'b mut Point) -> &'b f32 {
+//!     &point.x // Error
+//! }
+//! ```
+//!
+//! The `LIFETIME` rule here would accept `'b` because, in fact, the
+//! *memory is* guaranteed to remain valid (i.e., not be freed) for the
+//! lifetime `'b`, since the `&mut` pointer is valid for `'b`. However, we
+//! are returning an immutable reference, so we need the memory to be both
+//! valid and immutable. Even though `point.x` is referenced by an `&mut`
+//! pointer, it can still be considered immutable so long as that `&mut`
+//! pointer is found in an aliased location. That means the memory is
+//! guaranteed to be *immutable* for the lifetime of the `&` pointer,
+//! which is only `'a`, not `'b`. Hence this example yields an error.
+//!
+//! As a final twist, consider the case of two nested *immutable*
+//! pointers, rather than a mutable pointer within an immutable one:
+//!
+//! ```
+//! fn foo(point: &'a &'b Point) -> &'b f32 {
+//!     &point.x // OK
+//! }
+//! ```
+//!
+//! This function is legal. The reason for this is that the inner pointer
+//! (`*point : &'b Point`) is enough to guarantee the memory is immutable
+//! and valid for the lifetime `'b`.  This is reflected in
+//! `RESTRICTIONS()` by the fact that we do not recurse (i.e., we impose
+//! no restrictions on `LV`, which in this particular case is the pointer
+//! `point : &'a &'b Point`).
+//!
+//! #### Why both `LIFETIME()` and `RESTRICTIONS()`?
+//!
+//! Given the previous text, it might seem that `LIFETIME` and
+//! `RESTRICTIONS` should be folded together into one check, but there is
+//! a reason that they are separated. They answer separate concerns.
+//! The rules pertaining to `LIFETIME` exist to ensure that we don't
+//! create a borrowed pointer that outlives the memory it points at. So
+//! `LIFETIME` prevents a function like this:
+//!
+//! ```
+//! fn get_1<'a>() -> &'a int {
+//!     let x = 1;
+//!     &x
+//! }
+//! ```
+//!
+//! Here we would be returning a pointer into the stack. Clearly bad.
+//!
+//! However, the `RESTRICTIONS` rules are more concerned with how memory
+//! is used. The example above doesn't generate an error according to
+//! `RESTRICTIONS` because, for local variables, we don't require that the
+//! loan lifetime be a subset of the local variable lifetime. The idea
+//! here is that we *can* guarantee that `x` is not (e.g.) mutated for the
+//! lifetime `'a`, even though `'a` exceeds the function body and thus
+//! involves unknown code in the caller -- after all, `x` ceases to exist
+//! after we return and hence the remaining code in `'a` cannot possibly
+//! mutate it. This distinction is important for type checking functions
+//! like this one:
+//!
+//! ```
+//! fn inc_and_get<'a>(p: &'a mut Point) -> &'a int {
+//!     p.x += 1;
+//!     &p.x
+//! }
+//! ```
+//!
+//! In this case, we take in a `&mut` and return a frozen borrowed pointer
+//! with the same lifetime. So long as the lifetime of the returned value
+//! doesn't exceed the lifetime of the `&mut` we receive as input, this is
+//! fine, though it may seem surprising at first (it surprised me when I
+//! first worked it through). After all, we're guaranteeing that `*p`
+//! won't be mutated for the lifetime `'a`, even though we can't "see" the
+//! entirety of the code during that lifetime, since some of it occurs in
+//! our caller. But we *do* know that nobody can mutate `*p` except
+//! through `p`. So if we don't mutate `*p` and we don't return `p`, then
+//! we know that the right to mutate `*p` has been lost to our caller --
+//! in terms of capability, the caller passed in the ability to mutate
+//! `*p`, and we never gave it back. (Note that we can't return `p` while
+//! `*p` is borrowed since that would be a move of `p`, as `&mut` pointers
+//! are affine.)
+//!
+//! ### Restrictions for loans of const aliasable referents
+//!
+//! Freeze pointers are read-only. There may be `&mut` or `&` aliases, and
+//! we can not prevent *anything* but moves in that case. So the
+//! `RESTRICTIONS` function is only defined if `ACTIONS` is the empty set.
+//! Because moves from a `&const` lvalue are never legal, it is not
+//! necessary to add any restrictions at all to the final result.
+//!
+//! ```text
+//!     RESTRICTIONS(*LV, LT, []) = []                         // R-Deref-Freeze-Borrowed
+//!       TYPE(LV) = &const Ty
+//! ```
+//!
+//! ### Restrictions for loans of mutable borrowed referents
+//!
+//! Mutable borrowed pointers are guaranteed to be the only way to mutate
+//! their referent. This permits us to take greater license with them; for
+//! example, the referent can be frozen simply be ensuring that we do not
+//! use the original pointer to perform mutate. Similarly, we can allow
+//! the referent to be claimed, so long as the original pointer is unused
+//! while the new claimant is live.
+//!
+//! The rule for mutable borrowed pointers is as follows:
+//!
+//! ```text
+//! RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS)    // R-Deref-Mut-Borrowed
+//!   TYPE(LV) = &LT' mut Ty
+//!   LT <= LT'                                            // (1)
+//!   RESTRICTIONS(LV, LT, ACTIONS) = RS                   // (2)
+//! ```
+//!
+//! Let's examine the two numbered clauses:
+//!
+//! Clause (1) specifies that the lifetime of the loan (`LT`) cannot
+//! exceed the lifetime of the `&mut` pointer (`LT'`). The reason for this
+//! is that the `&mut` pointer is guaranteed to be the only legal way to
+//! mutate its referent -- but only for the lifetime `LT'`.  After that
+//! lifetime, the loan on the referent expires and hence the data may be
+//! modified by its owner again. This implies that we are only able to
+//! guarantee that the referent will not be modified or aliased for a
+//! maximum of `LT'`.
+//!
+//! Here is a concrete example of a bug this rule prevents:
+//!
+//! ```
+//! // Test region-reborrow-from-shorter-mut-ref.rs:
+//! fn copy_pointer<'a,'b,T>(x: &'a mut &'b mut T) -> &'b mut T {
+//!     &mut **p // ERROR due to clause (1)
+//! }
+//! fn main() {
+//!     let mut x = 1;
+//!     let mut y = &mut x; // <-'b-----------------------------+
+//!     //      +-'a--------------------+                       |
+//!     //      v                       v                       |
+//!     let z = copy_borrowed_ptr(&mut y); // y is lent         |
+//!     *y += 1; // Here y==z, so both should not be usable...  |
+//!     *z += 1; // ...and yet they would be, but for clause 1. |
+//! } // <------------------------------------------------------+
+//! ```
+//!
+//! Clause (2) propagates the restrictions on the referent to the pointer
+//! itself. This is the same as with an owned pointer, though the
+//! reasoning is mildly different. The basic goal in all cases is to
+//! prevent the user from establishing another route to the same data. To
+//! see what I mean, let's examine various cases of what can go wrong and
+//! show how it is prevented.
+//!
+//! **Example danger 1: Moving the base pointer.** One of the simplest
+//! ways to violate the rules is to move the base pointer to a new name
+//! and access it via that new name, thus bypassing the restrictions on
+//! the old name. Here is an example:
+//!
+//! ```
+//! // src/test/compile-fail/borrowck-move-mut-base-ptr.rs
+//! fn foo(t0: &mut int) {
+//!     let p: &int = &*t0; // Freezes `*t0`
+//!     let t1 = t0;        //~ ERROR cannot move out of `t0`
+//!     *t1 = 22;           // OK, not a write through `*t0`
+//! }
+//! ```
+//!
+//! Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a
+//! move of `t0` -- or would be, if it were legal. Instead, we get an
+//! error, because clause (2) imposes restrictions on `LV` (`t0`, here),
+//! and any restrictions on a path make it impossible to move from that
+//! path.
+//!
+//! **Example danger 2: Claiming the base pointer.** Another possible
+//! danger is to mutably borrow the base path. This can lead to two bad
+//! scenarios. The most obvious is that the mutable borrow itself becomes
+//! another path to access the same data, as shown here:
+//!
+//! ```
+//! // src/test/compile-fail/borrowck-mut-borrow-of-mut-base-ptr.rs
+//! fn foo<'a>(mut t0: &'a mut int,
+//!            mut t1: &'a mut int) {
+//!     let p: &int = &*t0;     // Freezes `*t0`
+//!     let mut t2 = &mut t0;   //~ ERROR cannot borrow `t0`
+//!     **t2 += 1;              // Mutates `*t0`
+//! }
+//! ```
+//!
+//! In this example, `**t2` is the same memory as `*t0`. Because `t2` is
+//! an `&mut` pointer, `**t2` is a unique path and hence it would be
+//! possible to mutate `**t2` even though that memory was supposed to be
+//! frozen by the creation of `p`. However, an error is reported -- the
+//! reason is that the freeze `&*t0` will restrict claims and mutation
+//! against `*t0` which, by clause 2, in turn prevents claims and mutation
+//! of `t0`. Hence the claim `&mut t0` is illegal.
+//!
+//! Another danger with an `&mut` pointer is that we could swap the `t0`
+//! value away to create a new path:
+//!
+//! ```
+//! // src/test/compile-fail/borrowck-swap-mut-base-ptr.rs
+//! fn foo<'a>(mut t0: &'a mut int,
+//!            mut t1: &'a mut int) {
+//!     let p: &int = &*t0;     // Freezes `*t0`
+//!     swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0`
+//!     *t1 = 22;
+//! }
+//! ```
+//!
+//! This is illegal for the same reason as above. Note that if we added
+//! back a swap operator -- as we used to have -- we would want to be very
+//! careful to ensure this example is still illegal.
+//!
+//! **Example danger 3: Freeze the base pointer.** In the case where the
+//! referent is claimed, even freezing the base pointer can be dangerous,
+//! as shown in the following example:
+//!
+//! ```
+//! // src/test/compile-fail/borrowck-borrow-of-mut-base-ptr.rs
+//! fn foo<'a>(mut t0: &'a mut int,
+//!            mut t1: &'a mut int) {
+//!     let p: &mut int = &mut *t0; // Claims `*t0`
+//!     let mut t2 = &t0;           //~ ERROR cannot borrow `t0`
+//!     let q: &int = &*t2;         // Freezes `*t0` but not through `*p`
+//!     *p += 1;                    // violates type of `*q`
+//! }
+//! ```
+//!
+//! Here the problem is that `*t0` is claimed by `p`, and hence `p` wants
+//! to be the controlling pointer through which mutation or freezes occur.
+//! But `t2` would -- if it were legal -- have the type `& &mut int`, and
+//! hence would be a mutable pointer in an aliasable location, which is
+//! considered frozen (since no one can write to `**t2` as it is not a
+//! unique path). Therefore, we could reasonably create a frozen `&int`
+//! pointer pointing at `*t0` that coexists with the mutable pointer `p`,
+//! which is clearly unsound.
+//!
+//! However, it is not always unsafe to freeze the base pointer. In
+//! particular, if the referent is frozen, there is no harm in it:
+//!
+//! ```
+//! // src/test/run-pass/borrowck-borrow-of-mut-base-ptr-safe.rs
+//! fn foo<'a>(mut t0: &'a mut int,
+//!            mut t1: &'a mut int) {
+//!     let p: &int = &*t0; // Freezes `*t0`
+//!     let mut t2 = &t0;
+//!     let q: &int = &*t2; // Freezes `*t0`, but that's ok...
+//!     let r: &int = &*t0; // ...after all, could do same thing directly.
+//! }
+//! ```
+//!
+//! In this case, creating the alias `t2` of `t0` is safe because the only
+//! thing `t2` can be used for is to further freeze `*t0`, which is
+//! already frozen. In particular, we cannot assign to `*t0` through the
+//! new alias `t2`, as demonstrated in this test case:
+//!
+//! ```
+//! // src/test/run-pass/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs
+//! fn foo(t0: & &mut int) {
+//!     let t1 = t0;
+//!     let p: &int = &**t0;
+//!     **t1 = 22; //~ ERROR cannot assign
+//! }
+//! ```
+//!
+//! This distinction is reflected in the rules. When doing an `&mut`
+//! borrow -- as in the first example -- the set `ACTIONS` will be
+//! `CLAIM|MUTATE|FREEZE`, because claiming the referent implies that it
+//! cannot be claimed, mutated, or frozen by anyone else. These
+//! restrictions are propagated back to the base path and hence the base
+//! path is considered unfreezable.
+//!
+//! In contrast, when the referent is merely frozen -- as in the second
+//! example -- the set `ACTIONS` will be `CLAIM|MUTATE`, because freezing
+//! the referent implies that it cannot be claimed or mutated but permits
+//! others to freeze. Hence when these restrictions are propagated back to
+//! the base path, it will still be considered freezable.
+//!
+//!
+//!
+//! **FIXME #10520: Restrictions against mutating the base pointer.** When
+//! an `&mut` pointer is frozen or claimed, we currently pass along the
+//! restriction against MUTATE to the base pointer. I do not believe this
+//! restriction is needed. It dates from the days when we had a way to
+//! mutate that preserved the value being mutated (i.e., swap). Nowadays
+//! the only form of mutation is assignment, which destroys the pointer
+//! being mutated -- therefore, a mutation cannot create a new path to the
+//! same data. Rather, it removes an existing path. This implies that not
+//! only can we permit mutation, we can have mutation kill restrictions in
+//! the dataflow sense.
+//!
+//! **WARNING:** We do not currently have `const` borrows in the
+//! language. If they are added back in, we must ensure that they are
+//! consistent with all of these examples. The crucial question will be
+//! what sorts of actions are permitted with a `&const &mut` pointer. I
+//! would suggest that an `&mut` referent found in an `&const` location be
+//! prohibited from both freezes and claims. This would avoid the need to
+//! prevent `const` borrows of the base pointer when the referent is
+//! borrowed.
+//!
+//! # Moves and initialization
+//!
+//! The borrow checker is also in charge of ensuring that:
+//!
+//! - all memory which is accessed is initialized
+//! - immutable local variables are assigned at most once.
+//!
+//! These are two separate dataflow analyses built on the same
+//! framework. Let's look at checking that memory is initialized first;
+//! the checking of immutable local variable assignments works in a very
+//! similar way.
+//!
+//! To track the initialization of memory, we actually track all the
+//! points in the program that *create uninitialized memory*, meaning
+//! moves and the declaration of uninitialized variables. For each of
+//! these points, we create a bit in the dataflow set. Assignments to a
+//! variable `x` or path `a.b.c` kill the move/uninitialization bits for
+//! those paths and any subpaths (e.g., `x`, `x.y`, `a.b.c`, `*a.b.c`).
+//! Bits are unioned when two control-flow paths join. Thus, the
+//! presence of a bit indicates that the move may have occurred without an
+//! intervening assignment to the same memory. At each use of a variable,
+//! we examine the bits in scope, and check that none of them are
+//! moves/uninitializations of the variable that is being used.
+//!
+//! Let's look at a simple example:
+//!
+//! ```
+//! fn foo(a: Box<int>) {
+//!     let b: Box<int>;   // Gen bit 0.
+//!
+//!     if cond {          // Bits: 0
+//!         use(&*a);
+//!         b = a;         // Gen bit 1, kill bit 0.
+//!         use(&*b);
+//!     } else {
+//!                        // Bits: 0
+//!     }
+//!                        // Bits: 0,1
+//!     use(&*a);          // Error.
+//!     use(&*b);          // Error.
+//! }
+//!
+//! fn use(a: &int) { }
+//! ```
+//!
+//! In this example, the variable `b` is created uninitialized. In one
+//! branch of an `if`, we then move the variable `a` into `b`. Once we
+//! exit the `if`, therefore, it is an error to use `a` or `b` since both
+//! are only conditionally initialized. I have annotated the dataflow
+//! state using comments. There are two dataflow bits, with bit 0
+//! corresponding to the creation of `b` without an initializer, and bit 1
+//! corresponding to the move of `a`. The assignment `b = a` both
+//! generates bit 1, because it is a move of `a`, and kills bit 0, because
+//! `b` is now initialized. On the else branch, though, `b` is never
+//! initialized, and so bit 0 remains untouched. When the two flows of
+//! control join, we union the bits from both sides, resulting in both
+//! bits 0 and 1 being set. Thus any attempt to use `a` uncovers the bit 1
+//! from the "then" branch, showing that `a` may be moved, and any attempt
+//! to use `b` uncovers bit 0, from the "else" branch, showing that `b`
+//! may not be initialized.
+//!
+//! ## Initialization of immutable variables
+//!
+//! Initialization of immutable variables works in a very similar way,
+//! except that:
+//!
+//! 1. we generate bits for each assignment to a variable;
+//! 2. the bits are never killed except when the variable goes out of scope.
+//!
+//! Thus the presence of an assignment bit indicates that the assignment
+//! may have occurred. Note that assignments are only killed when the
+//! variable goes out of scope, as it is not relevant whether or not there
+//! has been a move in the meantime. Using these bits, we can declare that
+//! an assignment to an immutable variable is legal iff there is no other
+//! assignment bit to that same variable in scope.
+//!
+//! ## Why is the design made this way?
+//!
+//! It may seem surprising that we assign dataflow bits to *each move*
+//! rather than *each path being moved*. This is somewhat less efficient,
+//! since on each use, we must iterate through all moves and check whether
+//! any of them correspond to the path in question. Similar concerns apply
+//! to the analysis for double assignments to immutable variables. The
+//! main reason to do it this way is that it allows us to print better
+//! error messages, because when a use occurs, we can print out the
+//! precise move that may be in scope, rather than simply having to say
+//! "the variable may not be initialized".
+//!
+//! ## Data structures used in the move analysis
+//!
+//! The move analysis maintains several data structures that enable it to
+//! cross-reference moves and assignments to determine when they may be
+//! moving/assigning the same memory. These are all collected into the
+//! `MoveData` and `FlowedMoveData` structs. The former represents the set
+//! of move paths, moves, and assignments, and the latter adds in the
+//! results of a dataflow computation.
+//!
+//! ### Move paths
+//!
+//! The `MovePath` tree tracks every path that is moved or assigned to.
+//! These paths have the same form as the `LoanPath` data structure, which
+//! in turn is the "real world version of the lvalues `LV` that we
+//! introduced earlier. The difference between a `MovePath` and a `LoanPath`
+//! is that move paths are:
+//!
+//! 1. Canonicalized, so that we have exactly one copy of each, and
+//!    we can refer to move paths by index;
+//! 2. Cross-referenced with other paths into a tree, so that given a move
+//!    path we can efficiently find all parent move paths and all
+//!    extensions (e.g., given the `a.b` move path, we can easily find the
+//!    move path `a` and also the move paths `a.b.c`)
+//! 3. Cross-referenced with moves and assignments, so that we can
+//!    easily find all moves and assignments to a given path.
+//!
+//! The mechanism that we use is to create a `MovePath` record for each
+//! move path. These are arranged in an array and are referenced using
+//! `MovePathIndex` values, which are newtype'd indices. The `MovePath`
+//! structs are arranged into a tree, representing using the standard
+//! Knuth representation where each node has a child 'pointer' and a "next
+//! sibling" 'pointer'. In addition, each `MovePath` has a parent
+//! 'pointer'.  In this case, the 'pointers' are just `MovePathIndex`
+//! values.
+//!
+//! In this way, if we want to find all base paths of a given move path,
+//! we can just iterate up the parent pointers (see `each_base_path()` in
+//! the `move_data` module). If we want to find all extensions, we can
+//! iterate through the subtree (see `each_extending_path()`).
+//!
+//! ### Moves and assignments
+//!
+//! There are structs to represent moves (`Move`) and assignments
+//! (`Assignment`), and these are also placed into arrays and referenced
+//! by index. All moves of a particular path are arranged into a linked
+//! lists, beginning with `MovePath.first_move` and continuing through
+//! `Move.next_move`.
+//!
+//! We distinguish between "var" assignments, which are assignments to a
+//! variable like `x = foo`, and "path" assignments (`x.f = foo`).  This
+//! is because we need to assign dataflows to the former, but not the
+//! latter, so as to check for double initialization of immutable
+//! variables.
+//!
+//! ### Gathering and checking moves
+//!
+//! Like loans, we distinguish two phases. The first, gathering, is where
+//! we uncover all the moves and assignments. As with loans, we do some
+//! basic sanity checking in this phase, so we'll report errors if you
+//! attempt to move out of a borrowed pointer etc. Then we do the dataflow
+//! (see `FlowedMoveData::new`). Finally, in the `check_loans.rs` code, we
+//! walk back over, identify all uses, assignments, and captures, and
+//! check that they are legal given the set of dataflow bits we have
+//! computed for that program point.
+//!
+//! # Drop flags and structural fragments
+//!
+//! In addition to the job of enforcing memory safety, the borrow checker
+//! code is also responsible for identifying the *structural fragments* of
+//! data in the function, to support out-of-band dynamic drop flags
+//! allocated on the stack. (For background, see [RFC PR #320].)
+//!
+//! [RFC PR #320]: https://github.com/rust-lang/rfcs/pull/320
+//!
+//! Semantically, each piece of data that has a destructor may need a
+//! boolean flag to indicate whether or not its destructor has been run
+//! yet. However, in many cases there is no need to actually maintain such
+//! a flag: It can be apparent from the code itself that a given path is
+//! always initialized (or always deinitialized) when control reaches the
+//! end of its owner's scope, and thus we can unconditionally emit (or
+//! not) the destructor invocation for that path.
+//!
+//! A simple example of this is the following:
+//!
+//! ```rust
+//! struct D { p: int }
+//! impl D { fn new(x: int) -> D { ... }
+//! impl Drop for D { ... }
+//!
+//! fn foo(a: D, b: D, t: || -> bool) {
+//!     let c: D;
+//!     let d: D;
+//!     if t() { c = b; }
+//! }
+//! ```
+//!
+//! At the end of the body of `foo`, the compiler knows that `a` is
+//! initialized, introducing a drop obligation (deallocating the boxed
+//! integer) for the end of `a`'s scope that is run unconditionally.
+//! Likewise the compiler knows that `d` is not initialized, and thus it
+//! leave out the drop code for `d`.
+//!
+//! The compiler cannot statically know the drop-state of `b` nor `c` at
+//! the end of their scope, since that depends on the value of
+//! `t`. Therefore, we need to insert boolean flags to track whether we
+//! need to drop `b` and `c`.
+//!
+//! However, the matter is not as simple as just mapping local variables
+//! to their corresponding drop flags when necessary. In particular, in
+//! addition to being able to move data out of local variables, Rust
+//! allows one to move values in and out of structured data.
+//!
+//! Consider the following:
+//!
+//! ```rust
+//! struct S { x: D, y: D, z: D }
+//!
+//! fn foo(a: S, mut b: S, t: || -> bool) {
+//!     let mut c: S;
+//!     let d: S;
+//!     let e: S = a.clone();
+//!     if t() {
+//!         c = b;
+//!         b.x = e.y;
+//!     }
+//!     if t() { c.y = D::new(4); }
+//! }
+//! ```
+//!
+//! As before, the drop obligations of `a` and `d` can be statically
+//! determined, and again the state of `b` and `c` depend on dynamic
+//! state. But additionally, the dynamic drop obligations introduced by
+//! `b` and `c` are not just per-local boolean flags. For example, if the
+//! first call to `t` returns `false` and the second call `true`, then at
+//! the end of their scope, `b` will be completely initialized, but only
+//! `c.y` in `c` will be initialized.  If both calls to `t` return `true`,
+//! then at the end of their scope, `c` will be completely initialized,
+//! but only `b.x` will be initialized in `b`, and only `e.x` and `e.z`
+//! will be initialized in `e`.
+//!
+//! Note that we need to cover the `z` field in each case in some way,
+//! since it may (or may not) need to be dropped, even though `z` is never
+//! directly mentioned in the body of the `foo` function. We call a path
+//! like `b.z` a *fragment sibling* of `b.x`, since the field `z` comes
+//! from the same structure `S` that declared the field `x` in `b.x`.
+//!
+//! In general we need to maintain boolean flags that match the
+//! `S`-structure of both `b` and `c`.  In addition, we need to consult
+//! such a flag when doing an assignment (such as `c.y = D::new(4);`
+//! above), in order to know whether or not there is a previous value that
+//! needs to be dropped before we do the assignment.
+//!
+//! So for any given function, we need to determine what flags are needed
+//! to track its drop obligations. Our strategy for determining the set of
+//! flags is to represent the fragmentation of the structure explicitly:
+//! by starting initially from the paths that are explicitly mentioned in
+//! moves and assignments (such as `b.x` and `c.y` above), and then
+//! traversing the structure of the path's type to identify leftover
+//! *unmoved fragments*: assigning into `c.y` means that `c.x` and `c.z`
+//! are leftover unmoved fragments. Each fragment represents a drop
+//! obligation that may need to be tracked. Paths that are only moved or
+//! assigned in their entirety (like `a` and `d`) are treated as a single
+//! drop obligation.
+//!
+//! The fragment construction process works by piggy-backing on the
+//! existing `move_data` module. We already have callbacks that visit each
+//! direct move and assignment; these form the basis for the sets of
+//! moved_leaf_paths and assigned_leaf_paths. From these leaves, we can
+//! walk up their parent chain to identify all of their parent paths.
+//! We need to identify the parents because of cases like the following:
+//!
+//! ```rust
+//! struct Pair<X,Y>{ x: X, y: Y }
+//! fn foo(dd_d_d: Pair<Pair<Pair<D, D>, D>, D>) {
+//!     other_function(dd_d_d.x.y);
+//! }
+//! ```
+//!
+//! In this code, the move of the path `dd_d.x.y` leaves behind not only
+//! the fragment drop-obligation `dd_d.x.x` but also `dd_d.y` as well.
+//!
+//! Once we have identified the directly-referenced leaves and their
+//! parents, we compute the left-over fragments, in the function
+//! `fragments::add_fragment_siblings`. As of this writing this works by
+//! looking at each directly-moved or assigned path P, and blindly
+//! gathering all sibling fields of P (as well as siblings for the parents
+//! of P, etc). After accumulating all such siblings, we filter out the
+//! entries added as siblings of P that turned out to be
+//! directly-referenced paths (or parents of directly referenced paths)
+//! themselves, thus leaving the never-referenced "left-overs" as the only
+//! thing left from the gathering step.
+//!
+//! ## Array structural fragments
+//!
+//! A special case of the structural fragments discussed above are
+//! the elements of an array that has been passed by value, such as
+//! the following:
+//!
+//! ```rust
+//! fn foo(a: [D, ..10], i: uint) -> D {
+//!     a[i]
+//! }
+//! ```
+//!
+//! The above code moves a single element out of the input array `a`.
+//! The remainder of the array still needs to be dropped; i.e., it
+//! is a structural fragment. Note that after performing such a move,
+//! it is not legal to read from the array `a`. There are a number of
+//! ways to deal with this, but the important thing to note is that
+//! the semantics needs to distinguish in some manner between a
+//! fragment that is the *entire* array versus a fragment that represents
+//! all-but-one element of the array.  A place where that distinction
+//! would arise is the following:
+//!
+//! ```rust
+//! fn foo(a: [D, ..10], b: [D, ..10], i: uint, t: bool) -> D {
+//!     if t {
+//!         a[i]
+//!     } else {
+//!         b[i]
+//!     }
+//!
+//!     // When control exits, we will need either to drop all of `a`
+//!     // and all-but-one of `b`, or to drop all of `b` and all-but-one
+//!     // of `a`.
+//! }
+//! ```
+//!
+//! There are a number of ways that the trans backend could choose to
+//! compile this (e.g. a `[bool, ..10]` array for each such moved array;
+//! or an `Option<uint>` for each moved array).  From the viewpoint of the
+//! borrow-checker, the important thing is to record what kind of fragment
+//! is implied by the relevant moves.
+//!
+//! # Future work
+//!
+//! While writing up these docs, I encountered some rules I believe to be
+//! stricter than necessary:
+//!
+//! - I think restricting the `&mut` LV against moves and `ALIAS` is sufficient,
+//!   `MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was
+//!   a built-in operator, but as it is not, it is implied by `CLAIM`,
+//!   and `CLAIM` is implied by `ALIAS`. The only net effect of this is an
+//!   extra error message in some cases, though.
+//! - I have not described how closures interact. Current code is unsound.
+//!   I am working on describing and implementing the fix.
+//! - If we wish, we can easily extend the move checking to allow finer-grained
+//!   tracking of what is initialized and what is not, enabling code like
+//!   this:
+//!
+//!       a = x.f.g; // x.f.g is now uninitialized
+//!       // here, x and x.f are not usable, but x.f.h *is*
+//!       x.f.g = b; // x.f.g is not initialized
+//!       // now x, x.f, x.f.g, x.f.h are all usable
+//!
+//!   What needs to change here, most likely, is that the `moves` module
+//!   should record not only what paths are moved, but what expressions
+//!   are actual *uses*. For example, the reference to `x` in `x.f.g = b`
+//!   is not a true *use* in the sense that it requires `x` to be fully
+//!   initialized. This is in fact why the above code produces an error
+//!   today: the reference to `x` in `x.f.g = b` is considered illegal
+//!   because `x` is not fully initialized.
+//!
+//! There are also some possible refactorings:
+//!
+//! - It might be nice to replace all loan paths with the MovePath mechanism,
+//!   since they allow lightweight comparison using an integer.
diff --git a/src/librustc_borrowck/borrowck/fragments.rs b/src/librustc_borrowck/borrowck/fragments.rs
new file mode 100644 (file)
index 0000000..25ed518
--- /dev/null
@@ -0,0 +1,475 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Helper routines used for fragmenting structural paths due to moves for
+//! tracking drop obligations. Please see the extensive comments in the
+//! section "Structural fragments" in `doc.rs`.
+
+use self::Fragment::*;
+
+use borrowck::{LoanPath};
+use borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
+use borrowck::LoanPathElem::{LpDeref, LpInterior};
+use borrowck::move_data::{InvalidMovePathIndex};
+use borrowck::move_data::{MoveData, MovePathIndex};
+use rustc::session::config;
+use rustc::middle::ty;
+use rustc::middle::mem_categorization as mc;
+use rustc::util::ppaux::{Repr, UserString};
+use std::mem;
+use std::rc::Rc;
+use std::slice;
+use syntax::ast;
+use syntax::ast_map;
+use syntax::attr::AttrMetaMethods;
+use syntax::codemap::Span;
+
+#[deriving(PartialEq, Eq, PartialOrd, Ord)]
+enum Fragment {
+    // This represents the path described by the move path index
+    Just(MovePathIndex),
+
+    // This represents the collection of all but one of the elements
+    // from an array at the path described by the move path index.
+    // Note that attached MovePathIndex should have mem_categorization
+    // of InteriorElement (i.e. array dereference `[]`).
+    AllButOneFrom(MovePathIndex),
+}
+
+impl Fragment {
+    fn loan_path_repr<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
+        let repr = |mpi| move_data.path_loan_path(mpi).repr(tcx);
+        match *self {
+            Just(mpi) => repr(mpi),
+            AllButOneFrom(mpi) => format!("$(allbutone {})", repr(mpi)),
+        }
+    }
+
+    fn loan_path_user_string<'tcx>(&self,
+                                   move_data: &MoveData<'tcx>,
+                                   tcx: &ty::ctxt<'tcx>) -> String {
+        let user_string = |mpi| move_data.path_loan_path(mpi).user_string(tcx);
+        match *self {
+            Just(mpi) => user_string(mpi),
+            AllButOneFrom(mpi) => format!("$(allbutone {})", user_string(mpi)),
+        }
+    }
+}
+
+pub struct FragmentSets {
+    /// During move_data construction, `moved_leaf_paths` tracks paths
+    /// that have been used directly by being moved out of.  When
+    /// move_data construction has been completed, `moved_leaf_paths`
+    /// tracks such paths that are *leaf fragments* (e.g. `a.j` if we
+    /// never move out any child like `a.j.x`); any parent paths
+    /// (e.g. `a` for the `a.j` example) are moved over to
+    /// `parents_of_fragments`.
+    moved_leaf_paths: Vec<MovePathIndex>,
+
+    /// `assigned_leaf_paths` tracks paths that have been used
+    /// directly by being overwritten, but is otherwise much like
+    /// `moved_leaf_paths`.
+    assigned_leaf_paths: Vec<MovePathIndex>,
+
+    /// `parents_of_fragments` tracks paths that are definitely
+    /// parents of paths that have been moved.
+    ///
+    /// FIXME(pnkfelix) probably do not want/need
+    /// `parents_of_fragments` at all, if we can avoid it.
+    ///
+    /// Update: I do not see a way to to avoid it.  Maybe just remove
+    /// above fixme, or at least document why doing this may be hard.
+    parents_of_fragments: Vec<MovePathIndex>,
+
+    /// During move_data construction (specifically the
+    /// fixup_fragment_sets call), `unmoved_fragments` tracks paths
+    /// that have been "left behind" after a sibling has been moved or
+    /// assigned.  When move_data construction has been completed,
+    /// `unmoved_fragments` tracks paths that were *only* results of
+    /// being left-behind, and never directly moved themselves.
+    unmoved_fragments: Vec<Fragment>,
+}
+
+impl FragmentSets {
+    pub fn new() -> FragmentSets {
+        FragmentSets {
+            unmoved_fragments: Vec::new(),
+            moved_leaf_paths: Vec::new(),
+            assigned_leaf_paths: Vec::new(),
+            parents_of_fragments: Vec::new(),
+        }
+    }
+
+    pub fn add_move(&mut self, path_index: MovePathIndex) {
+        self.moved_leaf_paths.push(path_index);
+    }
+
+    pub fn add_assignment(&mut self, path_index: MovePathIndex) {
+        self.assigned_leaf_paths.push(path_index);
+    }
+}
+
+pub fn instrument_move_fragments<'tcx>(this: &MoveData<'tcx>,
+                                       tcx: &ty::ctxt<'tcx>,
+                                       sp: Span,
+                                       id: ast::NodeId) {
+    let (span_err, print) = {
+        let attrs : &[ast::Attribute];
+        attrs = match tcx.map.find(id) {
+            Some(ast_map::NodeItem(ref item)) =>
+                item.attrs.as_slice(),
+            Some(ast_map::NodeImplItem(&ast::MethodImplItem(ref m))) =>
+                m.attrs.as_slice(),
+            Some(ast_map::NodeTraitItem(&ast::ProvidedMethod(ref m))) =>
+                m.attrs.as_slice(),
+            _ => [].as_slice(),
+        };
+
+        let span_err =
+            attrs.iter().any(|a| a.check_name("rustc_move_fragments"));
+        let print = tcx.sess.debugging_opt(config::PRINT_MOVE_FRAGMENTS);
+
+        (span_err, print)
+    };
+
+    if !span_err && !print { return; }
+
+    let instrument_all_paths = |kind, vec_rc: &Vec<MovePathIndex>| {
+        for (i, mpi) in vec_rc.iter().enumerate() {
+            let render = || this.path_loan_path(*mpi).user_string(tcx);
+            if span_err {
+                tcx.sess.span_err(sp, format!("{}: `{}`", kind, render()).as_slice());
+            }
+            if print {
+                println!("id:{} {}[{}] `{}`", id, kind, i, render());
+            }
+        }
+    };
+
+    let instrument_all_fragments = |kind, vec_rc: &Vec<Fragment>| {
+        for (i, f) in vec_rc.iter().enumerate() {
+            let render = || f.loan_path_user_string(this, tcx);
+            if span_err {
+                tcx.sess.span_err(sp, format!("{}: `{}`", kind, render()).as_slice());
+            }
+            if print {
+                println!("id:{} {}[{}] `{}`", id, kind, i, render());
+            }
+        }
+    };
+
+    let fragments = this.fragments.borrow();
+    instrument_all_paths("moved_leaf_path", &fragments.moved_leaf_paths);
+    instrument_all_fragments("unmoved_fragment", &fragments.unmoved_fragments);
+    instrument_all_paths("parent_of_fragments", &fragments.parents_of_fragments);
+    instrument_all_paths("assigned_leaf_path", &fragments.assigned_leaf_paths);
+}
+
+/// Normalizes the fragment sets in `this`; i.e., removes duplicate entries, constructs the set of
+/// parents, and constructs the left-over fragments.
+///
+/// Note: "left-over fragments" means paths that were not directly referenced in moves nor
+/// assignments, but must nonetheless be tracked as potential drop obligations.
+pub fn fixup_fragment_sets<'tcx>(this: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) {
+
+    let mut fragments = this.fragments.borrow_mut();
+
+    // Swap out contents of fragments so that we can modify the fields
+    // without borrowing the common fragments.
+    let mut unmoved = mem::replace(&mut fragments.unmoved_fragments, vec![]);
+    let mut parents = mem::replace(&mut fragments.parents_of_fragments, vec![]);
+    let mut moved = mem::replace(&mut fragments.moved_leaf_paths, vec![]);
+    let mut assigned = mem::replace(&mut fragments.assigned_leaf_paths, vec![]);
+
+    let path_lps = |mpis: &[MovePathIndex]| -> Vec<String> {
+        mpis.iter().map(|mpi| this.path_loan_path(*mpi).repr(tcx)).collect()
+    };
+
+    let frag_lps = |fs: &[Fragment]| -> Vec<String> {
+        fs.iter().map(|f| f.loan_path_repr(this, tcx)).collect()
+    };
+
+    // First, filter out duplicates
+    moved.sort();
+    moved.dedup();
+    debug!("fragments 1 moved: {}", path_lps(moved.as_slice()));
+
+    assigned.sort();
+    assigned.dedup();
+    debug!("fragments 1 assigned: {}", path_lps(assigned.as_slice()));
+
+    // Second, build parents from the moved and assigned.
+    for m in moved.iter() {
+        let mut p = this.path_parent(*m);
+        while p != InvalidMovePathIndex {
+            parents.push(p);
+            p = this.path_parent(p);
+        }
+    }
+    for a in assigned.iter() {
+        let mut p = this.path_parent(*a);
+        while p != InvalidMovePathIndex {
+            parents.push(p);
+            p = this.path_parent(p);
+        }
+    }
+
+    parents.sort();
+    parents.dedup();
+    debug!("fragments 2 parents: {}", path_lps(parents.as_slice()));
+
+    // Third, filter the moved and assigned fragments down to just the non-parents
+    moved.retain(|f| non_member(*f, parents.as_slice()));
+    debug!("fragments 3 moved: {}", path_lps(moved.as_slice()));
+
+    assigned.retain(|f| non_member(*f, parents.as_slice()));
+    debug!("fragments 3 assigned: {}", path_lps(assigned.as_slice()));
+
+    // Fourth, build the leftover from the moved, assigned, and parents.
+    for m in moved.iter() {
+        let lp = this.path_loan_path(*m);
+        add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
+    }
+    for a in assigned.iter() {
+        let lp = this.path_loan_path(*a);
+        add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
+    }
+    for p in parents.iter() {
+        let lp = this.path_loan_path(*p);
+        add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
+    }
+
+    unmoved.sort();
+    unmoved.dedup();
+    debug!("fragments 4 unmoved: {}", frag_lps(unmoved.as_slice()));
+
+    // Fifth, filter the leftover fragments down to its core.
+    unmoved.retain(|f| match *f {
+        AllButOneFrom(_) => true,
+        Just(mpi) => non_member(mpi, parents.as_slice()) &&
+            non_member(mpi, moved.as_slice()) &&
+            non_member(mpi, assigned.as_slice())
+    });
+    debug!("fragments 5 unmoved: {}", frag_lps(unmoved.as_slice()));
+
+    // Swap contents back in.
+    fragments.unmoved_fragments = unmoved;
+    fragments.parents_of_fragments = parents;
+    fragments.moved_leaf_paths = moved;
+    fragments.assigned_leaf_paths = assigned;
+
+    return;
+
+    fn non_member(elem: MovePathIndex, set: &[MovePathIndex]) -> bool {
+        match set.binary_search_elem(&elem) {
+            slice::BinarySearchResult::Found(_) => false,
+            slice::BinarySearchResult::NotFound(_) => true,
+        }
+    }
+}
+
+/// Adds all of the precisely-tracked siblings of `lp` as potential move paths of interest. For
+/// example, if `lp` represents `s.x.j`, then adds moves paths for `s.x.i` and `s.x.k`, the
+/// siblings of `s.x.j`.
+fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
+                               tcx: &ty::ctxt<'tcx>,
+                               gathered_fragments: &mut Vec<Fragment>,
+                               lp: Rc<LoanPath<'tcx>>,
+                               origin_id: Option<ast::NodeId>) {
+    match lp.kind {
+        LpVar(_) | LpUpvar(..) => {} // Local variables have no siblings.
+
+        // Consuming a downcast is like consuming the original value, so propage inward.
+        LpDowncast(ref loan_parent, _) => {
+            add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id);
+        }
+
+        // *LV for OwnedPtr consumes the contents of the box (at
+        // least when it is non-copy...), so propagate inward.
+        LpExtend(ref loan_parent, _, LpDeref(mc::OwnedPtr)) => {
+            add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id);
+        }
+
+        // *LV for unsafe and borrowed pointers do not consume their loan path, so stop here.
+        LpExtend(_, _, LpDeref(mc::UnsafePtr(..)))   |
+        LpExtend(_, _, LpDeref(mc::Implicit(..)))    |
+        LpExtend(_, _, LpDeref(mc::BorrowedPtr(..))) => {}
+
+        // FIXME(pnkfelix): LV[j] should be tracked, at least in the
+        // sense of we will track the remaining drop obligation of the
+        // rest of the array.
+        //
+        // LV[j] is not tracked precisely
+        LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
+            let mp = this.move_path(tcx, lp.clone());
+            gathered_fragments.push(AllButOneFrom(mp));
+        }
+
+        // field access LV.x and tuple access LV#k are the cases
+        // we are interested in
+        LpExtend(ref loan_parent, mc,
+                 LpInterior(mc::InteriorField(ref field_name))) => {
+            let enum_variant_info = match loan_parent.kind {
+                LpDowncast(ref loan_parent_2, variant_def_id) =>
+                    Some((variant_def_id, loan_parent_2.clone())),
+                LpExtend(..) | LpVar(..) | LpUpvar(..) =>
+                    None,
+            };
+            add_fragment_siblings_for_extension(
+                this,
+                tcx,
+                gathered_fragments,
+                loan_parent, mc, field_name, &lp, origin_id, enum_variant_info);
+        }
+    }
+}
+
+/// We have determined that `origin_lp` destructures to LpExtend(parent, original_field_name).
+/// Based on this, add move paths for all of the siblings of `origin_lp`.
+fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
+                                             tcx: &ty::ctxt<'tcx>,
+                                             gathered_fragments: &mut Vec<Fragment>,
+                                             parent_lp: &Rc<LoanPath<'tcx>>,
+                                             mc: mc::MutabilityCategory,
+                                             origin_field_name: &mc::FieldName,
+                                             origin_lp: &Rc<LoanPath<'tcx>>,
+                                             origin_id: Option<ast::NodeId>,
+                                             enum_variant_info: Option<(ast::DefId,
+                                                                        Rc<LoanPath<'tcx>>)>) {
+    let parent_ty = parent_lp.to_type();
+
+    let add_fragment_sibling_local = |field_name, variant_did| {
+        add_fragment_sibling_core(
+            this, tcx, gathered_fragments, parent_lp.clone(), mc, field_name, origin_lp,
+            variant_did);
+    };
+
+    match (&parent_ty.sty, enum_variant_info) {
+        (&ty::ty_tup(ref v), None) => {
+            let tuple_idx = match *origin_field_name {
+                mc::PositionalField(tuple_idx) => tuple_idx,
+                mc::NamedField(_) =>
+                    panic!("tuple type {} should not have named fields.",
+                           parent_ty.repr(tcx)),
+            };
+            let tuple_len = v.len();
+            for i in range(0, tuple_len) {
+                if i == tuple_idx { continue }
+                let field_name = mc::PositionalField(i);
+                add_fragment_sibling_local(field_name, None);
+            }
+        }
+
+        (&ty::ty_struct(def_id, ref _substs), None) => {
+            let fields = ty::lookup_struct_fields(tcx, def_id);
+            match *origin_field_name {
+                mc::NamedField(ast_name) => {
+                    for f in fields.iter() {
+                        if f.name == ast_name {
+                            continue;
+                        }
+                        let field_name = mc::NamedField(f.name);
+                        add_fragment_sibling_local(field_name, None);
+                    }
+                }
+                mc::PositionalField(tuple_idx) => {
+                    for (i, _f) in fields.iter().enumerate() {
+                        if i == tuple_idx {
+                            continue
+                        }
+                        let field_name = mc::PositionalField(i);
+                        add_fragment_sibling_local(field_name, None);
+                    }
+                }
+            }
+        }
+
+        (&ty::ty_enum(enum_def_id, ref substs), ref enum_variant_info) => {
+            let variant_info = {
+                let mut variants = ty::substd_enum_variants(tcx, enum_def_id, substs);
+                match *enum_variant_info {
+                    Some((variant_def_id, ref _lp2)) =>
+                        variants.iter()
+                        .find(|variant| variant.id == variant_def_id)
+                        .expect("enum_variant_with_id(): no variant exists with that ID")
+                        .clone(),
+                    None => {
+                        assert_eq!(variants.len(), 1);
+                        variants.pop().unwrap()
+                    }
+                }
+            };
+            match *origin_field_name {
+                mc::NamedField(ast_name) => {
+                    let variant_arg_names = variant_info.arg_names.as_ref().unwrap();
+                    for variant_arg_ident in variant_arg_names.iter() {
+                        if variant_arg_ident.name == ast_name {
+                            continue;
+                        }
+                        let field_name = mc::NamedField(variant_arg_ident.name);
+                        add_fragment_sibling_local(field_name, Some(variant_info.id));
+                    }
+                }
+                mc::PositionalField(tuple_idx) => {
+                    let variant_arg_types = &variant_info.args;
+                    for (i, _variant_arg_ty) in variant_arg_types.iter().enumerate() {
+                        if tuple_idx == i {
+                            continue;
+                        }
+                        let field_name = mc::PositionalField(i);
+                        add_fragment_sibling_local(field_name, None);
+                    }
+                }
+            }
+        }
+
+        ref sty_and_variant_info => {
+            let msg = format!("type {} ({}) is not fragmentable",
+                              parent_ty.repr(tcx), sty_and_variant_info);
+            let opt_span = origin_id.and_then(|id|tcx.map.opt_span(id));
+            tcx.sess.opt_span_bug(opt_span, msg.as_slice())
+        }
+    }
+}
+
+/// Adds the single sibling `LpExtend(parent, new_field_name)` of `origin_lp` (the original
+/// loan-path).
+fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>,
+                                   tcx: &ty::ctxt<'tcx>,
+                                   gathered_fragments: &mut Vec<Fragment>,
+                                   parent: Rc<LoanPath<'tcx>>,
+                                   mc: mc::MutabilityCategory,
+                                   new_field_name: mc::FieldName,
+                                   origin_lp: &Rc<LoanPath<'tcx>>,
+                                   enum_variant_did: Option<ast::DefId>) -> MovePathIndex {
+    let opt_variant_did = match parent.kind {
+        LpDowncast(_, variant_did) => Some(variant_did),
+        LpVar(..) | LpUpvar(..) | LpExtend(..) => enum_variant_did,
+    };
+
+    let loan_path_elem = LpInterior(mc::InteriorField(new_field_name));
+    let new_lp_type = match new_field_name {
+        mc::NamedField(ast_name) =>
+            ty::named_element_ty(tcx, parent.to_type(), ast_name, opt_variant_did),
+        mc::PositionalField(idx) =>
+            ty::positional_element_ty(tcx, parent.to_type(), idx, opt_variant_did),
+    };
+    let new_lp_variant = LpExtend(parent, mc, loan_path_elem);
+    let new_lp = LoanPath::new(new_lp_variant, new_lp_type.unwrap());
+    debug!("add_fragment_sibling_core(new_lp={}, origin_lp={})",
+           new_lp.repr(tcx), origin_lp.repr(tcx));
+    let mp = this.move_path(tcx, Rc::new(new_lp));
+
+    // Do not worry about checking for duplicates here; we will sort
+    // and dedup after all are added.
+    gathered_fragments.push(Just(mp));
+
+    mp
+}
diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
new file mode 100644 (file)
index 0000000..01cbab6
--- /dev/null
@@ -0,0 +1,197 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Computes moves.
+
+use borrowck::*;
+use borrowck::LoanPathKind::*;
+use borrowck::gather_loans::move_error::MoveSpanAndPath;
+use borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
+use borrowck::move_data::*;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::ty;
+use rustc::util::ppaux::Repr;
+use std::rc::Rc;
+use syntax::ast;
+use syntax::codemap::Span;
+
+struct GatherMoveInfo<'tcx> {
+    id: ast::NodeId,
+    kind: MoveKind,
+    cmt: mc::cmt<'tcx>,
+    span_path_opt: Option<MoveSpanAndPath>
+}
+
+pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                             move_data: &MoveData<'tcx>,
+                             decl_id: ast::NodeId,
+                             _decl_span: Span,
+                             var_id: ast::NodeId) {
+    let ty = ty::node_id_to_type(bccx.tcx, var_id);
+    let loan_path = Rc::new(LoanPath::new(LpVar(var_id), ty));
+    move_data.add_move(bccx.tcx, loan_path, decl_id, Declared);
+}
+
+pub fn gather_move_from_expr<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                       move_data: &MoveData<'tcx>,
+                                       move_error_collector: &MoveErrorCollector<'tcx>,
+                                       move_expr_id: ast::NodeId,
+                                       cmt: mc::cmt<'tcx>,
+                                       move_reason: euv::MoveReason) {
+    let kind = match move_reason {
+        euv::DirectRefMove | euv::PatBindingMove => MoveExpr,
+        euv::CaptureMove => Captured
+    };
+    let move_info = GatherMoveInfo {
+        id: move_expr_id,
+        kind: kind,
+        cmt: cmt,
+        span_path_opt: None,
+    };
+    gather_move(bccx, move_data, move_error_collector, move_info);
+}
+
+pub fn gather_match_variant<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                      move_data: &MoveData<'tcx>,
+                                      _move_error_collector: &MoveErrorCollector<'tcx>,
+                                      move_pat: &ast::Pat,
+                                      cmt: mc::cmt<'tcx>,
+                                      mode: euv::MatchMode) {
+    let tcx = bccx.tcx;
+    debug!("gather_match_variant(move_pat={}, cmt={}, mode={})",
+           move_pat.id, cmt.repr(tcx), mode);
+
+    let opt_lp = opt_loan_path(&cmt);
+    match opt_lp {
+        Some(lp) => {
+            match lp.kind {
+                LpDowncast(ref base_lp, _) =>
+                    move_data.add_variant_match(
+                        tcx, lp.clone(), move_pat.id, base_lp.clone(), mode),
+                _ => panic!("should only call gather_match_variant \
+                             for cat_downcast cmt"),
+            }
+        }
+        None => {
+            // We get None when input to match is non-path (e.g.
+            // temporary result like a function call). Since no
+            // loan-path is being matched, no need to record a
+            // downcast.
+            return;
+        }
+    }
+}
+
+pub fn gather_move_from_pat<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                      move_data: &MoveData<'tcx>,
+                                      move_error_collector: &MoveErrorCollector<'tcx>,
+                                      move_pat: &ast::Pat,
+                                      cmt: mc::cmt<'tcx>) {
+    let pat_span_path_opt = match move_pat.node {
+        ast::PatIdent(_, ref path1, _) => {
+            Some(MoveSpanAndPath{span: move_pat.span,
+                                 ident: path1.node})
+        },
+        _ => None,
+    };
+    let move_info = GatherMoveInfo {
+        id: move_pat.id,
+        kind: MovePat,
+        cmt: cmt,
+        span_path_opt: pat_span_path_opt,
+    };
+    gather_move(bccx, move_data, move_error_collector, move_info);
+}
+
+fn gather_move<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                         move_data: &MoveData<'tcx>,
+                         move_error_collector: &MoveErrorCollector<'tcx>,
+                         move_info: GatherMoveInfo<'tcx>) {
+    debug!("gather_move(move_id={}, cmt={})",
+           move_info.id, move_info.cmt.repr(bccx.tcx));
+
+    let potentially_illegal_move =
+                check_and_get_illegal_move_origin(bccx, &move_info.cmt);
+    match potentially_illegal_move {
+        Some(illegal_move_origin) => {
+            debug!("illegal_move_origin={}", illegal_move_origin.repr(bccx.tcx));
+            let error = MoveError::with_move_info(illegal_move_origin,
+                                                  move_info.span_path_opt);
+            move_error_collector.add_error(error);
+            return
+        }
+        None => ()
+    }
+
+    match opt_loan_path(&move_info.cmt) {
+        Some(loan_path) => {
+            move_data.add_move(bccx.tcx, loan_path,
+                               move_info.id, move_info.kind);
+        }
+        None => {
+            // move from rvalue or unsafe pointer, hence ok
+        }
+    }
+}
+
+pub fn gather_assignment<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                   move_data: &MoveData<'tcx>,
+                                   assignment_id: ast::NodeId,
+                                   assignment_span: Span,
+                                   assignee_loan_path: Rc<LoanPath<'tcx>>,
+                                   assignee_id: ast::NodeId,
+                                   mode: euv::MutateMode) {
+    move_data.add_assignment(bccx.tcx,
+                             assignee_loan_path,
+                             assignment_id,
+                             assignment_span,
+                             assignee_id,
+                             mode);
+}
+
+fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                               cmt: &mc::cmt<'tcx>)
+                                               -> Option<mc::cmt<'tcx>> {
+    match cmt.cat {
+        mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
+        mc::cat_deref(_, _, mc::Implicit(..)) |
+        mc::cat_deref(_, _, mc::UnsafePtr(..)) |
+        mc::cat_static_item => {
+            Some(cmt.clone())
+        }
+
+        mc::cat_rvalue(..) |
+        mc::cat_local(..) |
+        mc::cat_upvar(..) => {
+            None
+        }
+
+        mc::cat_downcast(ref b, _) |
+        mc::cat_interior(ref b, _) => {
+            match b.ty.sty {
+                ty::ty_struct(did, _) | ty::ty_enum(did, _) => {
+                    if ty::has_dtor(bccx.tcx, did) {
+                        Some(cmt.clone())
+                    } else {
+                        check_and_get_illegal_move_origin(bccx, b)
+                    }
+                }
+                _ => {
+                    check_and_get_illegal_move_origin(bccx, b)
+                }
+            }
+        }
+
+        mc::cat_deref(ref b, _, mc::OwnedPtr) => {
+            check_and_get_illegal_move_origin(bccx, b)
+        }
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs b/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs
new file mode 100644 (file)
index 0000000..d7c9634
--- /dev/null
@@ -0,0 +1,145 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module implements the check that the lifetime of a borrow
+//! does not exceed the lifetime of the value being borrowed.
+
+use borrowck::*;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::region;
+use rustc::middle::ty;
+use rustc::util::ppaux::Repr;
+use syntax::ast;
+use syntax::codemap::Span;
+
+type R = Result<(),()>;
+
+pub fn guarantee_lifetime<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                    item_scope: region::CodeExtent,
+                                    span: Span,
+                                    cause: euv::LoanCause,
+                                    cmt: mc::cmt<'tcx>,
+                                    loan_region: ty::Region,
+                                    _: ty::BorrowKind)
+                                    -> Result<(),()> {
+    //! Reports error if `loan_region` is larger than S
+    //! where S is `item_scope` if `cmt` is an upvar,
+    //! and is scope of `cmt` otherwise.
+    debug!("guarantee_lifetime(cmt={}, loan_region={})",
+           cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx));
+    let ctxt = GuaranteeLifetimeContext {bccx: bccx,
+                                         item_scope: item_scope,
+                                         span: span,
+                                         cause: cause,
+                                         loan_region: loan_region,
+                                         cmt_original: cmt.clone()};
+    ctxt.check(&cmt, None)
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Private
+
+struct GuaranteeLifetimeContext<'a, 'tcx: 'a> {
+    bccx: &'a BorrowckCtxt<'a, 'tcx>,
+
+    // the scope of the function body for the enclosing item
+    item_scope: region::CodeExtent,
+
+    span: Span,
+    cause: euv::LoanCause,
+    loan_region: ty::Region,
+    cmt_original: mc::cmt<'tcx>
+}
+
+impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
+
+    fn check(&self, cmt: &mc::cmt<'tcx>, discr_scope: Option<ast::NodeId>) -> R {
+        //! Main routine. Walks down `cmt` until we find the
+        //! "guarantor".  Reports an error if `self.loan_region` is
+        //! larger than scope of `cmt`.
+        debug!("guarantee_lifetime.check(cmt={}, loan_region={})",
+               cmt.repr(self.bccx.tcx),
+               self.loan_region.repr(self.bccx.tcx));
+
+        match cmt.cat {
+            mc::cat_rvalue(..) |
+            mc::cat_local(..) |                         // L-Local
+            mc::cat_upvar(..) |
+            mc::cat_deref(_, _, mc::BorrowedPtr(..)) |  // L-Deref-Borrowed
+            mc::cat_deref(_, _, mc::Implicit(..)) |
+            mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
+                self.check_scope(self.scope(cmt))
+            }
+
+            mc::cat_static_item => {
+                Ok(())
+            }
+
+            mc::cat_downcast(ref base, _) |
+            mc::cat_deref(ref base, _, mc::OwnedPtr) |     // L-Deref-Send
+            mc::cat_interior(ref base, _) => {             // L-Field
+                self.check(base, discr_scope)
+            }
+        }
+    }
+
+    fn check_scope(&self, max_scope: ty::Region) -> R {
+        //! Reports an error if `loan_region` is larger than `max_scope`
+
+        if !self.bccx.is_subregion_of(self.loan_region, max_scope) {
+            Err(self.report_error(err_out_of_scope(max_scope, self.loan_region)))
+        } else {
+            Ok(())
+        }
+    }
+
+    fn scope(&self, cmt: &mc::cmt) -> ty::Region {
+        //! Returns the maximal region scope for the which the
+        //! lvalue `cmt` is guaranteed to be valid without any
+        //! rooting etc, and presuming `cmt` is not mutated.
+
+        // See the SCOPE(LV) function in doc.rs
+
+        match cmt.cat {
+            mc::cat_rvalue(temp_scope) => {
+                temp_scope
+            }
+            mc::cat_upvar(..) => {
+                ty::ReScope(self.item_scope)
+            }
+            mc::cat_static_item => {
+                ty::ReStatic
+            }
+            mc::cat_local(local_id) => {
+                ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id))
+            }
+            mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
+                ty::ReStatic
+            }
+            mc::cat_deref(_, _, mc::BorrowedPtr(_, r)) |
+            mc::cat_deref(_, _, mc::Implicit(_, r)) => {
+                r
+            }
+            mc::cat_downcast(ref cmt, _) |
+            mc::cat_deref(ref cmt, _, mc::OwnedPtr) |
+            mc::cat_interior(ref cmt, _) => {
+                self.scope(cmt)
+            }
+        }
+    }
+
+    fn report_error(&self, code: bckerr_code) {
+        self.bccx.report(BckError { cmt: self.cmt_original.clone(),
+                                    span: self.span,
+                                    cause: self.cause,
+                                    code: code });
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
new file mode 100644 (file)
index 0000000..08d12f8
--- /dev/null
@@ -0,0 +1,517 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ----------------------------------------------------------------------
+// Gathering loans
+//
+// The borrow check proceeds in two phases. In phase one, we gather the full
+// set of loans that are required at any point.  These are sorted according to
+// their associated scopes.  In phase two, checking loans, we will then make
+// sure that all of these loans are honored.
+
+use borrowck::*;
+use borrowck::LoanPathKind::*;
+use borrowck::move_data::MoveData;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::region;
+use rustc::middle::ty;
+use rustc::util::ppaux::{Repr};
+use syntax::ast;
+use syntax::codemap::Span;
+use syntax::visit;
+use syntax::visit::Visitor;
+use syntax::ast::{Expr, FnDecl, Block, NodeId, Pat};
+
+mod lifetime;
+mod restrictions;
+mod gather_moves;
+mod move_error;
+
+pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                    fn_id: NodeId,
+                                    decl: &ast::FnDecl,
+                                    body: &ast::Block)
+                                    -> (Vec<Loan<'tcx>>,
+                                        move_data::MoveData<'tcx>) {
+    let mut glcx = GatherLoanCtxt {
+        bccx: bccx,
+        all_loans: Vec::new(),
+        item_ub: region::CodeExtent::from_node_id(body.id),
+        move_data: MoveData::new(),
+        move_error_collector: move_error::MoveErrorCollector::new(),
+    };
+
+    let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
+
+    {
+        let mut euv = euv::ExprUseVisitor::new(&mut glcx,
+                                               bccx.tcx,
+                                               param_env);
+        euv.walk_fn(decl, body);
+    }
+
+    glcx.report_potential_errors();
+    let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
+    (all_loans, move_data)
+}
+
+struct GatherLoanCtxt<'a, 'tcx: 'a> {
+    bccx: &'a BorrowckCtxt<'a, 'tcx>,
+    move_data: move_data::MoveData<'tcx>,
+    move_error_collector: move_error::MoveErrorCollector<'tcx>,
+    all_loans: Vec<Loan<'tcx>>,
+    /// `item_ub` is used as an upper-bound on the lifetime whenever we
+    /// ask for the scope of an expression categorized as an upvar.
+    item_ub: region::CodeExtent,
+}
+
+impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
+    fn consume(&mut self,
+               consume_id: ast::NodeId,
+               _consume_span: Span,
+               cmt: mc::cmt<'tcx>,
+               mode: euv::ConsumeMode) {
+        debug!("consume(consume_id={}, cmt={}, mode={})",
+               consume_id, cmt.repr(self.tcx()), mode);
+
+        match mode {
+            euv::Move(move_reason) => {
+                gather_moves::gather_move_from_expr(
+                    self.bccx, &self.move_data, &self.move_error_collector,
+                    consume_id, cmt, move_reason);
+            }
+            euv::Copy => { }
+        }
+    }
+
+    fn matched_pat(&mut self,
+                   matched_pat: &ast::Pat,
+                   cmt: mc::cmt<'tcx>,
+                   mode: euv::MatchMode) {
+        debug!("matched_pat(matched_pat={}, cmt={}, mode={})",
+               matched_pat.repr(self.tcx()),
+               cmt.repr(self.tcx()),
+               mode);
+
+        if let mc::cat_downcast(..) = cmt.cat {
+            gather_moves::gather_match_variant(
+                self.bccx, &self.move_data, &self.move_error_collector,
+                matched_pat, cmt, mode);
+        }
+    }
+
+    fn consume_pat(&mut self,
+                   consume_pat: &ast::Pat,
+                   cmt: mc::cmt<'tcx>,
+                   mode: euv::ConsumeMode) {
+        debug!("consume_pat(consume_pat={}, cmt={}, mode={})",
+               consume_pat.repr(self.tcx()),
+               cmt.repr(self.tcx()),
+               mode);
+
+        match mode {
+            euv::Copy => { return; }
+            euv::Move(_) => { }
+        }
+
+        gather_moves::gather_move_from_pat(
+            self.bccx, &self.move_data, &self.move_error_collector,
+            consume_pat, cmt);
+    }
+
+    fn borrow(&mut self,
+              borrow_id: ast::NodeId,
+              borrow_span: Span,
+              cmt: mc::cmt<'tcx>,
+              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);
+
+        self.guarantee_valid(borrow_id,
+                             borrow_span,
+                             cmt,
+                             bk,
+                             loan_region,
+                             loan_cause);
+    }
+
+    fn mutate(&mut self,
+              assignment_id: ast::NodeId,
+              assignment_span: Span,
+              assignee_cmt: mc::cmt<'tcx>,
+              mode: euv::MutateMode)
+    {
+        debug!("mutate(assignment_id={}, assignee_cmt={})",
+               assignment_id, assignee_cmt.repr(self.tcx()));
+
+        match opt_loan_path(&assignee_cmt) {
+            Some(lp) => {
+                gather_moves::gather_assignment(self.bccx, &self.move_data,
+                                                assignment_id, assignment_span,
+                                                lp, assignee_cmt.id, mode);
+            }
+            None => {
+                // This can occur with e.g. `*foo() = 5`.  In such
+                // cases, there is no need to check for conflicts
+                // with moves etc, just ignore.
+            }
+        }
+    }
+
+    fn decl_without_init(&mut self, id: ast::NodeId, span: Span) {
+        gather_moves::gather_decl(self.bccx, &self.move_data, id, span, id);
+    }
+}
+
+/// Implements the A-* rules in doc.rs.
+fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                borrow_span: Span,
+                                loan_cause: euv::LoanCause,
+                                cmt: mc::cmt<'tcx>,
+                                req_kind: ty::BorrowKind)
+                                -> Result<(),()> {
+
+    match (cmt.freely_aliasable(bccx.tcx), req_kind) {
+        (None, _) => {
+            /* Uniquely accessible path -- OK for `&` and `&mut` */
+            Ok(())
+        }
+        (Some(mc::AliasableStatic(safety)), ty::ImmBorrow) => {
+            // Borrow of an immutable static item:
+            match safety {
+                mc::InteriorUnsafe => {
+                    // If the static item contains an Unsafe<T>, it has interior
+                    // mutability.  In such cases, another phase of the compiler
+                    // will ensure that the type is `Sync` and then trans will
+                    // not put it in rodata, so this is ok to allow.
+                    Ok(())
+                }
+                mc::InteriorSafe => {
+                    // Immutable static can be borrowed, no problem.
+                    Ok(())
+                }
+            }
+        }
+        (Some(mc::AliasableStaticMut(..)), _) => {
+            // Even touching a static mut is considered unsafe. We assume the
+            // user knows what they're doing in these cases.
+            Ok(())
+        }
+        (Some(alias_cause), ty::UniqueImmBorrow) |
+        (Some(alias_cause), ty::MutBorrow) => {
+            bccx.report_aliasability_violation(
+                        borrow_span,
+                        BorrowViolation(loan_cause),
+                        alias_cause);
+            Err(())
+        }
+        (_, _) => {
+            Ok(())
+        }
+    }
+}
+
+impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
+    pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
+
+    /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
+    /// reports an error.  This may entail taking out loans, which will be added to the
+    /// `req_loan_map`.
+    fn guarantee_valid(&mut self,
+                       borrow_id: ast::NodeId,
+                       borrow_span: Span,
+                       cmt: mc::cmt<'tcx>,
+                       req_kind: ty::BorrowKind,
+                       loan_region: ty::Region,
+                       cause: euv::LoanCause) {
+        debug!("guarantee_valid(borrow_id={}, cmt={}, \
+                req_mutbl={}, loan_region={})",
+               borrow_id,
+               cmt.repr(self.tcx()),
+               req_kind,
+               loan_region);
+
+        // a loan for the empty region can never be dereferenced, so
+        // it is always safe
+        if loan_region == ty::ReEmpty {
+            return;
+        }
+
+        // Check that the lifetime of the borrow does not exceed
+        // the lifetime of the data being borrowed.
+        if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
+                                        borrow_span, cause, cmt.clone(), loan_region,
+                                        req_kind).is_err() {
+            return; // reported an error, no sense in reporting more.
+        }
+
+        // Check that we don't allow mutable borrows of non-mutable data.
+        if check_mutability(self.bccx, borrow_span, cause,
+                            cmt.clone(), req_kind).is_err() {
+            return; // reported an error, no sense in reporting more.
+        }
+
+        // Check that we don't allow mutable borrows of aliasable data.
+        if check_aliasability(self.bccx, borrow_span, cause,
+                              cmt.clone(), req_kind).is_err() {
+            return; // reported an error, no sense in reporting more.
+        }
+
+        // Compute the restrictions that are required to enforce the
+        // loan is safe.
+        let restr = restrictions::compute_restrictions(
+            self.bccx, borrow_span, cause,
+            cmt.clone(), loan_region);
+
+        debug!("guarantee_valid(): restrictions={}", restr);
+
+        // Create the loan record (if needed).
+        let loan = match restr {
+            restrictions::Safe => {
+                // No restrictions---no loan record necessary
+                return;
+            }
+
+            restrictions::SafeIf(loan_path, restricted_paths) => {
+                let loan_scope = match loan_region {
+                    ty::ReScope(scope) => scope,
+
+                    ty::ReFree(ref fr) => fr.scope,
+
+                    ty::ReStatic => {
+                        // If we get here, an error must have been
+                        // reported in
+                        // `lifetime::guarantee_lifetime()`, because
+                        // the only legal ways to have a borrow with a
+                        // static lifetime should not require
+                        // restrictions. To avoid reporting derived
+                        // errors, we just return here without adding
+                        // any loans.
+                        return;
+                    }
+
+                    ty::ReEmpty |
+                    ty::ReLateBound(..) |
+                    ty::ReEarlyBound(..) |
+                    ty::ReInfer(..) => {
+                        self.tcx().sess.span_bug(
+                            cmt.span,
+                            format!("invalid borrow lifetime: {}",
+                                    loan_region).as_slice());
+                    }
+                };
+                debug!("loan_scope = {}", loan_scope);
+
+                let borrow_scope = region::CodeExtent::from_node_id(borrow_id);
+                let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
+                debug!("gen_scope = {}", gen_scope);
+
+                let kill_scope = self.compute_kill_scope(loan_scope, &*loan_path);
+                debug!("kill_scope = {}", kill_scope);
+
+                if req_kind == ty::MutBorrow {
+                    self.mark_loan_path_as_mutated(&*loan_path);
+                }
+
+                Loan {
+                    index: self.all_loans.len(),
+                    loan_path: loan_path,
+                    kind: req_kind,
+                    gen_scope: gen_scope,
+                    kill_scope: kill_scope,
+                    span: borrow_span,
+                    restricted_paths: restricted_paths,
+                    cause: cause,
+                }
+            }
+        };
+
+        debug!("guarantee_valid(borrow_id={}), loan={}",
+               borrow_id, loan.repr(self.tcx()));
+
+        // let loan_path = loan.loan_path;
+        // let loan_gen_scope = loan.gen_scope;
+        // let loan_kill_scope = loan.kill_scope;
+        self.all_loans.push(loan);
+
+        // if loan_gen_scope != borrow_id {
+            // FIXME(#6268) Nested method calls
+            //
+            // Typically, the scope of the loan includes the point at
+            // which the loan is originated. This
+            // This is a subtle case. See the test case
+            // <compile-fail/borrowck-bad-nested-calls-free.rs>
+            // to see what we are guarding against.
+
+            //let restr = restrictions::compute_restrictions(
+            //    self.bccx, borrow_span, cmt, RESTR_EMPTY);
+            //let loan = {
+            //    let all_loans = &mut *self.all_loans; // FIXME(#5074)
+            //    Loan {
+            //        index: all_loans.len(),
+            //        loan_path: loan_path,
+            //        cmt: cmt,
+            //        mutbl: ConstMutability,
+            //        gen_scope: borrow_id,
+            //        kill_scope: kill_scope,
+            //        span: borrow_span,
+            //        restrictions: restrictions
+            //    }
+        // }
+
+        fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                      borrow_span: Span,
+                                      cause: euv::LoanCause,
+                                      cmt: mc::cmt<'tcx>,
+                                      req_kind: ty::BorrowKind)
+                                      -> Result<(),()> {
+            //! Implements the M-* rules in doc.rs.
+
+            match req_kind {
+                ty::UniqueImmBorrow | ty::ImmBorrow => {
+                    match cmt.mutbl {
+                        // I am intentionally leaving this here to help
+                        // refactoring if, in the future, we should add new
+                        // kinds of mutability.
+                        mc::McImmutable | mc::McDeclared | mc::McInherited => {
+                            // both imm and mut data can be lent as imm;
+                            // for mutable data, this is a freeze
+                            Ok(())
+                        }
+                    }
+                }
+
+                ty::MutBorrow => {
+                    // Only mutable data can be lent as mutable.
+                    if !cmt.mutbl.is_mutable() {
+                        Err(bccx.report(BckError { span: borrow_span,
+                                                   cause: cause,
+                                                   cmt: cmt,
+                                                   code: err_mutbl }))
+                    } else {
+                        Ok(())
+                    }
+                }
+            }
+        }
+    }
+
+    pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
+        //! For mutable loans of content whose mutability derives
+        //! from a local variable, mark the mutability decl as necessary.
+
+        match loan_path.kind {
+            LpVar(local_id) |
+            LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
+                self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
+            }
+            LpDowncast(ref base, _) |
+            LpExtend(ref base, mc::McInherited, _) |
+            LpExtend(ref base, mc::McDeclared, _) => {
+                self.mark_loan_path_as_mutated(&**base);
+            }
+            LpExtend(_, mc::McImmutable, _) => {
+                // Nothing to do.
+            }
+        }
+    }
+
+    pub fn compute_gen_scope(&self,
+                             borrow_scope: region::CodeExtent,
+                             loan_scope: region::CodeExtent)
+                             -> region::CodeExtent {
+        //! Determine when to introduce the loan. Typically the loan
+        //! is introduced at the point of the borrow, but in some cases,
+        //! notably method arguments, the loan may be introduced only
+        //! later, once it comes into scope.
+
+        if self.bccx.tcx.region_maps.is_subscope_of(borrow_scope, loan_scope) {
+            borrow_scope
+        } else {
+            loan_scope
+        }
+    }
+
+    pub fn compute_kill_scope(&self, loan_scope: region::CodeExtent, lp: &LoanPath<'tcx>)
+                              -> region::CodeExtent {
+        //! Determine when the loan restrictions go out of scope.
+        //! This is either when the lifetime expires or when the
+        //! local variable which roots the loan-path goes out of scope,
+        //! whichever happens faster.
+        //!
+        //! It may seem surprising that we might have a loan region
+        //! larger than the variable which roots the loan-path; this can
+        //! come about when variables of `&mut` type are re-borrowed,
+        //! as in this example:
+        //!
+        //!     fn counter<'a>(v: &'a mut Foo) -> &'a mut uint {
+        //!         &mut v.counter
+        //!     }
+        //!
+        //! In this case, the reference (`'a`) outlives the
+        //! variable `v` that hosts it. Note that this doesn't come up
+        //! with immutable `&` pointers, because borrows of such pointers
+        //! do not require restrictions and hence do not cause a loan.
+
+        let lexical_scope = lp.kill_scope(self.bccx.tcx);
+        let rm = &self.bccx.tcx.region_maps;
+        if rm.is_subscope_of(lexical_scope, loan_scope) {
+            lexical_scope
+        } else {
+            assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope));
+            loan_scope
+        }
+    }
+
+    pub fn report_potential_errors(&self) {
+        self.move_error_collector.report_potential_errors(self.bccx);
+    }
+}
+
+/// Context used while gathering loans on static initializers
+///
+/// This visitor walks static initializer's expressions and makes
+/// sure the loans being taken are sound.
+struct StaticInitializerCtxt<'a, 'tcx: 'a> {
+    bccx: &'a BorrowckCtxt<'a, 'tcx>
+}
+
+impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
+    fn visit_expr(&mut self, ex: &Expr) {
+        if let ast::ExprAddrOf(mutbl, ref base) = ex.node {
+            let base_cmt = self.bccx.cat_expr(&**base);
+            let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
+            // Check that we don't allow borrows of unsafe static items.
+            if check_aliasability(self.bccx, ex.span, euv::AddrOf,
+                                  base_cmt, borrow_kind).is_err() {
+                return; // reported an error, no sense in reporting more.
+            }
+        }
+
+        visit::walk_expr(self, ex);
+    }
+}
+
+pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &ast::Expr) {
+
+    debug!("gather_loans_in_static_initializer(expr={})", expr.repr(bccx.tcx));
+
+    let mut sicx = StaticInitializerCtxt {
+        bccx: bccx
+    };
+
+    sicx.visit_expr(expr);
+}
diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
new file mode 100644 (file)
index 0000000..fbe7815
--- /dev/null
@@ -0,0 +1,164 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use borrowck::BorrowckCtxt;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::ty;
+use rustc::util::ppaux::UserString;
+use std::cell::RefCell;
+use syntax::ast;
+use syntax::codemap;
+use syntax::print::pprust;
+
+pub struct MoveErrorCollector<'tcx> {
+    errors: RefCell<Vec<MoveError<'tcx>>>
+}
+
+impl<'tcx> MoveErrorCollector<'tcx> {
+    pub fn new() -> MoveErrorCollector<'tcx> {
+        MoveErrorCollector {
+            errors: RefCell::new(Vec::new())
+        }
+    }
+
+    pub fn add_error(&self, error: MoveError<'tcx>) {
+        self.errors.borrow_mut().push(error);
+    }
+
+    pub fn report_potential_errors<'a>(&self, bccx: &BorrowckCtxt<'a, 'tcx>) {
+        report_move_errors(bccx, self.errors.borrow().deref())
+    }
+}
+
+pub struct MoveError<'tcx> {
+    move_from: mc::cmt<'tcx>,
+    move_to: Option<MoveSpanAndPath>
+}
+
+impl<'tcx> MoveError<'tcx> {
+    pub fn with_move_info(move_from: mc::cmt<'tcx>,
+                          move_to: Option<MoveSpanAndPath>)
+                          -> MoveError<'tcx> {
+        MoveError {
+            move_from: move_from,
+            move_to: move_to,
+        }
+    }
+}
+
+#[deriving(Clone)]
+pub struct MoveSpanAndPath {
+    pub span: codemap::Span,
+    pub ident: ast::Ident
+}
+
+pub struct GroupedMoveErrors<'tcx> {
+    move_from: mc::cmt<'tcx>,
+    move_to_places: Vec<MoveSpanAndPath>
+}
+
+fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                errors: &Vec<MoveError<'tcx>>) {
+    let grouped_errors = group_errors_with_same_origin(errors);
+    for error in grouped_errors.iter() {
+        report_cannot_move_out_of(bccx, error.move_from.clone());
+        let mut is_first_note = true;
+        for move_to in error.move_to_places.iter() {
+            note_move_destination(bccx, move_to.span,
+                                  &move_to.ident, is_first_note);
+            is_first_note = false;
+        }
+    }
+}
+
+fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
+                                       -> Vec<GroupedMoveErrors<'tcx>> {
+    let mut grouped_errors = Vec::new();
+    for error in errors.iter() {
+        append_to_grouped_errors(&mut grouped_errors, error)
+    }
+    return grouped_errors;
+
+    fn append_to_grouped_errors<'tcx>(grouped_errors: &mut Vec<GroupedMoveErrors<'tcx>>,
+                                      error: &MoveError<'tcx>) {
+        let move_from_id = error.move_from.id;
+        debug!("append_to_grouped_errors(move_from_id={})", move_from_id);
+        let move_to = if error.move_to.is_some() {
+            vec!(error.move_to.clone().unwrap())
+        } else {
+            Vec::new()
+        };
+        for ge in grouped_errors.iter_mut() {
+            if move_from_id == ge.move_from.id && error.move_to.is_some() {
+                debug!("appending move_to to list");
+                ge.move_to_places.extend(move_to.into_iter());
+                return
+            }
+        }
+        debug!("found a new move from location");
+        grouped_errors.push(GroupedMoveErrors {
+            move_from: error.move_from.clone(),
+            move_to_places: move_to
+        })
+    }
+}
+
+fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                       move_from: mc::cmt<'tcx>) {
+    match move_from.cat {
+        mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
+        mc::cat_deref(_, _, mc::Implicit(..)) |
+        mc::cat_deref(_, _, mc::UnsafePtr(..)) |
+        mc::cat_static_item => {
+            bccx.span_err(
+                move_from.span,
+                format!("cannot move out of {}",
+                        bccx.cmt_to_string(&*move_from)).as_slice());
+        }
+
+        mc::cat_downcast(ref b, _) |
+        mc::cat_interior(ref b, _) => {
+            match b.ty.sty {
+                ty::ty_struct(did, _)
+                | ty::ty_enum(did, _) if ty::has_dtor(bccx.tcx, did) => {
+                    bccx.span_err(
+                        move_from.span,
+                        format!("cannot move out of type `{}`, \
+                                 which defines the `Drop` trait",
+                                b.ty.user_string(bccx.tcx)).as_slice());
+                },
+                _ => panic!("this path should not cause illegal move")
+            }
+        }
+        _ => panic!("this path should not cause illegal move")
+    }
+}
+
+fn note_move_destination(bccx: &BorrowckCtxt,
+                         move_to_span: codemap::Span,
+                         pat_ident: &ast::Ident,
+                         is_first_note: bool) {
+    let pat_name = pprust::ident_to_string(pat_ident);
+    if is_first_note {
+        bccx.span_note(
+            move_to_span,
+            "attempting to move value to here");
+        bccx.span_help(
+            move_to_span,
+            format!("to prevent the move, \
+                     use `ref {0}` or `ref mut {0}` to capture value by \
+                     reference",
+                    pat_name).as_slice());
+    } else {
+        bccx.span_note(move_to_span,
+                       format!("and here (use `ref {0}` or `ref mut {0}`)",
+                               pat_name).as_slice());
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
new file mode 100644 (file)
index 0000000..c783489
--- /dev/null
@@ -0,0 +1,169 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Computes the restrictions that result from a borrow.
+
+pub use self::RestrictionResult::*;
+
+use borrowck::*;
+use borrowck::LoanPathElem::*;
+use borrowck::LoanPathKind::*;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::ty;
+use rustc::util::ppaux::Repr;
+use syntax::codemap::Span;
+
+use std::rc::Rc;
+
+#[deriving(Show)]
+pub enum RestrictionResult<'tcx> {
+    Safe,
+    SafeIf(Rc<LoanPath<'tcx>>, Vec<Rc<LoanPath<'tcx>>>)
+}
+
+pub fn compute_restrictions<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
+                                      span: Span,
+                                      cause: euv::LoanCause,
+                                      cmt: mc::cmt<'tcx>,
+                                      loan_region: ty::Region)
+                                      -> RestrictionResult<'tcx> {
+    let ctxt = RestrictionsContext {
+        bccx: bccx,
+        span: span,
+        cause: cause,
+        loan_region: loan_region,
+    };
+
+    ctxt.restrict(cmt)
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Private
+
+struct RestrictionsContext<'a, 'tcx: 'a> {
+    bccx: &'a BorrowckCtxt<'a, 'tcx>,
+    span: Span,
+    loan_region: ty::Region,
+    cause: euv::LoanCause,
+}
+
+impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
+    fn restrict(&self,
+                cmt: mc::cmt<'tcx>) -> RestrictionResult<'tcx> {
+        debug!("restrict(cmt={})", cmt.repr(self.bccx.tcx));
+
+        let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
+
+        match cmt.cat.clone() {
+            mc::cat_rvalue(..) => {
+                // Effectively, rvalues are stored into a
+                // non-aliasable temporary on the stack. Since they
+                // are inherently non-aliasable, they can only be
+                // accessed later through the borrow itself and hence
+                // must inherently comply with its terms.
+                Safe
+            }
+
+            mc::cat_local(local_id) => {
+                // R-Variable, locally declared
+                let lp = new_lp(LpVar(local_id));
+                SafeIf(lp.clone(), vec![lp])
+            }
+
+            mc::cat_upvar(mc::Upvar { id, .. }) => {
+                // R-Variable, captured into closure
+                let lp = new_lp(LpUpvar(id));
+                SafeIf(lp.clone(), vec![lp])
+            }
+
+            mc::cat_downcast(cmt_base, _) => {
+                // When we borrow the interior of an enum, we have to
+                // ensure the enum itself is not mutated, because that
+                // could cause the type of the memory to change.
+                self.restrict(cmt_base)
+            }
+
+            mc::cat_interior(cmt_base, i) => {
+                // R-Field
+                //
+                // Overwriting the base would not change the type of
+                // the memory, so no additional restrictions are
+                // needed.
+                let result = self.restrict(cmt_base);
+                self.extend(result, &cmt, LpInterior(i))
+            }
+
+            mc::cat_static_item(..) => {
+                Safe
+            }
+
+            mc::cat_deref(cmt_base, _, pk) => {
+                match pk {
+                    mc::OwnedPtr => {
+                        // R-Deref-Send-Pointer
+                        //
+                        // When we borrow the interior of an owned pointer, we
+                        // cannot permit the base to be mutated, because that
+                        // would cause the unique pointer to be freed.
+                        //
+                        // Eventually we should make these non-special and
+                        // just rely on Deref<T> implementation.
+                        let result = self.restrict(cmt_base);
+                        self.extend(result, &cmt, LpDeref(pk))
+                    }
+                    mc::Implicit(bk, lt) | mc::BorrowedPtr(bk, lt) => {
+                        // R-Deref-[Mut-]Borrowed
+                        if !self.bccx.is_subregion_of(self.loan_region, lt) {
+                            self.bccx.report(
+                                BckError {
+                                    span: self.span,
+                                    cause: self.cause,
+                                    cmt: cmt_base,
+                                    code: err_borrowed_pointer_too_short(
+                                        self.loan_region, lt)});
+                            return Safe;
+                        }
+
+                        match bk {
+                            ty::ImmBorrow => Safe,
+                            ty::MutBorrow | ty::UniqueImmBorrow => {
+                                // R-Deref-Mut-Borrowed
+                                //
+                                // The referent can be aliased after the
+                                // references lifetime ends (by a newly-unfrozen
+                                // borrow).
+                                let result = self.restrict(cmt_base);
+                                self.extend(result, &cmt, LpDeref(pk))
+                            }
+                        }
+                    }
+                    // Borrowck is not relevant for unsafe pointers
+                    mc::UnsafePtr(..) => Safe
+                }
+            }
+        }
+    }
+
+    fn extend(&self,
+              result: RestrictionResult<'tcx>,
+              cmt: &mc::cmt<'tcx>,
+              elem: LoanPathElem) -> RestrictionResult<'tcx> {
+        match result {
+            Safe => Safe,
+            SafeIf(base_lp, mut base_vec) => {
+                let v = LpExtend(base_lp, cmt.mutbl, elem);
+                let lp = Rc::new(LoanPath::new(v, cmt.ty));
+                base_vec.push(lp.clone());
+                SafeIf(lp, base_vec)
+            }
+        }
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
new file mode 100644 (file)
index 0000000..1722f9a
--- /dev/null
@@ -0,0 +1,1111 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! See doc.rs for a thorough explanation of the borrow checker
+
+#![allow(non_camel_case_types)]
+
+pub use self::LoanPathKind::*;
+pub use self::LoanPathElem::*;
+pub use self::bckerr_code::*;
+pub use self::AliasableViolationKind::*;
+pub use self::MovedValueUseKind::*;
+
+use rustc::middle::cfg;
+use rustc::middle::dataflow::DataFlowContext;
+use rustc::middle::dataflow::BitwiseOperator;
+use rustc::middle::dataflow::DataFlowOperator;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::region;
+use rustc::middle::ty::{mod, Ty};
+use rustc::util::ppaux::{note_and_explain_region, Repr, UserString};
+use std::rc::Rc;
+use std::string::String;
+use syntax::ast;
+use syntax::ast_map;
+use syntax::ast_map::blocks::{FnLikeNode, FnParts};
+use syntax::ast_util;
+use syntax::codemap::Span;
+use syntax::parse::token;
+use syntax::visit;
+use syntax::visit::{Visitor, FnKind};
+use syntax::ast::{FnDecl, Block, NodeId};
+
+macro_rules! if_ok(
+    ($inp: expr) => (
+        match $inp {
+            Ok(v) => { v }
+            Err(e) => { return Err(e); }
+        }
+    )
+)
+
+pub mod doc;
+
+pub mod check_loans;
+
+pub mod gather_loans;
+
+pub mod move_data;
+
+#[deriving(Clone)]
+pub struct LoanDataFlowOperator;
+
+impl Copy for LoanDataFlowOperator {}
+
+pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
+
+impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
+    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
+                b: &'v Block, s: Span, id: ast::NodeId) {
+        borrowck_fn(self, fk, fd, b, s, id);
+    }
+
+    fn visit_item(&mut self, item: &ast::Item) {
+        borrowck_item(self, item);
+    }
+}
+
+pub fn check_crate(tcx: &ty::ctxt) {
+    let mut bccx = BorrowckCtxt {
+        tcx: tcx,
+        stats: BorrowStats {
+            loaned_paths_same: 0,
+            loaned_paths_imm: 0,
+            stable_paths: 0,
+            guaranteed_paths: 0
+        }
+    };
+
+    visit::walk_crate(&mut bccx, tcx.map.krate());
+
+    if tcx.sess.borrowck_stats() {
+        println!("--- borrowck stats ---");
+        println!("paths requiring guarantees: {}",
+                 bccx.stats.guaranteed_paths);
+        println!("paths requiring loans     : {}",
+                 make_stat(&bccx, bccx.stats.loaned_paths_same));
+        println!("paths requiring imm loans : {}",
+                 make_stat(&bccx, bccx.stats.loaned_paths_imm));
+        println!("stable paths              : {}",
+                 make_stat(&bccx, bccx.stats.stable_paths));
+    }
+
+    fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> String {
+        let total = bccx.stats.guaranteed_paths as f64;
+        let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
+        format!("{} ({:.0}%)", stat, perc)
+    }
+}
+
+fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
+    // Gather loans for items. Note that we don't need
+    // to check loans for single expressions. The check
+    // loan step is intended for things that have a data
+    // flow dependent conditions.
+    match item.node {
+        ast::ItemStatic(_, _, ref ex) |
+        ast::ItemConst(_, ref ex) => {
+            gather_loans::gather_loans_in_static_initializer(this, &**ex);
+        }
+        _ => {
+            visit::walk_item(this, item);
+        }
+    }
+}
+
+/// Collection of conclusions determined via borrow checker analyses.
+pub struct AnalysisData<'a, 'tcx: 'a> {
+    pub all_loans: Vec<Loan<'tcx>>,
+    pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
+    pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
+}
+
+fn borrowck_fn(this: &mut BorrowckCtxt,
+               fk: FnKind,
+               decl: &ast::FnDecl,
+               body: &ast::Block,
+               sp: Span,
+               id: ast::NodeId) {
+    debug!("borrowck_fn(id={})", id);
+    let cfg = cfg::CFG::new(this.tcx, body);
+    let AnalysisData { all_loans,
+                       loans: loan_dfcx,
+                       move_data:flowed_moves } =
+        build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
+
+    move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
+                                                    this.tcx, sp, id);
+
+    check_loans::check_loans(this,
+                             &loan_dfcx,
+                             flowed_moves,
+                             all_loans.as_slice(),
+                             id,
+                             decl,
+                             body);
+
+    visit::walk_fn(this, fk, decl, body, sp);
+}
+
+fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
+                                          fk: FnKind,
+                                          decl: &ast::FnDecl,
+                                          cfg: &cfg::CFG,
+                                          body: &ast::Block,
+                                          sp: Span,
+                                          id: ast::NodeId) -> AnalysisData<'a, 'tcx> {
+    // Check the body of fn items.
+    let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
+    let (all_loans, move_data) =
+        gather_loans::gather_loans_in_fn(this, id, decl, body);
+
+    let mut loan_dfcx =
+        DataFlowContext::new(this.tcx,
+                             "borrowck",
+                             Some(decl),
+                             cfg,
+                             LoanDataFlowOperator,
+                             id_range,
+                             all_loans.len());
+    for (loan_idx, loan) in all_loans.iter().enumerate() {
+        loan_dfcx.add_gen(loan.gen_scope.node_id(), loan_idx);
+        loan_dfcx.add_kill(loan.kill_scope.node_id(), loan_idx);
+    }
+    loan_dfcx.add_kills_from_flow_exits(cfg);
+    loan_dfcx.propagate(cfg, body);
+
+    let flowed_moves = move_data::FlowedMoveData::new(move_data,
+                                                      this.tcx,
+                                                      cfg,
+                                                      id_range,
+                                                      decl,
+                                                      body);
+
+    AnalysisData { all_loans: all_loans,
+                   loans: loan_dfcx,
+                   move_data:flowed_moves }
+}
+
+/// This and a `ty::ctxt` is all you need to run the dataflow analyses
+/// used in the borrow checker.
+pub struct FnPartsWithCFG<'a> {
+    pub fn_parts: FnParts<'a>,
+    pub cfg:  &'a cfg::CFG,
+}
+
+impl<'a> FnPartsWithCFG<'a> {
+    pub fn from_fn_like(f: &'a FnLikeNode,
+                        g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
+        FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
+    }
+}
+
+/// Accessor for introspective clients inspecting `AnalysisData` and
+/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
+pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
+    tcx: &'a ty::ctxt<'tcx>,
+    input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) {
+
+    let mut bccx = BorrowckCtxt {
+        tcx: tcx,
+        stats: BorrowStats {
+            loaned_paths_same: 0,
+            loaned_paths_imm: 0,
+            stable_paths: 0,
+            guaranteed_paths: 0
+        }
+    };
+
+    let p = input.fn_parts;
+
+    let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
+                                                     p.kind,
+                                                     &*p.decl,
+                                                     input.cfg,
+                                                     &*p.body,
+                                                     p.span,
+                                                     p.id);
+
+    (bccx, dataflow_data)
+}
+
+// ----------------------------------------------------------------------
+// Type definitions
+
+pub struct BorrowckCtxt<'a, 'tcx: 'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+
+    // Statistics:
+    stats: BorrowStats
+}
+
+struct BorrowStats {
+    loaned_paths_same: uint,
+    loaned_paths_imm: uint,
+    stable_paths: uint,
+    guaranteed_paths: uint
+}
+
+pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
+
+///////////////////////////////////////////////////////////////////////////
+// Loans and loan paths
+
+/// Record of a loan that was issued.
+pub struct Loan<'tcx> {
+    index: uint,
+    loan_path: Rc<LoanPath<'tcx>>,
+    kind: ty::BorrowKind,
+    restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
+
+    /// gen_scope indicates where loan is introduced. Typically the
+    /// loan is introduced at the point of the borrow, but in some
+    /// cases, notably method arguments, the loan may be introduced
+    /// only later, once it comes into scope.  See also
+    /// `GatherLoanCtxt::compute_gen_scope`.
+    gen_scope: region::CodeExtent,
+
+    /// kill_scope indicates when the loan goes out of scope.  This is
+    /// either when the lifetime expires or when the local variable
+    /// which roots the loan-path goes out of scope, whichever happens
+    /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
+    kill_scope: region::CodeExtent,
+    span: Span,
+    cause: euv::LoanCause,
+}
+
+impl<'tcx> Loan<'tcx> {
+    pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
+        self.loan_path.clone()
+    }
+}
+
+#[deriving(Eq, Hash, Show)]
+pub struct LoanPath<'tcx> {
+    kind: LoanPathKind<'tcx>,
+    ty: ty::Ty<'tcx>,
+}
+
+impl<'tcx> PartialEq for LoanPath<'tcx> {
+    fn eq(&self, that: &LoanPath<'tcx>) -> bool {
+        let r = self.kind == that.kind;
+        debug_assert!(self.ty == that.ty || !r,
+                      "Somehow loan paths are equal though their tys are not.");
+        r
+    }
+}
+
+#[deriving(PartialEq, Eq, Hash, Show)]
+pub enum LoanPathKind<'tcx> {
+    LpVar(ast::NodeId),                         // `x` in doc.rs
+    LpUpvar(ty::UpvarId),                       // `x` captured by-value into closure
+    LpDowncast(Rc<LoanPath<'tcx>>, ast::DefId), // `x` downcast to particular enum variant
+    LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
+}
+
+impl<'tcx> LoanPath<'tcx> {
+    fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
+        LoanPath { kind: kind, ty: ty }
+    }
+
+    fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
+}
+
+// FIXME (pnkfelix): See discussion here
+// https://github.com/pnkfelix/rust/commit/
+//     b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
+static DOWNCAST_PRINTED_OPERATOR : &'static str = " as ";
+
+#[deriving(PartialEq, Eq, Hash, Show)]
+pub enum LoanPathElem {
+    LpDeref(mc::PointerKind),    // `*LV` in doc.rs
+    LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
+}
+
+impl Copy for LoanPathElem {}
+
+pub fn closure_to_block(closure_id: ast::NodeId,
+                        tcx: &ty::ctxt) -> ast::NodeId {
+    match tcx.map.get(closure_id) {
+        ast_map::NodeExpr(expr) => match expr.node {
+            ast::ExprProc(_, ref block) |
+            ast::ExprClosure(_, _, _, ref block) => {
+                block.id
+            }
+            _ => {
+                panic!("encountered non-closure id: {}", closure_id)
+            }
+        },
+        _ => panic!("encountered non-expr id: {}", closure_id)
+    }
+}
+
+impl<'tcx> LoanPath<'tcx> {
+    pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
+        match self.kind {
+            LpVar(local_id) => tcx.region_maps.var_scope(local_id),
+            LpUpvar(upvar_id) => {
+                let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
+                region::CodeExtent::from_node_id(block_id)
+            }
+            LpDowncast(ref base, _) |
+            LpExtend(ref base, _, _) => base.kill_scope(tcx),
+        }
+    }
+
+    fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
+        match (&self.kind, &other.kind) {
+            (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
+                if id == id2 {
+                    base.has_fork(&**base2)
+                } else {
+                    true
+                },
+            (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
+            (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
+            _ => false,
+        }
+    }
+
+    fn depth(&self) -> uint {
+        match self.kind {
+            LpExtend(ref base, _, LpDeref(_)) => base.depth(),
+            LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
+            _ => 0,
+        }
+    }
+
+    fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
+        match (&self.kind, &other.kind) {
+            (&LpExtend(ref base, a, LpInterior(id)),
+             &LpExtend(ref base2, _, LpInterior(id2))) => {
+                if id == id2 {
+                    base.common(&**base2).map(|x| {
+                        let xd = x.depth();
+                        if base.depth() == xd && base2.depth() == xd {
+                            assert_eq!(base.ty, base2.ty);
+                            assert_eq!(self.ty, other.ty);
+                            LoanPath {
+                                kind: LpExtend(Rc::new(x), a, LpInterior(id)),
+                                ty: self.ty,
+                            }
+                        } else {
+                            x
+                        }
+                    })
+                } else {
+                    base.common(&**base2)
+                }
+            }
+            (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
+            (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
+            (&LpVar(id), &LpVar(id2)) => {
+                if id == id2 {
+                    assert_eq!(self.ty, other.ty);
+                    Some(LoanPath { kind: LpVar(id), ty: self.ty })
+                } else {
+                    None
+                }
+            }
+            (&LpUpvar(id), &LpUpvar(id2)) => {
+                if id == id2 {
+                    assert_eq!(self.ty, other.ty);
+                    Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        }
+    }
+}
+
+pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
+    //! Computes the `LoanPath` (if any) for a `cmt`.
+    //! Note that this logic is somewhat duplicated in
+    //! the method `compute()` found in `gather_loans::restrictions`,
+    //! which allows it to share common loan path pieces as it
+    //! traverses the CMT.
+
+    let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
+
+    match cmt.cat {
+        mc::cat_rvalue(..) |
+        mc::cat_static_item => {
+            None
+        }
+
+        mc::cat_local(id) => {
+            Some(new_lp(LpVar(id)))
+        }
+
+        mc::cat_upvar(mc::Upvar { id, .. }) => {
+            Some(new_lp(LpUpvar(id)))
+        }
+
+        mc::cat_deref(ref cmt_base, _, pk) => {
+            opt_loan_path(cmt_base).map(|lp| {
+                new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
+            })
+        }
+
+        mc::cat_interior(ref cmt_base, ik) => {
+            opt_loan_path(cmt_base).map(|lp| {
+                new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
+            })
+        }
+
+        mc::cat_downcast(ref cmt_base, variant_def_id) =>
+            opt_loan_path(cmt_base)
+            .map(|lp| {
+                new_lp(LpDowncast(lp, variant_def_id))
+            }),
+
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Errors
+
+// Errors that can occur
+#[deriving(PartialEq)]
+#[allow(missing_copy_implementations)]
+pub enum bckerr_code {
+    err_mutbl,
+    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
+    err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
+}
+
+// Combination of an error code and the categorization of the expression
+// that caused it
+#[deriving(PartialEq)]
+pub struct BckError<'tcx> {
+    span: Span,
+    cause: euv::LoanCause,
+    cmt: mc::cmt<'tcx>,
+    code: bckerr_code
+}
+
+pub enum AliasableViolationKind {
+    MutabilityViolation,
+    BorrowViolation(euv::LoanCause)
+}
+
+impl Copy for AliasableViolationKind {}
+
+#[deriving(Show)]
+pub enum MovedValueUseKind {
+    MovedInUse,
+    MovedInCapture,
+}
+
+impl Copy for MovedValueUseKind {}
+
+///////////////////////////////////////////////////////////////////////////
+// Misc
+
+impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
+    pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
+                           -> bool {
+        self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
+    }
+
+    pub fn mc(&self) -> mc::MemCategorizationContext<'a, ty::ctxt<'tcx>> {
+        mc::MemCategorizationContext::new(self.tcx)
+    }
+
+    pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt<'tcx> {
+        match self.mc().cat_expr(expr) {
+            Ok(c) => c,
+            Err(()) => {
+                self.tcx.sess.span_bug(expr.span, "error in mem categorization");
+            }
+        }
+    }
+
+    pub fn report(&self, err: BckError<'tcx>) {
+        self.span_err(
+            err.span,
+            self.bckerr_to_string(&err).as_slice());
+        self.note_and_explain_bckerr(err);
+    }
+
+    pub fn report_use_of_moved_value(&self,
+                                     use_span: Span,
+                                     use_kind: MovedValueUseKind,
+                                     lp: &LoanPath<'tcx>,
+                                     the_move: &move_data::Move,
+                                     moved_lp: &LoanPath<'tcx>,
+                                     param_env: &ty::ParameterEnvironment<'tcx>) {
+        let verb = match use_kind {
+            MovedInUse => "use",
+            MovedInCapture => "capture",
+        };
+
+        let (ol, moved_lp_msg) = match the_move.kind {
+            move_data::Declared => {
+                self.tcx.sess.span_err(
+                    use_span,
+                    format!("{} of possibly uninitialized variable: `{}`",
+                            verb,
+                            self.loan_path_to_string(lp)).as_slice());
+                (self.loan_path_to_string(moved_lp),
+                 String::new())
+            }
+            _ => {
+                // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
+                // normally generate a rather confusing message:
+                //
+                //     error: use of moved value: `x.b`
+                //     note: `x.a` moved here...
+                //
+                // What we want to do instead is get the 'common ancestor' of the two moves and
+                // use that for most of the message instead, giving is something like this:
+                //
+                //     error: use of moved value: `x`
+                //     note: `x` moved here (through moving `x.a`)...
+
+                let common = moved_lp.common(lp);
+                let has_common = common.is_some();
+                let has_fork = moved_lp.has_fork(lp);
+                let (nl, ol, moved_lp_msg) =
+                    if has_fork && has_common {
+                        let nl = self.loan_path_to_string(&common.unwrap());
+                        let ol = nl.clone();
+                        let moved_lp_msg = format!(" (through moving `{}`)",
+                                                   self.loan_path_to_string(moved_lp));
+                        (nl, ol, moved_lp_msg)
+                    } else {
+                        (self.loan_path_to_string(lp),
+                         self.loan_path_to_string(moved_lp),
+                         String::new())
+                    };
+
+                let partial = moved_lp.depth() > lp.depth();
+                let msg = if !has_fork && partial { "partially " }
+                          else if has_fork && !has_common { "collaterally "}
+                          else { "" };
+                self.tcx.sess.span_err(
+                    use_span,
+                    format!("{} of {}moved value: `{}`",
+                            verb,
+                            msg,
+                            nl).as_slice());
+                (ol, moved_lp_msg)
+            }
+        };
+
+        match the_move.kind {
+            move_data::Declared => {}
+
+            move_data::MoveExpr => {
+                let (expr_ty, expr_span) = match self.tcx
+                                                     .map
+                                                     .find(the_move.id) {
+                    Some(ast_map::NodeExpr(expr)) => {
+                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
+                    }
+                    r => {
+                        self.tcx.sess.bug(format!("MoveExpr({}) maps to \
+                                                   {}, not Expr",
+                                                  the_move.id,
+                                                  r).as_slice())
+                    }
+                };
+                let (suggestion, _) = move_suggestion(self.tcx, param_env, expr_ty,
+                        ("moved by default", ""));
+                self.tcx.sess.span_note(
+                    expr_span,
+                    format!("`{}` moved here{} because it has type `{}`, which is {}",
+                            ol,
+                            moved_lp_msg,
+                            expr_ty.user_string(self.tcx),
+                            suggestion).as_slice());
+            }
+
+            move_data::MovePat => {
+                let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
+                let span = self.tcx.map.span(the_move.id);
+                self.tcx.sess.span_note(span,
+                    format!("`{}` moved here{} because it has type `{}`, \
+                             which is moved by default",
+                            ol,
+                            moved_lp_msg,
+                            pat_ty.user_string(self.tcx)).as_slice());
+                self.tcx.sess.span_help(span,
+                    "use `ref` to override");
+            }
+
+            move_data::Captured => {
+                let (expr_ty, expr_span) = match self.tcx
+                                                     .map
+                                                     .find(the_move.id) {
+                    Some(ast_map::NodeExpr(expr)) => {
+                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
+                    }
+                    r => {
+                        self.tcx.sess.bug(format!("Captured({}) maps to \
+                                                   {}, not Expr",
+                                                  the_move.id,
+                                                  r).as_slice())
+                    }
+                };
+                let (suggestion, help) = move_suggestion(self.tcx,
+                                                         param_env,
+                                                         expr_ty,
+                        ("moved by default", "make a copy and \
+                         capture that instead to override"));
+                self.tcx.sess.span_note(
+                    expr_span,
+                    format!("`{}` moved into closure environment here{} because it \
+                            has type `{}`, which is {}",
+                            ol,
+                            moved_lp_msg,
+                            expr_ty.user_string(self.tcx),
+                            suggestion).as_slice());
+                self.tcx.sess.span_help(expr_span, help);
+            }
+        }
+
+        fn move_suggestion<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                 param_env: &ty::ParameterEnvironment<'tcx>,
+                                 ty: Ty<'tcx>,
+                                 default_msgs: (&'static str, &'static str))
+                                 -> (&'static str, &'static str) {
+            match ty.sty {
+                ty::ty_closure(box ty::ClosureTy {
+                        store: ty::RegionTraitStore(..),
+                        ..
+                    }) =>
+                    ("a non-copyable stack closure",
+                     "capture it in a new closure, e.g. `|x| f(x)`, to override"),
+                _ if ty::type_moves_by_default(tcx, ty, param_env) =>
+                    ("non-copyable",
+                     "perhaps you meant to use `clone()`?"),
+                _ => default_msgs,
+            }
+        }
+    }
+
+    pub fn report_reassigned_immutable_variable(&self,
+                                                span: Span,
+                                                lp: &LoanPath<'tcx>,
+                                                assign:
+                                                &move_data::Assignment) {
+        self.tcx.sess.span_err(
+            span,
+            format!("re-assignment of immutable variable `{}`",
+                    self.loan_path_to_string(lp)).as_slice());
+        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
+    }
+
+    pub fn span_err(&self, s: Span, m: &str) {
+        self.tcx.sess.span_err(s, m);
+    }
+
+    pub fn span_note(&self, s: Span, m: &str) {
+        self.tcx.sess.span_note(s, m);
+    }
+
+    pub fn span_end_note(&self, s: Span, m: &str) {
+        self.tcx.sess.span_end_note(s, m);
+    }
+
+    pub fn span_help(&self, s: Span, m: &str) {
+        self.tcx.sess.span_help(s, m);
+    }
+
+    pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
+        match err.code {
+            err_mutbl => {
+                let descr = match err.cmt.note {
+                    mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
+                        self.cmt_to_string(&*err.cmt)
+                    }
+                    _ => match opt_loan_path(&err.cmt) {
+                        None => {
+                            format!("{} {}",
+                                    err.cmt.mutbl.to_user_str(),
+                                    self.cmt_to_string(&*err.cmt))
+                        }
+                        Some(lp) => {
+                            format!("{} {} `{}`",
+                                    err.cmt.mutbl.to_user_str(),
+                                    self.cmt_to_string(&*err.cmt),
+                                    self.loan_path_to_string(&*lp))
+                        }
+                    }
+                };
+
+                match err.cause {
+                    euv::ClosureCapture(_) => {
+                        format!("closure cannot assign to {}", descr)
+                    }
+                    euv::OverloadedOperator |
+                    euv::AddrOf |
+                    euv::RefBinding |
+                    euv::AutoRef |
+                    euv::ForLoop |
+                    euv::MatchDiscriminant => {
+                        format!("cannot borrow {} as mutable", descr)
+                    }
+                    euv::ClosureInvocation => {
+                        self.tcx.sess.span_bug(err.span,
+                            "err_mutbl with a closure invocation");
+                    }
+                }
+            }
+            err_out_of_scope(..) => {
+                let msg = match opt_loan_path(&err.cmt) {
+                    None => "borrowed value".to_string(),
+                    Some(lp) => {
+                        format!("`{}`", self.loan_path_to_string(&*lp))
+                    }
+                };
+                format!("{} does not live long enough", msg)
+            }
+            err_borrowed_pointer_too_short(..) => {
+                let descr = match opt_loan_path(&err.cmt) {
+                    Some(lp) => {
+                        format!("`{}`", self.loan_path_to_string(&*lp))
+                    }
+                    None => self.cmt_to_string(&*err.cmt),
+                };
+
+                format!("lifetime of {} is too short to guarantee \
+                                its contents can be safely reborrowed",
+                               descr)
+            }
+        }
+    }
+
+    pub fn report_aliasability_violation(&self,
+                                         span: Span,
+                                         kind: AliasableViolationKind,
+                                         cause: mc::AliasableReason) {
+        let mut is_closure = false;
+        let prefix = match kind {
+            MutabilityViolation => {
+                "cannot assign to data"
+            }
+            BorrowViolation(euv::ClosureCapture(_)) => {
+                // I don't think we can get aliasability violations
+                // with closure captures, so no need to come up with a
+                // good error message. The reason this cannot happen
+                // is because we only capture local variables in
+                // closures, and those are never aliasable.
+                self.tcx.sess.span_bug(
+                    span,
+                    "aliasability violation with closure");
+            }
+            BorrowViolation(euv::OverloadedOperator) |
+            BorrowViolation(euv::AddrOf) |
+            BorrowViolation(euv::AutoRef) |
+            BorrowViolation(euv::RefBinding) |
+            BorrowViolation(euv::MatchDiscriminant) => {
+                "cannot borrow data mutably"
+            }
+
+            BorrowViolation(euv::ClosureInvocation) => {
+                is_closure = true;
+                "closure invocation"
+            }
+
+            BorrowViolation(euv::ForLoop) => {
+                "`for` loop"
+            }
+        };
+
+        match cause {
+            mc::AliasableOther => {
+                self.tcx.sess.span_err(
+                    span,
+                    format!("{} in an aliasable location",
+                             prefix).as_slice());
+            }
+            mc::AliasableClosure(id) => {
+                self.tcx.sess.span_err(span,
+                                       format!("{} in a captured outer \
+                                               variable in an `Fn` closure", prefix).as_slice());
+                span_help!(self.tcx.sess, self.tcx.map.span(id),
+                           "consider changing this closure to take self by mutable reference");
+            }
+            mc::AliasableStatic(..) |
+            mc::AliasableStaticMut(..) => {
+                self.tcx.sess.span_err(
+                    span,
+                    format!("{} in a static location", prefix).as_slice());
+            }
+            mc::AliasableBorrowed => {
+                self.tcx.sess.span_err(
+                    span,
+                    format!("{} in a `&` reference", prefix).as_slice());
+            }
+        }
+
+        if is_closure {
+            self.tcx.sess.span_help(
+                span,
+                "closures behind references must be called via `&mut`");
+        }
+    }
+
+    pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
+        let code = err.code;
+        match code {
+            err_mutbl(..) => {
+                match err.cmt.note {
+                    mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
+                        // If this is an `Fn` closure, it simply can't mutate upvars.
+                        // If it's an `FnMut` closure, the original variable was declared immutable.
+                        // We need to determine which is the case here.
+                        let kind = match err.cmt.upvar().unwrap().cat {
+                            mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
+                            _ => unreachable!()
+                        };
+                        if kind == ty::FnUnboxedClosureKind {
+                            self.tcx.sess.span_help(
+                                self.tcx.map.span(upvar_id.closure_expr_id),
+                                "consider changing this closure to take \
+                                 self by mutable reference");
+                        }
+                    }
+                    _ => {}
+                }
+            }
+
+            err_out_of_scope(super_scope, sub_scope) => {
+                note_and_explain_region(
+                    self.tcx,
+                    "reference must be valid for ",
+                    sub_scope,
+                    "...");
+                let suggestion = if is_statement_scope(self.tcx, super_scope) {
+                    Some("consider using a `let` binding to increase its lifetime")
+                } else {
+                    None
+                };
+                let span = note_and_explain_region(
+                    self.tcx,
+                    "...but borrowed value is only valid for ",
+                    super_scope,
+                    "");
+                match (span, suggestion) {
+                    (_, None) => {},
+                    (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
+                    (None, Some(msg)) => self.tcx.sess.help(msg),
+                }
+            }
+
+            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
+                let descr = match opt_loan_path(&err.cmt) {
+                    Some(lp) => {
+                        format!("`{}`", self.loan_path_to_string(&*lp))
+                    }
+                    None => self.cmt_to_string(&*err.cmt),
+                };
+                note_and_explain_region(
+                    self.tcx,
+                    format!("{} would have to be valid for ",
+                            descr).as_slice(),
+                    loan_scope,
+                    "...");
+                note_and_explain_region(
+                    self.tcx,
+                    format!("...but {} is only valid for ", descr).as_slice(),
+                    ptr_scope,
+                    "");
+            }
+        }
+    }
+
+    pub fn append_loan_path_to_string(&self,
+                                      loan_path: &LoanPath<'tcx>,
+                                      out: &mut String) {
+        match loan_path.kind {
+            LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
+            LpVar(id) => {
+                out.push_str(ty::local_var_name_str(self.tcx, id).get());
+            }
+
+            LpDowncast(ref lp_base, variant_def_id) => {
+                out.push('(');
+                self.append_loan_path_to_string(&**lp_base, out);
+                out.push_str(DOWNCAST_PRINTED_OPERATOR);
+                out.push_str(ty::item_path_str(self.tcx, variant_def_id).as_slice());
+                out.push(')');
+            }
+
+
+            LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
+                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
+                match fname {
+                    mc::NamedField(fname) => {
+                        out.push('.');
+                        out.push_str(token::get_name(fname).get());
+                    }
+                    mc::PositionalField(idx) => {
+                        out.push('.');
+                        out.push_str(idx.to_string().as_slice());
+                    }
+                }
+            }
+
+            LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
+                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
+                out.push_str("[..]");
+            }
+
+            LpExtend(ref lp_base, _, LpDeref(_)) => {
+                out.push('*');
+                self.append_loan_path_to_string(&**lp_base, out);
+            }
+        }
+    }
+
+    pub fn append_autoderefd_loan_path_to_string(&self,
+                                                 loan_path: &LoanPath<'tcx>,
+                                                 out: &mut String) {
+        match loan_path.kind {
+            LpExtend(ref lp_base, _, LpDeref(_)) => {
+                // For a path like `(*x).f` or `(*x)[3]`, autoderef
+                // rules would normally allow users to omit the `*x`.
+                // So just serialize such paths to `x.f` or x[3]` respectively.
+                self.append_autoderefd_loan_path_to_string(&**lp_base, out)
+            }
+
+            LpDowncast(ref lp_base, variant_def_id) => {
+                out.push('(');
+                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
+                out.push(':');
+                out.push_str(ty::item_path_str(self.tcx, variant_def_id).as_slice());
+                out.push(')');
+            }
+
+            LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
+                self.append_loan_path_to_string(loan_path, out)
+            }
+        }
+    }
+
+    pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
+        let mut result = String::new();
+        self.append_loan_path_to_string(loan_path, &mut result);
+        result
+    }
+
+    pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
+        self.mc().cmt_to_string(cmt)
+    }
+}
+
+fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
+     match region {
+         ty::ReScope(scope) => {
+             match tcx.map.find(scope.node_id()) {
+                 Some(ast_map::NodeStmt(_)) => true,
+                 _ => false
+             }
+         }
+         _ => false
+     }
+}
+
+impl BitwiseOperator for LoanDataFlowOperator {
+    #[inline]
+    fn join(&self, succ: uint, pred: uint) -> uint {
+        succ | pred // loans from both preds are in scope
+    }
+}
+
+impl DataFlowOperator for LoanDataFlowOperator {
+    #[inline]
+    fn initial_value(&self) -> bool {
+        false // no loans in scope by default
+    }
+}
+
+impl<'tcx> Repr<'tcx> for Loan<'tcx> {
+    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
+        format!("Loan_{}({}, {}, {}-{}, {})",
+                 self.index,
+                 self.loan_path.repr(tcx),
+                 self.kind,
+                 self.gen_scope,
+                 self.kill_scope,
+                 self.restricted_paths.repr(tcx))
+    }
+}
+
+impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
+    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
+        match self.kind {
+            LpVar(id) => {
+                format!("$({})", tcx.map.node_to_string(id))
+            }
+
+            LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
+                let s = tcx.map.node_to_string(var_id);
+                format!("$({} captured by id={})", s, closure_expr_id)
+            }
+
+            LpDowncast(ref lp, variant_def_id) => {
+                let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
+                    ty::item_path_str(tcx, variant_def_id)
+                } else {
+                    variant_def_id.repr(tcx)
+                };
+                format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
+            }
+
+            LpExtend(ref lp, _, LpDeref(_)) => {
+                format!("{}.*", lp.repr(tcx))
+            }
+
+            LpExtend(ref lp, _, LpInterior(ref interior)) => {
+                format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
+            }
+        }
+    }
+}
+
+impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
+    fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
+        match self.kind {
+            LpVar(id) => {
+                format!("$({})", tcx.map.node_to_user_string(id))
+            }
+
+            LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
+                let s = tcx.map.node_to_user_string(var_id);
+                format!("$({} captured by closure)", s)
+            }
+
+            LpDowncast(ref lp, variant_def_id) => {
+                let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
+                    ty::item_path_str(tcx, variant_def_id)
+                } else {
+                    variant_def_id.repr(tcx)
+                };
+                format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
+            }
+
+            LpExtend(ref lp, _, LpDeref(_)) => {
+                format!("{}.*", lp.user_string(tcx))
+            }
+
+            LpExtend(ref lp, _, LpInterior(ref interior)) => {
+                format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))
+            }
+        }
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/move_data.rs b/src/librustc_borrowck/borrowck/move_data.rs
new file mode 100644 (file)
index 0000000..681a577
--- /dev/null
@@ -0,0 +1,771 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Data structures used for tracking moves. Please see the extensive
+//! comments in the section "Moves and initialization" in `doc.rs`.
+
+pub use self::MoveKind::*;
+
+use borrowck::*;
+use borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
+use borrowck::LoanPathElem::{LpInterior};
+use rustc::middle::cfg;
+use rustc::middle::dataflow::DataFlowContext;
+use rustc::middle::dataflow::BitwiseOperator;
+use rustc::middle::dataflow::DataFlowOperator;
+use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::mem_categorization as mc;
+use rustc::middle::ty;
+use rustc::util::nodemap::{FnvHashMap, NodeSet};
+use rustc::util::ppaux::Repr;
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::uint;
+use syntax::ast;
+use syntax::ast_util;
+use syntax::codemap::Span;
+
+#[path="fragments.rs"]
+pub mod fragments;
+
+pub struct MoveData<'tcx> {
+    /// Move paths. See section "Move paths" in `doc.rs`.
+    pub paths: RefCell<Vec<MovePath<'tcx>>>,
+
+    /// Cache of loan path to move path index, for easy lookup.
+    pub path_map: RefCell<FnvHashMap<Rc<LoanPath<'tcx>>, MovePathIndex>>,
+
+    /// Each move or uninitialized variable gets an entry here.
+    pub moves: RefCell<Vec<Move>>,
+
+    /// Assignments to a variable, like `x = foo`. These are assigned
+    /// bits for dataflow, since we must track them to ensure that
+    /// immutable variables are assigned at most once along each path.
+    pub var_assignments: RefCell<Vec<Assignment>>,
+
+    /// Assignments to a path, like `x.f = foo`. These are not
+    /// assigned dataflow bits, but we track them because they still
+    /// kill move bits.
+    pub path_assignments: RefCell<Vec<Assignment>>,
+
+    /// Enum variant matched within a pattern on some match arm, like
+    /// `SomeStruct{ f: Variant1(x, y) } => ...`
+    pub variant_matches: RefCell<Vec<VariantMatch>>,
+
+    /// Assignments to a variable or path, like `x = foo`, but not `x += foo`.
+    pub assignee_ids: RefCell<NodeSet>,
+
+    /// Path-fragments from moves in to or out of parts of structured data.
+    pub fragments: RefCell<fragments::FragmentSets>,
+}
+
+pub struct FlowedMoveData<'a, 'tcx: 'a> {
+    pub move_data: MoveData<'tcx>,
+
+    pub dfcx_moves: MoveDataFlow<'a, 'tcx>,
+
+    // We could (and maybe should, for efficiency) combine both move
+    // and assign data flow into one, but this way it's easier to
+    // distinguish the bits that correspond to moves and assignments.
+    pub dfcx_assign: AssignDataFlow<'a, 'tcx>
+}
+
+/// Index into `MoveData.paths`, used like a pointer
+#[deriving(PartialEq, Eq, PartialOrd, Ord, Show)]
+pub struct MovePathIndex(uint);
+
+impl Copy for MovePathIndex {}
+
+impl MovePathIndex {
+    fn get(&self) -> uint {
+        let MovePathIndex(v) = *self; v
+    }
+}
+
+impl Clone for MovePathIndex {
+    fn clone(&self) -> MovePathIndex {
+        MovePathIndex(self.get())
+    }
+}
+
+#[allow(non_upper_case_globals)]
+static InvalidMovePathIndex: MovePathIndex =
+    MovePathIndex(uint::MAX);
+
+/// Index into `MoveData.moves`, used like a pointer
+#[deriving(PartialEq)]
+pub struct MoveIndex(uint);
+
+impl Copy for MoveIndex {}
+
+impl MoveIndex {
+    fn get(&self) -> uint {
+        let MoveIndex(v) = *self; v
+    }
+}
+
+#[allow(non_upper_case_globals)]
+static InvalidMoveIndex: MoveIndex =
+    MoveIndex(uint::MAX);
+
+pub struct MovePath<'tcx> {
+    /// Loan path corresponding to this move path
+    pub loan_path: Rc<LoanPath<'tcx>>,
+
+    /// Parent pointer, `InvalidMovePathIndex` if root
+    pub parent: MovePathIndex,
+
+    /// Head of linked list of moves to this path,
+    /// `InvalidMoveIndex` if not moved
+    pub first_move: MoveIndex,
+
+    /// First node in linked list of children, `InvalidMovePathIndex` if leaf
+    pub first_child: MovePathIndex,
+
+    /// Next node in linked list of parent's children (siblings),
+    /// `InvalidMovePathIndex` if none.
+    pub next_sibling: MovePathIndex,
+}
+
+#[deriving(PartialEq, Show)]
+pub enum MoveKind {
+    Declared,   // When declared, variables start out "moved".
+    MoveExpr,   // Expression or binding that moves a variable
+    MovePat,    // By-move binding
+    Captured    // Closure creation that moves a value
+}
+
+impl Copy for MoveKind {}
+
+pub struct Move {
+    /// Path being moved.
+    pub path: MovePathIndex,
+
+    /// id of node that is doing the move.
+    pub id: ast::NodeId,
+
+    /// Kind of move, for error messages.
+    pub kind: MoveKind,
+
+    /// Next node in linked list of moves from `path`, or `InvalidMoveIndex`
+    pub next_move: MoveIndex
+}
+
+impl Copy for Move {}
+
+pub struct Assignment {
+    /// Path being assigned.
+    pub path: MovePathIndex,
+
+    /// id where assignment occurs
+    pub id: ast::NodeId,
+
+    /// span of node where assignment occurs
+    pub span: Span,
+}
+
+impl Copy for Assignment {}
+
+pub struct VariantMatch {
+    /// downcast to the variant.
+    pub path: MovePathIndex,
+
+    /// path being downcast to the variant.
+    pub base_path: MovePathIndex,
+
+    /// id where variant's pattern occurs
+    pub id: ast::NodeId,
+
+    /// says if variant established by move (and why), by copy, or by borrow.
+    pub mode: euv::MatchMode
+}
+
+impl Copy for VariantMatch {}
+
+#[deriving(Clone)]
+pub struct MoveDataFlowOperator;
+
+impl Copy for MoveDataFlowOperator {}
+
+pub type MoveDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, MoveDataFlowOperator>;
+
+#[deriving(Clone)]
+pub struct AssignDataFlowOperator;
+
+impl Copy for AssignDataFlowOperator {}
+
+pub type AssignDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, AssignDataFlowOperator>;
+
+fn loan_path_is_precise(loan_path: &LoanPath) -> bool {
+    match loan_path.kind {
+        LpVar(_) | LpUpvar(_) => {
+            true
+        }
+        LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
+            // Paths involving element accesses do not refer to a unique
+            // location, as there is no accurate tracking of the indices.
+            false
+        }
+        LpDowncast(ref lp_base, _) |
+        LpExtend(ref lp_base, _, _) => {
+            loan_path_is_precise(&**lp_base)
+        }
+    }
+}
+
+impl<'tcx> MoveData<'tcx> {
+    pub fn new() -> MoveData<'tcx> {
+        MoveData {
+            paths: RefCell::new(Vec::new()),
+            path_map: RefCell::new(FnvHashMap::new()),
+            moves: RefCell::new(Vec::new()),
+            path_assignments: RefCell::new(Vec::new()),
+            var_assignments: RefCell::new(Vec::new()),
+            variant_matches: RefCell::new(Vec::new()),
+            assignee_ids: RefCell::new(NodeSet::new()),
+            fragments: RefCell::new(fragments::FragmentSets::new()),
+        }
+    }
+
+    pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath<'tcx>> {
+        (*self.paths.borrow())[index.get()].loan_path.clone()
+    }
+
+    fn path_parent(&self, index: MovePathIndex) -> MovePathIndex {
+        (*self.paths.borrow())[index.get()].parent
+    }
+
+    fn path_first_move(&self, index: MovePathIndex) -> MoveIndex {
+        (*self.paths.borrow())[index.get()].first_move
+    }
+
+    /// Returns the index of first child, or `InvalidMovePathIndex` if
+    /// `index` is leaf.
+    fn path_first_child(&self, index: MovePathIndex) -> MovePathIndex {
+        (*self.paths.borrow())[index.get()].first_child
+    }
+
+    fn path_next_sibling(&self, index: MovePathIndex) -> MovePathIndex {
+        (*self.paths.borrow())[index.get()].next_sibling
+    }
+
+    fn set_path_first_move(&self,
+                           index: MovePathIndex,
+                           first_move: MoveIndex) {
+        (*self.paths.borrow_mut())[index.get()].first_move = first_move
+    }
+
+    fn set_path_first_child(&self,
+                            index: MovePathIndex,
+                            first_child: MovePathIndex) {
+        (*self.paths.borrow_mut())[index.get()].first_child = first_child
+    }
+
+    fn move_next_move(&self, index: MoveIndex) -> MoveIndex {
+        //! Type safe indexing operator
+        (*self.moves.borrow())[index.get()].next_move
+    }
+
+    fn is_var_path(&self, index: MovePathIndex) -> bool {
+        //! True if `index` refers to a variable
+        self.path_parent(index) == InvalidMovePathIndex
+    }
+
+    /// Returns the existing move path index for `lp`, if any, and otherwise adds a new index for
+    /// `lp` and any of its base paths that do not yet have an index.
+    pub fn move_path(&self,
+                     tcx: &ty::ctxt<'tcx>,
+                     lp: Rc<LoanPath<'tcx>>) -> MovePathIndex {
+        match self.path_map.borrow().get(&lp) {
+            Some(&index) => {
+                return index;
+            }
+            None => {}
+        }
+
+        let index = match lp.kind {
+            LpVar(..) | LpUpvar(..) => {
+                let index = MovePathIndex(self.paths.borrow().len());
+
+                self.paths.borrow_mut().push(MovePath {
+                    loan_path: lp.clone(),
+                    parent: InvalidMovePathIndex,
+                    first_move: InvalidMoveIndex,
+                    first_child: InvalidMovePathIndex,
+                    next_sibling: InvalidMovePathIndex,
+                });
+
+                index
+            }
+
+            LpDowncast(ref base, _) |
+            LpExtend(ref base, _, _) => {
+                let parent_index = self.move_path(tcx, base.clone());
+
+                let index = MovePathIndex(self.paths.borrow().len());
+
+                let next_sibling = self.path_first_child(parent_index);
+                self.set_path_first_child(parent_index, index);
+
+                self.paths.borrow_mut().push(MovePath {
+                    loan_path: lp.clone(),
+                    parent: parent_index,
+                    first_move: InvalidMoveIndex,
+                    first_child: InvalidMovePathIndex,
+                    next_sibling: next_sibling,
+                });
+
+                index
+            }
+        };
+
+        debug!("move_path(lp={}, index={})",
+               lp.repr(tcx),
+               index);
+
+        assert_eq!(index.get(), self.paths.borrow().len() - 1);
+        self.path_map.borrow_mut().insert(lp, index);
+        return index;
+    }
+
+    fn existing_move_path(&self, lp: &Rc<LoanPath<'tcx>>)
+                          -> Option<MovePathIndex> {
+        self.path_map.borrow().get(lp).cloned()
+    }
+
+    fn existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>)
+                           -> Vec<MovePathIndex> {
+        let mut result = vec!();
+        self.add_existing_base_paths(lp, &mut result);
+        result
+    }
+
+    /// Adds any existing move path indices for `lp` and any base paths of `lp` to `result`, but
+    /// does not add new move paths
+    fn add_existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>,
+                               result: &mut Vec<MovePathIndex>) {
+        match self.path_map.borrow().get(lp).cloned() {
+            Some(index) => {
+                self.each_base_path(index, |p| {
+                    result.push(p);
+                    true
+                });
+            }
+            None => {
+                match lp.kind {
+                    LpVar(..) | LpUpvar(..) => { }
+                    LpDowncast(ref b, _) |
+                    LpExtend(ref b, _, _) => {
+                        self.add_existing_base_paths(b, result);
+                    }
+                }
+            }
+        }
+
+    }
+
+    /// Adds a new move entry for a move of `lp` that occurs at location `id` with kind `kind`.
+    pub fn add_move(&self,
+                    tcx: &ty::ctxt<'tcx>,
+                    lp: Rc<LoanPath<'tcx>>,
+                    id: ast::NodeId,
+                    kind: MoveKind) {
+        debug!("add_move(lp={}, id={}, kind={})",
+               lp.repr(tcx),
+               id,
+               kind);
+
+        let path_index = self.move_path(tcx, lp.clone());
+        let move_index = MoveIndex(self.moves.borrow().len());
+
+        self.fragments.borrow_mut().add_move(path_index);
+
+        let next_move = self.path_first_move(path_index);
+        self.set_path_first_move(path_index, move_index);
+
+        self.moves.borrow_mut().push(Move {
+            path: path_index,
+            id: id,
+            kind: kind,
+            next_move: next_move
+        });
+    }
+
+    /// Adds a new record for an assignment to `lp` that occurs at location `id` with the given
+    /// `span`.
+    pub fn add_assignment(&self,
+                          tcx: &ty::ctxt<'tcx>,
+                          lp: Rc<LoanPath<'tcx>>,
+                          assign_id: ast::NodeId,
+                          span: Span,
+                          assignee_id: ast::NodeId,
+                          mode: euv::MutateMode) {
+        debug!("add_assignment(lp={}, assign_id={}, assignee_id={}",
+               lp.repr(tcx), assign_id, assignee_id);
+
+        let path_index = self.move_path(tcx, lp.clone());
+
+        self.fragments.borrow_mut().add_assignment(path_index);
+
+        match mode {
+            euv::Init | euv::JustWrite => {
+                self.assignee_ids.borrow_mut().insert(assignee_id);
+            }
+            euv::WriteAndRead => { }
+        }
+
+        let assignment = Assignment {
+            path: path_index,
+            id: assign_id,
+            span: span,
+        };
+
+        if self.is_var_path(path_index) {
+            debug!("add_assignment[var](lp={}, assignment={}, path_index={})",
+                   lp.repr(tcx), self.var_assignments.borrow().len(), path_index);
+
+            self.var_assignments.borrow_mut().push(assignment);
+        } else {
+            debug!("add_assignment[path](lp={}, path_index={})",
+                   lp.repr(tcx), path_index);
+
+            self.path_assignments.borrow_mut().push(assignment);
+        }
+    }
+
+    /// Adds a new record for a match of `base_lp`, downcast to
+    /// variant `lp`, that occurs at location `pattern_id`.  (One
+    /// should be able to recover the span info from the
+    /// `pattern_id` and the ast_map, I think.)
+    pub fn add_variant_match(&self,
+                             tcx: &ty::ctxt<'tcx>,
+                             lp: Rc<LoanPath<'tcx>>,
+                             pattern_id: ast::NodeId,
+                             base_lp: Rc<LoanPath<'tcx>>,
+                             mode: euv::MatchMode) {
+        debug!("add_variant_match(lp={}, pattern_id={})",
+               lp.repr(tcx), pattern_id);
+
+        let path_index = self.move_path(tcx, lp.clone());
+        let base_path_index = self.move_path(tcx, base_lp.clone());
+
+        self.fragments.borrow_mut().add_assignment(path_index);
+
+        let variant_match = VariantMatch {
+            path: path_index,
+            base_path: base_path_index,
+            id: pattern_id,
+            mode: mode,
+        };
+
+        self.variant_matches.borrow_mut().push(variant_match);
+    }
+
+    fn fixup_fragment_sets(&self, tcx: &ty::ctxt<'tcx>) {
+        fragments::fixup_fragment_sets(self, tcx)
+    }
+
+    /// Adds the gen/kills for the various moves and
+    /// assignments into the provided data flow contexts.
+    /// Moves are generated by moves and killed by assignments and
+    /// scoping. Assignments are generated by assignment to variables and
+    /// killed by scoping. See `doc.rs` for more details.
+    fn add_gen_kills(&self,
+                     tcx: &ty::ctxt<'tcx>,
+                     dfcx_moves: &mut MoveDataFlow,
+                     dfcx_assign: &mut AssignDataFlow) {
+        for (i, the_move) in self.moves.borrow().iter().enumerate() {
+            dfcx_moves.add_gen(the_move.id, i);
+        }
+
+        for (i, assignment) in self.var_assignments.borrow().iter().enumerate() {
+            dfcx_assign.add_gen(assignment.id, i);
+            self.kill_moves(assignment.path, assignment.id, dfcx_moves);
+        }
+
+        for assignment in self.path_assignments.borrow().iter() {
+            self.kill_moves(assignment.path, assignment.id, dfcx_moves);
+        }
+
+        // Kill all moves related to a variable `x` when
+        // it goes out of scope:
+        for path in self.paths.borrow().iter() {
+            match path.loan_path.kind {
+                LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
+                    let kill_scope = path.loan_path.kill_scope(tcx);
+                    let path = self.path_map.borrow()[path.loan_path];
+                    self.kill_moves(path, kill_scope.node_id(), dfcx_moves);
+                }
+                LpExtend(..) => {}
+            }
+        }
+
+        // Kill all assignments when the variable goes out of scope:
+        for (assignment_index, assignment) in
+                self.var_assignments.borrow().iter().enumerate() {
+            let lp = self.path_loan_path(assignment.path);
+            match lp.kind {
+                LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
+                    let kill_scope = lp.kill_scope(tcx);
+                    dfcx_assign.add_kill(kill_scope.node_id(), assignment_index);
+                }
+                LpExtend(..) => {
+                    tcx.sess.bug("var assignment for non var path");
+                }
+            }
+        }
+    }
+
+    fn each_base_path(&self, index: MovePathIndex, f: |MovePathIndex| -> bool)
+                      -> bool {
+        let mut p = index;
+        while p != InvalidMovePathIndex {
+            if !f(p) {
+                return false;
+            }
+            p = self.path_parent(p);
+        }
+        return true;
+    }
+
+    fn each_extending_path(&self,
+                           index: MovePathIndex,
+                           f: |MovePathIndex| -> bool)
+                           -> bool {
+        if !f(index) {
+            return false;
+        }
+
+        let mut p = self.path_first_child(index);
+        while p != InvalidMovePathIndex {
+            if !self.each_extending_path(p, |x| f(x)) {
+                return false;
+            }
+            p = self.path_next_sibling(p);
+        }
+
+        return true;
+    }
+
+    fn each_applicable_move(&self,
+                            index0: MovePathIndex,
+                            f: |MoveIndex| -> bool)
+                            -> bool {
+        let mut ret = true;
+        self.each_extending_path(index0, |index| {
+            let mut p = self.path_first_move(index);
+            while p != InvalidMoveIndex {
+                if !f(p) {
+                    ret = false;
+                    break;
+                }
+                p = self.move_next_move(p);
+            }
+            ret
+        });
+        ret
+    }
+
+    fn kill_moves(&self,
+                  path: MovePathIndex,
+                  kill_id: ast::NodeId,
+                  dfcx_moves: &mut MoveDataFlow) {
+        // We can only perform kills for paths that refer to a unique location,
+        // since otherwise we may kill a move from one location with an
+        // assignment referring to another location.
+
+        let loan_path = self.path_loan_path(path);
+        if loan_path_is_precise(&*loan_path) {
+            self.each_applicable_move(path, |move_index| {
+                dfcx_moves.add_kill(kill_id, move_index.get());
+                true
+            });
+        }
+    }
+}
+
+impl<'a, 'tcx> FlowedMoveData<'a, 'tcx> {
+    pub fn new(move_data: MoveData<'tcx>,
+               tcx: &'a ty::ctxt<'tcx>,
+               cfg: &cfg::CFG,
+               id_range: ast_util::IdRange,
+               decl: &ast::FnDecl,
+               body: &ast::Block)
+               -> FlowedMoveData<'a, 'tcx> {
+        let mut dfcx_moves =
+            DataFlowContext::new(tcx,
+                                 "flowed_move_data_moves",
+                                 Some(decl),
+                                 cfg,
+                                 MoveDataFlowOperator,
+                                 id_range,
+                                 move_data.moves.borrow().len());
+        let mut dfcx_assign =
+            DataFlowContext::new(tcx,
+                                 "flowed_move_data_assigns",
+                                 Some(decl),
+                                 cfg,
+                                 AssignDataFlowOperator,
+                                 id_range,
+                                 move_data.var_assignments.borrow().len());
+
+        move_data.fixup_fragment_sets(tcx);
+
+        move_data.add_gen_kills(tcx,
+                                &mut dfcx_moves,
+                                &mut dfcx_assign);
+
+        dfcx_moves.add_kills_from_flow_exits(cfg);
+        dfcx_assign.add_kills_from_flow_exits(cfg);
+
+        dfcx_moves.propagate(cfg, body);
+        dfcx_assign.propagate(cfg, body);
+
+        FlowedMoveData {
+            move_data: move_data,
+            dfcx_moves: dfcx_moves,
+            dfcx_assign: dfcx_assign,
+        }
+    }
+
+    pub fn kind_of_move_of_path(&self,
+                                id: ast::NodeId,
+                                loan_path: &Rc<LoanPath<'tcx>>)
+                                -> Option<MoveKind> {
+        //! Returns the kind of a move of `loan_path` by `id`, if one exists.
+
+        let mut ret = None;
+        for loan_path_index in self.move_data.path_map.borrow().get(&*loan_path).iter() {
+            self.dfcx_moves.each_gen_bit(id, |move_index| {
+                let the_move = self.move_data.moves.borrow();
+                let the_move = (*the_move)[move_index];
+                if the_move.path == **loan_path_index {
+                    ret = Some(the_move.kind);
+                    false
+                } else {
+                    true
+                }
+            });
+        }
+        ret
+    }
+
+    /// Iterates through each move of `loan_path` (or some base path of `loan_path`) that *may*
+    /// have occurred on entry to `id` without an intervening assignment. In other words, any moves
+    /// that would invalidate a reference to `loan_path` at location `id`.
+    pub fn each_move_of(&self,
+                        id: ast::NodeId,
+                        loan_path: &Rc<LoanPath<'tcx>>,
+                        f: |&Move, &LoanPath<'tcx>| -> bool)
+                        -> bool {
+        // Bad scenarios:
+        //
+        // 1. Move of `a.b.c`, use of `a.b.c`
+        // 2. Move of `a.b.c`, use of `a.b.c.d`
+        // 3. Move of `a.b.c`, use of `a` or `a.b`
+        //
+        // OK scenario:
+        //
+        // 4. move of `a.b.c`, use of `a.b.d`
+
+        let base_indices = self.move_data.existing_base_paths(loan_path);
+        if base_indices.is_empty() {
+            return true;
+        }
+
+        let opt_loan_path_index = self.move_data.existing_move_path(loan_path);
+
+        let mut ret = true;
+
+        self.dfcx_moves.each_bit_on_entry(id, |index| {
+            let the_move = self.move_data.moves.borrow();
+            let the_move = &(*the_move)[index];
+            let moved_path = the_move.path;
+            if base_indices.iter().any(|x| x == &moved_path) {
+                // Scenario 1 or 2: `loan_path` or some base path of
+                // `loan_path` was moved.
+                if !f(the_move, &*self.move_data.path_loan_path(moved_path)) {
+                    ret = false;
+                }
+            } else {
+                for &loan_path_index in opt_loan_path_index.iter() {
+                    let cont = self.move_data.each_base_path(moved_path, |p| {
+                        if p == loan_path_index {
+                            // Scenario 3: some extension of `loan_path`
+                            // was moved
+                            f(the_move,
+                              &*self.move_data.path_loan_path(moved_path))
+                        } else {
+                            true
+                        }
+                    });
+                    if !cont { ret = false; break }
+                }
+            }
+            ret
+        })
+    }
+
+    /// Iterates through every assignment to `loan_path` that may have occurred on entry to `id`.
+    /// `loan_path` must be a single variable.
+    pub fn each_assignment_of(&self,
+                              id: ast::NodeId,
+                              loan_path: &Rc<LoanPath<'tcx>>,
+                              f: |&Assignment| -> bool)
+                              -> bool {
+        let loan_path_index = {
+            match self.move_data.existing_move_path(loan_path) {
+                Some(i) => i,
+                None => {
+                    // if there were any assignments, it'd have an index
+                    return true;
+                }
+            }
+        };
+
+        self.dfcx_assign.each_bit_on_entry(id, |index| {
+            let assignment = self.move_data.var_assignments.borrow();
+            let assignment = &(*assignment)[index];
+            if assignment.path == loan_path_index && !f(assignment) {
+                false
+            } else {
+                true
+            }
+        })
+    }
+}
+
+impl BitwiseOperator for MoveDataFlowOperator {
+    #[inline]
+    fn join(&self, succ: uint, pred: uint) -> uint {
+        succ | pred // moves from both preds are in scope
+    }
+}
+
+impl DataFlowOperator for MoveDataFlowOperator {
+    #[inline]
+    fn initial_value(&self) -> bool {
+        false // no loans in scope by default
+    }
+}
+
+impl BitwiseOperator for AssignDataFlowOperator {
+    #[inline]
+    fn join(&self, succ: uint, pred: uint) -> uint {
+        succ | pred // moves from both preds are in scope
+    }
+}
+
+impl DataFlowOperator for AssignDataFlowOperator {
+    #[inline]
+    fn initial_value(&self) -> bool {
+        false // no assignments in scope by default
+    }
+}
diff --git a/src/librustc_borrowck/graphviz.rs b/src/librustc_borrowck/graphviz.rs
new file mode 100644 (file)
index 0000000..36c7472
--- /dev/null
@@ -0,0 +1,150 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module provides linkage between rustc::middle::graph and
+//! libgraphviz traits, specialized to attaching borrowck analysis
+//! data to rendered labels.
+
+pub use self::Variant::*;
+
+pub use rustc::middle::cfg::graphviz::{Node, Edge};
+use rustc::middle::cfg::graphviz as cfg_dot;
+
+use borrowck;
+use borrowck::{BorrowckCtxt, LoanPath};
+use dot;
+use rustc::middle::cfg::{CFGIndex};
+use rustc::middle::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit};
+use rustc::middle::dataflow;
+use std::rc::Rc;
+
+#[deriving(Show)]
+pub enum Variant {
+    Loans,
+    Moves,
+    Assigns,
+}
+
+impl Copy for Variant {}
+
+impl Variant {
+    pub fn short_name(&self) -> &'static str {
+        match *self {
+            Loans   => "loans",
+            Moves   => "moves",
+            Assigns => "assigns",
+        }
+    }
+}
+
+pub struct DataflowLabeller<'a, 'tcx: 'a> {
+    pub inner: cfg_dot::LabelledCFG<'a, 'tcx>,
+    pub variants: Vec<Variant>,
+    pub borrowck_ctxt: &'a BorrowckCtxt<'a, 'tcx>,
+    pub analysis_data: &'a borrowck::AnalysisData<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> DataflowLabeller<'a, 'tcx> {
+    fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String {
+        let id = n.val1().data.id;
+        debug!("dataflow_for({}, id={}) {}", e, id, self.variants);
+        let mut sets = "".to_string();
+        let mut seen_one = false;
+        for &variant in self.variants.iter() {
+            if seen_one { sets.push_str(" "); } else { seen_one = true; }
+            sets.push_str(variant.short_name());
+            sets.push_str(": ");
+            sets.push_str(self.dataflow_for_variant(e, n, variant).as_slice());
+        }
+        sets
+    }
+
+    fn dataflow_for_variant(&self, e: EntryOrExit, n: &Node, v: Variant) -> String {
+        let cfgidx = n.val0();
+        match v {
+            Loans   => self.dataflow_loans_for(e, cfgidx),
+            Moves   => self.dataflow_moves_for(e, cfgidx),
+            Assigns => self.dataflow_assigns_for(e, cfgidx),
+        }
+    }
+
+    fn build_set<O:DataFlowOperator>(&self,
+                                     e: EntryOrExit,
+                                     cfgidx: CFGIndex,
+                                     dfcx: &DataFlowContext<'a, 'tcx, O>,
+                                     to_lp: |uint| -> Rc<LoanPath<'tcx>>) -> String {
+        let mut saw_some = false;
+        let mut set = "{".to_string();
+        dfcx.each_bit_for_node(e, cfgidx, |index| {
+            let lp = to_lp(index);
+            if saw_some {
+                set.push_str(", ");
+            }
+            let loan_str = self.borrowck_ctxt.loan_path_to_string(&*lp);
+            set.push_str(loan_str.as_slice());
+            saw_some = true;
+            true
+        });
+        set.push_str("}");
+        set
+    }
+
+    fn dataflow_loans_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+        let dfcx = &self.analysis_data.loans;
+        let loan_index_to_path = |loan_index| {
+            let all_loans = &self.analysis_data.all_loans;
+            all_loans[loan_index].loan_path()
+        };
+        self.build_set(e, cfgidx, dfcx, loan_index_to_path)
+    }
+
+    fn dataflow_moves_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+        let dfcx = &self.analysis_data.move_data.dfcx_moves;
+        let move_index_to_path = |move_index| {
+            let move_data = &self.analysis_data.move_data.move_data;
+            let moves = move_data.moves.borrow();
+            let the_move = &(*moves)[move_index];
+            move_data.path_loan_path(the_move.path)
+        };
+        self.build_set(e, cfgidx, dfcx, move_index_to_path)
+    }
+
+    fn dataflow_assigns_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+        let dfcx = &self.analysis_data.move_data.dfcx_assign;
+        let assign_index_to_path = |assign_index| {
+            let move_data = &self.analysis_data.move_data.move_data;
+            let assignments = move_data.var_assignments.borrow();
+            let assignment = &(*assignments)[assign_index];
+            move_data.path_loan_path(assignment.path)
+        };
+        self.build_set(e, cfgidx, dfcx, assign_index_to_path)
+    }
+}
+
+impl<'a, 'tcx> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 'tcx> {
+    fn graph_id(&'a self) -> dot::Id<'a> { self.inner.graph_id() }
+    fn node_id(&'a self, n: &Node<'a>) -> dot::Id<'a> { self.inner.node_id(n) }
+    fn node_label(&'a self, n: &Node<'a>) -> dot::LabelText<'a> {
+        let prefix = self.dataflow_for(dataflow::Entry, n);
+        let suffix = self.dataflow_for(dataflow::Exit, n);
+        let inner_label = self.inner.node_label(n);
+        inner_label
+            .prefix_line(dot::LabelStr(prefix.into_cow()))
+            .suffix_line(dot::LabelStr(suffix.into_cow()))
+    }
+    fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) }
+}
+
+impl<'a, 'tcx> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 'tcx> {
+    fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() }
+    fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() }
+    fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) }
+    fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) }
+}
diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs
new file mode 100644 (file)
index 0000000..f187c0c
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "rustc_borrowck"]
+#![experimental]
+#![crate_type = "dylib"]
+#![crate_type = "rlib"]
+#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+      html_favicon_url = "http://www.rust-lang.org/favicon.ico",
+      html_root_url = "http://doc.rust-lang.org/nightly/")]
+
+#![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)]
+#![feature(slicing_syntax, tuple_indexing, unsafe_destructor)]
+#![feature(rustc_diagnostic_macros)]
+#![allow(non_camel_case_types)]
+
+#[phase(plugin, link)] extern crate log;
+#[phase(plugin, link)] extern crate syntax;
+
+// for "clarity", rename the graphviz crate to dot; graphviz within `borrowck`
+// refers to the borrowck-specific graphviz adapter traits.
+extern crate "graphviz" as dot;
+extern crate rustc;
+
+pub use borrowck::check_crate;
+pub use borrowck::build_borrowck_dataflow_data_for_fn;
+pub use borrowck::FnPartsWithCFG;
+
+mod borrowck;
+
+pub mod graphviz;
+
index 749bed15e38e05cc05a4c293d35f667327f1fed3..6cddcaca1e45c5716447c4bc85e1ae2c79f1feb2 100644 (file)
@@ -19,6 +19,7 @@
 use rustc::plugin::registry::Registry;
 use rustc::plugin;
 use rustc::util::common::time;
+use rustc_borrowck as borrowck;
 use rustc_trans::back::link;
 use rustc_trans::back::write;
 use rustc_trans::save;
@@ -420,7 +421,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
          middle::liveness::check_crate(&ty_cx));
 
     time(time_passes, "borrow checking", (), |_|
-         middle::borrowck::check_crate(&ty_cx));
+         borrowck::check_crate(&ty_cx));
 
     time(time_passes, "rvalue checking", (), |_|
          middle::check_rvalues::check_crate(&ty_cx, krate));
index f6b808e8e1b2d0bd949e74833213f75f33cb4bb8..85d9646c282111dd2f67081a11ba00eabbb2459a 100644 (file)
 extern crate graphviz;
 extern crate libc;
 extern crate rustc;
-extern crate rustc_typeck;
 extern crate rustc_back;
+extern crate rustc_borrowck;
 extern crate rustc_trans;
+extern crate rustc_typeck;
 #[phase(plugin, link)] extern crate log;
 #[phase(plugin, link)] extern crate syntax;
 extern crate serialize;
index d143d05acfe4b9b5ec05c489e0e1ff34398d02bf..971379b8dd2961d732514b463d19b4a918e04045 100644 (file)
 use driver;
 
 use rustc::middle::ty;
-use rustc::middle::borrowck::{mod, FnPartsWithCFG};
-use rustc::middle::borrowck::graphviz as borrowck_dot;
 use rustc::middle::cfg;
 use rustc::middle::cfg::graphviz::LabelledCFG;
 use rustc::session::Session;
 use rustc::session::config::{mod, Input};
 use rustc::util::ppaux;
+use rustc_borrowck as borrowck;
+use rustc_borrowck::graphviz as borrowck_dot;
 
 use syntax::ast;
 use syntax::ast_map::{mod, blocks, NodePrinter};
@@ -565,7 +565,7 @@ fn print_flowgraph<W:io::Writer>(variants: Vec<borrowck_dot::Variant>,
             return Ok(())
         }
         blocks::FnLikeCode(fn_like) => {
-            let fn_parts = FnPartsWithCFG::from_fn_like(&fn_like, &cfg);
+            let fn_parts = borrowck::FnPartsWithCFG::from_fn_like(&fn_like, &cfg);
             let (bccx, analysis_data) =
                 borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts);