From 96dfed2b62c6e84a542f9a170133ec528e2191c2 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 21 Apr 2014 19:21:53 -0400
Subject: [PATCH] Pre-step towards issue #12624 and others: Introduce
 ExprUseVisitor, remove the moves computation. ExprUseVisitor is a visitor
 that walks the AST for a function and calls a delegate to inform it where
 borrows, copies, and moves occur.

In this patch, I rewrite the gather_loans visitor to use ExprUseVisitor, but in
future patches, I think we could rewrite regionck, check_loans, and possibly
other passes to use it as well. This would refactor the repeated code between
those places that tries to determine where copies/moves/etc occur.
---
 src/librustc/driver/driver.rs                 |  20 +-
 src/librustc/lib.rs                           |   2 +-
 src/librustc/middle/astencode.rs              |  69 +-
 src/librustc/middle/borrowck/check_loans.rs   |  41 +-
 .../borrowck/gather_loans/gather_moves.rs     |  49 +-
 .../middle/borrowck/gather_loans/lifetime.rs  |  13 +-
 .../middle/borrowck/gather_loans/mod.rs       | 692 +++-----------
 .../borrowck/gather_loans/move_error.rs       |   3 +
 .../borrowck/gather_loans/restrictions.rs     |   5 +-
 src/librustc/middle/borrowck/mod.rs           | 106 +--
 src/librustc/middle/borrowck/move_data.rs     |  10 +-
 src/librustc/middle/check_match.rs            |  22 +-
 src/librustc/middle/const_eval.rs             |  13 +-
 src/librustc/middle/expr_use_visitor.rs       | 868 ++++++++++++++++++
 src/librustc/middle/freevars.rs               |  21 +-
 src/librustc/middle/kind.rs                   |   6 +-
 src/librustc/middle/liveness.rs               |  69 +-
 src/librustc/middle/mem_categorization.rs     |  20 +-
 src/librustc/middle/moves.rs                  | 655 -------------
 src/librustc/middle/trans/base.rs             |   6 +-
 src/librustc/middle/trans/closure.rs          |  94 +-
 src/librustc/middle/trans/context.rs          |   4 -
 src/librustc/middle/trans/inline.rs           |   2 +-
 src/librustc/middle/ty.rs                     |  33 +-
 src/librustc/middle/typeck/check/regionck.rs  |  49 +-
 src/libsyntax/ast_util.rs                     |  25 +-
 .../compile-fail/moves-sru-moved-field.rs     |   4 +-
 27 files changed, 1271 insertions(+), 1630 deletions(-)
 create mode 100644 src/librustc/middle/expr_use_visitor.rs
 delete mode 100644 src/librustc/middle/moves.rs

diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 62ecacd2c3a..2bc3ed0fce6 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -23,7 +23,7 @@ use metadata::{creader, filesearch};
 use metadata::cstore::CStore;
 use metadata::creader::Loader;
 use metadata;
-use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable};
+use middle::{trans, freevars, kind, ty, typeck, lint, reachable};
 use middle;
 use util::common::time;
 use util::ppaux;
@@ -35,7 +35,6 @@ use std::cell::{Cell, RefCell};
 use std::io;
 use std::io::fs;
 use std::io::MemReader;
-use std::mem::drop;
 use std::os;
 use getopts::{optopt, optmulti, optflag, optflagopt};
 use getopts;
@@ -270,7 +269,6 @@ pub struct CrateAnalysis {
     pub exported_items: middle::privacy::ExportedItems,
     pub public_items: middle::privacy::PublicItems,
     pub ty_cx: ty::ctxt,
-    pub maps: astencode::Maps,
     pub reachable: NodeSet,
 }
 
@@ -346,21 +344,14 @@ pub fn phase_3_run_analysis_passes(sess: Session,
     time(time_passes, "effect checking", (), |_|
          middle::effect::check_crate(&ty_cx, krate));
 
-    let middle::moves::MoveMaps {moves_map, capture_map} =
-        time(time_passes, "compute moves", (), |_|
-             middle::moves::compute_moves(&ty_cx, krate));
-
     time(time_passes, "match checking", (), |_|
-         middle::check_match::check_crate(&ty_cx, &moves_map, krate));
+         middle::check_match::check_crate(&ty_cx, krate));
 
     time(time_passes, "liveness checking", (), |_|
-         middle::liveness::check_crate(&ty_cx, &capture_map, krate));
+         middle::liveness::check_crate(&ty_cx, krate));
 
     time(time_passes, "borrow checking", (), |_|
-         middle::borrowck::check_crate(&ty_cx, &moves_map,
-                                       &capture_map, krate));
-
-    drop(moves_map);
+         middle::borrowck::check_crate(&ty_cx, krate));
 
     time(time_passes, "kind checking", (), |_|
          kind::check_crate(&ty_cx, krate));
@@ -384,9 +375,6 @@ pub fn phase_3_run_analysis_passes(sess: Session,
         ty_cx: ty_cx,
         exported_items: exported_items,
         public_items: public_items,
-        maps: astencode::Maps {
-            capture_map: RefCell::new(capture_map)
-        },
         reachable: reachable_map
     }
 }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 8ffba905773..25a4adcfca1 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -86,13 +86,13 @@ pub mod middle {
     pub mod astencode;
     pub mod lang_items;
     pub mod privacy;
-    pub mod moves;
     pub mod entry;
     pub mod effect;
     pub mod reachable;
     pub mod graph;
     pub mod cfg;
     pub mod dead;
+    pub mod expr_use_visitor;
 }
 
 pub mod front {
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 7f3757c478c..5ada0068a47 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -24,8 +24,7 @@ use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter,
                          RegionParameter};
 use metadata::tyencode;
 use middle::typeck::{MethodCall, MethodCallee, MethodOrigin};
-use middle::{ty, typeck, moves};
-use middle;
+use middle::{ty, typeck};
 use util::ppaux::ty_to_str;
 
 use syntax::{ast, ast_map, ast_util, codemap, fold};
@@ -36,7 +35,6 @@ use syntax;
 
 use libc;
 use std::cast;
-use std::cell::RefCell;
 use std::io::Seek;
 use std::io::MemWriter;
 use std::rc::Rc;
@@ -52,15 +50,9 @@ use writer = serialize::ebml::writer;
 #[cfg(test)] use syntax::parse;
 #[cfg(test)] use syntax::print::pprust;
 
-// Auxiliary maps of things to be encoded
-pub struct Maps {
-    pub capture_map: RefCell<middle::moves::CaptureMap>,
-}
-
 struct DecodeContext<'a> {
     cdata: &'a cstore::crate_metadata,
     tcx: &'a ty::ctxt,
-    maps: &'a Maps
 }
 
 struct ExtendedDecodeContext<'a> {
@@ -84,8 +76,7 @@ pub type Encoder<'a> = writer::Encoder<'a, MemWriter>;
 
 pub fn encode_inlined_item(ecx: &e::EncodeContext,
                            ebml_w: &mut Encoder,
-                           ii: e::InlinedItemRef,
-                           maps: &Maps) {
+                           ii: e::InlinedItemRef) {
     let id = match ii {
         e::IIItemRef(i) => i.id,
         e::IIForeignRef(i) => i.id,
@@ -101,7 +92,7 @@ pub fn encode_inlined_item(ecx: &e::EncodeContext,
     ebml_w.start_tag(c::tag_ast as uint);
     id_range.encode(ebml_w);
     encode_ast(ebml_w, ii);
-    encode_side_tables_for_ii(ecx, maps, ebml_w, &ii);
+    encode_side_tables_for_ii(ecx, ebml_w, &ii);
     ebml_w.end_tag();
 
     debug!("< Encoded inlined fn: {} ({})",
@@ -111,14 +102,12 @@ pub fn encode_inlined_item(ecx: &e::EncodeContext,
 
 pub fn decode_inlined_item(cdata: &cstore::crate_metadata,
                            tcx: &ty::ctxt,
-                           maps: &Maps,
                            path: Vec<ast_map::PathElem>,
                            par_doc: ebml::Doc)
                            -> Result<ast::InlinedItem, Vec<ast_map::PathElem>> {
     let dcx = &DecodeContext {
         cdata: cdata,
         tcx: tcx,
-        maps: maps
     };
     match par_doc.opt_child(c::tag_ast) {
       None => Err(path),
@@ -551,32 +540,6 @@ impl tr for freevar_entry {
     }
 }
 
-// ______________________________________________________________________
-// Encoding and decoding of CaptureVar information
-
-trait capture_var_helper {
-    fn read_capture_var(&mut self, xcx: &ExtendedDecodeContext)
-                        -> moves::CaptureVar;
-}
-
-impl<'a> capture_var_helper for reader::Decoder<'a> {
-    fn read_capture_var(&mut self, xcx: &ExtendedDecodeContext)
-                        -> moves::CaptureVar {
-        let cvar: moves::CaptureVar = Decodable::decode(self).unwrap();
-        cvar.tr(xcx)
-    }
-}
-
-impl tr for moves::CaptureVar {
-    fn tr(&self, xcx: &ExtendedDecodeContext) -> moves::CaptureVar {
-        moves::CaptureVar {
-            def: self.def.tr(xcx),
-            span: self.span.tr(xcx),
-            mode: self.mode
-        }
-    }
-}
-
 // ______________________________________________________________________
 // Encoding and decoding of MethodCallee
 
@@ -935,7 +898,6 @@ impl<'a> write_tag_and_id for Encoder<'a> {
 struct SideTableEncodingIdVisitor<'a,'b> {
     ecx_ptr: *libc::c_void,
     new_ebml_w: &'a mut Encoder<'b>,
-    maps: &'a Maps,
 }
 
 impl<'a,'b> ast_util::IdVisitingOperation for
@@ -953,12 +915,11 @@ impl<'a,'b> ast_util::IdVisitingOperation for
         let ecx: &e::EncodeContext = unsafe {
             cast::transmute(self.ecx_ptr)
         };
-        encode_side_tables_for_id(ecx, self.maps, &mut new_ebml_w, id)
+        encode_side_tables_for_id(ecx, &mut new_ebml_w, id)
     }
 }
 
 fn encode_side_tables_for_ii(ecx: &e::EncodeContext,
-                             maps: &Maps,
                              ebml_w: &mut Encoder,
                              ii: &ast::InlinedItem) {
     ebml_w.start_tag(c::tag_table as uint);
@@ -974,13 +935,11 @@ fn encode_side_tables_for_ii(ecx: &e::EncodeContext,
             cast::transmute(ecx)
         },
         new_ebml_w: &mut new_ebml_w,
-        maps: maps,
     });
     ebml_w.end_tag();
 }
 
 fn encode_side_tables_for_id(ecx: &e::EncodeContext,
-                             maps: &Maps,
                              ebml_w: &mut Encoder,
                              id: ast::NodeId) {
     let tcx = ecx.tcx;
@@ -1096,17 +1055,6 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
             })
         })
     }
-
-    for &cap_vars in maps.capture_map.borrow().find(&id).iter() {
-        ebml_w.tag(c::tag_table_capture_map, |ebml_w| {
-            ebml_w.id(id);
-            ebml_w.tag(c::tag_table_val, |ebml_w| {
-                ebml_w.emit_from_vec(cap_vars.as_slice(), |ebml_w, cap_var| {
-                    cap_var.encode(ebml_w)
-                });
-            })
-        })
-    }
 }
 
 trait doc_decoder_helpers {
@@ -1405,15 +1353,6 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext,
                         let adj: ty::AutoAdjustment = val_dsr.read_auto_adjustment(xcx);
                         dcx.tcx.adjustments.borrow_mut().insert(id, adj);
                     }
-                    c::tag_table_capture_map => {
-                        let cvars =
-                                val_dsr.read_to_vec(
-                                            |val_dsr| Ok(val_dsr.read_capture_var(xcx)))
-                                       .unwrap()
-                                       .move_iter()
-                                       .collect();
-                        dcx.maps.capture_map.borrow_mut().insert(id, Rc::new(cvars));
-                    }
                     _ => {
                         xcx.dcx.tcx.sess.bug(
                             format!("unknown tag found in side tables: {:x}", tag));
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index d0f47966832..a24e7d66380 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -18,9 +18,10 @@
 // 4. moves do not affect things loaned out in any way
 
 
-use mc = middle::mem_categorization;
 use middle::borrowck::*;
-use middle::moves;
+use euv = middle::expr_use_visitor;
+use middle::freevars;
+use mc = middle::mem_categorization;
 use middle::ty;
 use middle::typeck::MethodCall;
 use syntax::ast;
@@ -288,7 +289,7 @@ impl<'a> CheckLoanCtxt<'a> {
             }
 
             match new_loan.cause {
-                ClosureCapture(span) => {
+                euv::ClosureCapture(span) => {
                     self.bccx.span_note(
                         span,
                         format!("borrow occurs due to use of `{}` in closure",
@@ -321,13 +322,17 @@ impl<'a> CheckLoanCtxt<'a> {
             };
 
             let borrow_summary = match old_loan.cause {
-                ClosureCapture(_) => {
+                euv::ClosureCapture(_) => {
                     format!("previous borrow of `{}` occurs here due to \
                             use in closure",
                             self.bccx.loan_path_to_str(&*old_loan.loan_path))
                 }
 
-                AddrOf | AutoRef | RefBinding | ClosureInvocation => {
+                euv::OverloadedOperator(..) |
+                euv::AddrOf(..) |
+                euv::AutoRef(..) |
+                euv::ClosureInvocation(..) |
+                euv::RefBinding(..) => {
                     format!("previous borrow of `{}` occurs here",
                             self.bccx.loan_path_to_str(&*old_loan.loan_path))
                 }
@@ -711,29 +716,33 @@ impl<'a> CheckLoanCtxt<'a> {
     fn check_captured_variables(&self,
                                 closure_id: ast::NodeId,
                                 span: Span) {
-        for cap_var in self.bccx.capture_map.get(&closure_id).iter() {
-            let var_id = ast_util::def_id_of_def(cap_var.def).node;
-            self.check_if_path_is_moved(closure_id, span,
-                                        MovedInCapture, &Rc::new(LpVar(var_id)));
-            match cap_var.mode {
-                moves::CapRef | moves::CapCopy => {}
-                moves::CapMove => {
-                    check_by_move_capture(self, closure_id, cap_var, &LpVar(var_id));
+        let freevar_mode = freevars::get_capture_mode(self.tcx(), closure_id);
+        freevars::with_freevars(self.tcx(), closure_id, |freevars| {
+            for freevar in freevars.iter() {
+                let var_id = ast_util::def_id_of_def(freevar.def).node;
+                let var_path = Rc::new(LpVar(var_id));
+                self.check_if_path_is_moved(closure_id, span,
+                                            MovedInCapture, &var_path);
+                match freevar_mode {
+                    freevars::CaptureByRef => { }
+                    freevars::CaptureByValue => {
+                        check_by_move_capture(self, closure_id, freevar, &*var_path);
+                    }
                 }
             }
-        }
+        });
         return;
 
         fn check_by_move_capture(this: &CheckLoanCtxt,
                                  closure_id: ast::NodeId,
-                                 cap_var: &moves::CaptureVar,
+                                 freevar: &freevars::freevar_entry,
                                  move_path: &LoanPath) {
             let move_err = this.analyze_move_out_from(closure_id, move_path);
             match move_err {
                 MoveOk => {}
                 MoveWhileBorrowed(loan_path, loan_span) => {
                     this.bccx.span_err(
-                        cap_var.span,
+                        freevar.span,
                         format!("cannot move `{}` into closure \
                                 because it is borrowed",
                                 this.bccx.loan_path_to_str(move_path)));
diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs
index f4eee0526dd..16fd03c1aad 100644
--- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs
+++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs
@@ -17,7 +17,7 @@ use middle::borrowck::*;
 use middle::borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
 use middle::borrowck::gather_loans::move_error::MoveSpanAndPath;
 use middle::borrowck::move_data::*;
-use middle::moves;
+use euv = middle::expr_use_visitor;
 use middle::ty;
 use syntax::ast;
 use syntax::codemap::Span;
@@ -44,10 +44,10 @@ pub fn gather_decl(bccx: &BorrowckCtxt,
 pub fn gather_move_from_expr(bccx: &BorrowckCtxt,
                              move_data: &MoveData,
                              move_error_collector: &MoveErrorCollector,
-                             move_expr: &ast::Expr,
+                             move_expr_id: ast::NodeId,
                              cmt: mc::cmt) {
     let move_info = GatherMoveInfo {
-        id: move_expr.id,
+        id: move_expr_id,
         kind: MoveExpr,
         cmt: cmt,
         span_path_opt: None,
@@ -76,29 +76,6 @@ pub fn gather_move_from_pat(bccx: &BorrowckCtxt,
     gather_move(bccx, move_data, move_error_collector, move_info);
 }
 
-pub fn gather_captures(bccx: &BorrowckCtxt,
-                       move_data: &MoveData,
-                       move_error_collector: &MoveErrorCollector,
-                       closure_expr: &ast::Expr) {
-    for captured_var in bccx.capture_map.get(&closure_expr.id).iter() {
-        match captured_var.mode {
-            moves::CapMove => {
-                let cmt = bccx.cat_captured_var(closure_expr.id,
-                                                closure_expr.span,
-                                                captured_var);
-                let move_info = GatherMoveInfo {
-                    id: closure_expr.id,
-                    kind: Captured,
-                    cmt: cmt,
-                    span_path_opt: None
-                };
-                gather_move(bccx, move_data, move_error_collector, move_info);
-            }
-            moves::CapCopy | moves::CapRef => {}
-        }
-    }
-}
-
 fn gather_move(bccx: &BorrowckCtxt,
                move_data: &MoveData,
                move_error_collector: &MoveErrorCollector,
@@ -110,6 +87,7 @@ fn gather_move(bccx: &BorrowckCtxt,
                 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);
@@ -134,27 +112,14 @@ pub fn gather_assignment(bccx: &BorrowckCtxt,
                          assignment_id: ast::NodeId,
                          assignment_span: Span,
                          assignee_loan_path: Rc<LoanPath>,
-                         assignee_id: ast::NodeId) {
+                         assignee_id: ast::NodeId,
+                         mode: euv::MutateMode) {
     move_data.add_assignment(bccx.tcx,
                              assignee_loan_path,
                              assignment_id,
                              assignment_span,
                              assignee_id,
-                             false);
-}
-
-pub fn gather_move_and_assignment(bccx: &BorrowckCtxt,
-                                  move_data: &MoveData,
-                                  assignment_id: ast::NodeId,
-                                  assignment_span: Span,
-                                  assignee_loan_path: Rc<LoanPath>,
-                                  assignee_id: ast::NodeId) {
-    move_data.add_assignment(bccx.tcx,
-                             assignee_loan_path,
-                             assignment_id,
-                             assignment_span,
-                             assignee_id,
-                             true);
+                             mode);
 }
 
 fn check_and_get_illegal_move_origin(bccx: &BorrowckCtxt,
diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
index 4c17ddbb31f..3337a56edf0 100644
--- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs
+++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
@@ -14,6 +14,7 @@
  */
 
 use middle::borrowck::*;
+use euv = middle::expr_use_visitor;
 use mc = middle::mem_categorization;
 use middle::ty;
 use util::ppaux::Repr;
@@ -24,9 +25,8 @@ type R = Result<(),()>;
 
 pub fn guarantee_lifetime(bccx: &BorrowckCtxt,
                           item_scope_id: ast::NodeId,
-                          root_scope_id: ast::NodeId,
                           span: Span,
-                          cause: LoanCause,
+                          cause: euv::LoanCause,
                           cmt: mc::cmt,
                           loan_region: ty::Region,
                           loan_kind: ty::BorrowKind)
@@ -39,8 +39,7 @@ pub fn guarantee_lifetime(bccx: &BorrowckCtxt,
                                          cause: cause,
                                          loan_region: loan_region,
                                          loan_kind: loan_kind,
-                                         cmt_original: cmt.clone(),
-                                         root_scope_id: root_scope_id};
+                                         cmt_original: cmt.clone()};
     ctxt.check(&cmt, None)
 }
 
@@ -53,12 +52,8 @@ struct GuaranteeLifetimeContext<'a> {
     // the node id of the function body for the enclosing item
     item_scope_id: ast::NodeId,
 
-    // the node id of the innermost loop / function body; this is the
-    // longest scope for which we can root managed boxes
-    root_scope_id: ast::NodeId,
-
     span: Span,
-    cause: LoanCause,
+    cause: euv::LoanCause,
     loan_region: ty::Region,
     loan_kind: ty::BorrowKind,
     cmt_original: mc::cmt
diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs
index 8bb95b798d0..b351c99084d 100644
--- a/src/librustc/middle/borrowck/gather_loans/mod.rs
+++ b/src/librustc/middle/borrowck/gather_loans/mod.rs
@@ -18,323 +18,144 @@
 
 use middle::borrowck::*;
 use middle::borrowck::move_data::MoveData;
+use euv = middle::expr_use_visitor;
 use mc = middle::mem_categorization;
-use middle::moves;
-use middle::pat_util;
-use middle::ty::{ty_region};
 use middle::ty;
-use middle::typeck::MethodCall;
-use util::common::indenter;
 use util::ppaux::{Repr};
 
 use syntax::ast;
-use syntax::ast_util;
-use syntax::ast_util::IdRange;
 use syntax::codemap::Span;
-use syntax::print::pprust;
 use syntax::visit;
-use syntax::visit::{Visitor, FnKind};
-use syntax::ast::{Expr, FnDecl, Block, NodeId, Stmt, Pat, Local};
-
-use std::rc::Rc;
+use syntax::visit::{Visitor};
+use syntax::ast::{Expr, FnDecl, Block, NodeId, Pat};
 
 mod lifetime;
 mod restrictions;
 mod gather_moves;
 mod move_error;
 
-/// Context used while gathering loans:
-///
-/// - `bccx`: the borrow check context
-/// - `item_ub`: the id of the block for the enclosing fn/method item
-/// - `root_ub`: the id of the outermost block for which we can root
-///   an `@T`.  This is the id of the innermost enclosing
-///   loop or function body.
-///
-/// The role of `root_ub` is to prevent us from having to accumulate
-/// vectors of rooted items at runtime.  Consider this case:
-///
-///     fn foo(...) -> int {
-///         let mut ptr: &int;
-///         while some_cond {
-///             let x: @int = ...;
-///             ptr = &*x;
-///         }
-///         *ptr
-///     }
-///
-/// If we are not careful here, we would infer the scope of the borrow `&*x`
-/// to be the body of the function `foo()` as a whole.  We would then
-/// have root each `@int` that is produced, which is an unbounded number.
-/// No good.  Instead what will happen is that `root_ub` will be set to the
-/// body of the while loop and we will refuse to root the pointer `&*x`
-/// because it would have to be rooted for a region greater than `root_ub`.
+pub fn gather_loans_in_fn(bccx: &BorrowckCtxt,
+                          decl: &ast::FnDecl,
+                          body: &ast::Block)
+                          -> (Vec<Loan>, move_data::MoveData)
+{
+    let mut glcx = GatherLoanCtxt {
+        bccx: bccx,
+        all_loans: Vec::new(),
+        item_ub: body.id,
+        move_data: MoveData::new(),
+        move_error_collector: move_error::MoveErrorCollector::new(),
+    };
+
+    {
+        let mut euv = euv::ExprUseVisitor::new(&mut glcx, bccx.tcx);
+        euv.walk_fn(decl, body);
+    }
+
+    glcx.report_potential_errors();
+    let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
+    (all_loans, move_data)
+}
+
 struct GatherLoanCtxt<'a> {
     bccx: &'a BorrowckCtxt<'a>,
-    id_range: IdRange,
     move_data: move_data::MoveData,
     move_error_collector: move_error::MoveErrorCollector,
     all_loans: Vec<Loan>,
     item_ub: ast::NodeId,
-    repeating_ids: Vec<ast::NodeId> }
-
-impl<'a> visit::Visitor<()> for GatherLoanCtxt<'a> {
-    fn visit_expr(&mut self, ex: &Expr, _: ()) {
-        gather_loans_in_expr(self, ex);
-    }
-    fn visit_block(&mut self, b: &Block, _: ()) {
-        gather_loans_in_block(self, b);
-    }
-
-    /// Do not visit closures or fn items here, the outer loop in
-    /// borrowck/mod will visit them for us in turn.
-    fn visit_fn(&mut self, _: &FnKind, _: &FnDecl, _: &Block,
-                _: Span, _: NodeId, _: ()) {}
-
-    fn visit_stmt(&mut self, s: &Stmt, _: ()) {
-        visit::walk_stmt(self, s, ());
-    }
-    fn visit_pat(&mut self, p: &Pat, _: ()) {
-        add_pat_to_id_range(self, p);
-    }
-    fn visit_local(&mut self, l: &Local, _: ()) {
-        gather_loans_in_local(self, l);
-    }
-
-    // #7740: Do not visit items here, not even fn items nor methods
-    // of impl items; the outer loop in borrowck/mod will visit them
-    // for us in turn.  Thus override visit_item's walk with a no-op.
-    fn visit_item(&mut self, _: &ast::Item, _: ()) {}
 }
 
-fn add_pat_to_id_range(this: &mut GatherLoanCtxt,
-                       p: &ast::Pat) {
-    // NB: This visitor function just adds the pat ids into the id
-    // range. We gather loans that occur in patterns using the
-    // `gather_pat()` method below. Eventually these two should be
-    // brought together.
-    this.id_range.add(p.id);
-    visit::walk_pat(this, p, ());
-}
+impl<'a> euv::Delegate for GatherLoanCtxt<'a> {
+    fn consume(&mut self,
+               consume_id: ast::NodeId,
+               _consume_span: Span,
+               cmt: mc::cmt,
+               mode: euv::ConsumeMode) {
+        debug!("consume(consume_id={}, cmt={}, mode={})",
+               consume_id, cmt.repr(self.tcx()), mode);
 
-pub fn gather_loans_in_fn(bccx: &BorrowckCtxt, decl: &ast::FnDecl, body: &ast::Block)
-                    -> (IdRange, Vec<Loan>, move_data::MoveData) {
-    let mut glcx = GatherLoanCtxt {
-        bccx: bccx,
-        id_range: IdRange::max(),
-        all_loans: Vec::new(),
-        item_ub: body.id,
-        repeating_ids: vec!(body.id),
-        move_data: MoveData::new(),
-        move_error_collector: move_error::MoveErrorCollector::new(),
-    };
-    glcx.gather_fn_arg_patterns(decl, body);
-
-    glcx.visit_block(body, ());
-    glcx.report_potential_errors();
-    let GatherLoanCtxt { id_range, all_loans, move_data, .. } = glcx;
-    (id_range, all_loans, move_data)
-}
-
-fn gather_loans_in_block(this: &mut GatherLoanCtxt,
-                         blk: &ast::Block) {
-    this.id_range.add(blk.id);
-    visit::walk_block(this, blk, ());
-}
-
-fn gather_loans_in_local(this: &mut GatherLoanCtxt,
-                         local: &ast::Local) {
-    match local.init {
-        None => {
-            // Variable declarations without initializers are considered "moves":
-            let tcx = this.bccx.tcx;
-            pat_util::pat_bindings(&tcx.def_map, local.pat, |_, id, span, _| {
-                gather_moves::gather_decl(this.bccx,
-                                          &this.move_data,
-                                          id,
-                                          span,
-                                          id);
-            })
+        match mode {
+            euv::Copy => { return; }
+            euv::Move => { }
         }
-        Some(init) => {
-            // Variable declarations with initializers are considered "assigns",
-            // which is handled by `gather_pat`:
-            let init_cmt = this.bccx.cat_expr(init);
-            this.gather_pat(init_cmt, local.pat, None);
-        }
-    }
 
-    visit::walk_local(this, local, ());
-}
-
-fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
-                        ex: &ast::Expr) {
-    let bccx = this.bccx;
-    let tcx = bccx.tcx;
-
-    debug!("gather_loans_in_expr(expr={:?}/{})",
-           ex.id, pprust::expr_to_str(ex));
-
-    this.id_range.add(ex.id);
-
-    // If this expression is borrowed, have to ensure it remains valid:
-    for &adjustments in tcx.adjustments.borrow().find(&ex.id).iter() {
-        this.guarantee_adjustments(ex, adjustments);
-    }
-
-    // If this expression is a move, gather it:
-    if this.bccx.is_move(ex.id) {
-        let cmt = this.bccx.cat_expr(ex);
         gather_moves::gather_move_from_expr(
-            this.bccx, &this.move_data, &this.move_error_collector, ex, cmt);
+            self.bccx, &self.move_data, &self.move_error_collector,
+            consume_id, cmt);
     }
 
-    // Special checks for various kinds of expressions:
-    let method_map = this.bccx.tcx.method_map.borrow();
-    match ex.node {
-      ast::ExprAddrOf(mutbl, base) => {
-        let base_cmt = this.bccx.cat_expr(base);
+    fn consume_pat(&mut self,
+                   consume_pat: &ast::Pat,
+                   cmt: mc::cmt,
+                   mode: euv::ConsumeMode) {
+        debug!("consume_pat(consume_pat={}, cmt={}, mode={})",
+               consume_pat.repr(self.tcx()),
+               cmt.repr(self.tcx()),
+               mode);
 
-        // make sure that the thing we are pointing out stays valid
-        // for the lifetime `scope_r` of the resulting ptr:
-        let expr_ty = ty::expr_ty(tcx, ex);
-        if !ty::type_is_bot(expr_ty) {
-            let scope_r = ty_region(tcx, ex.span, expr_ty);
-            this.guarantee_valid(ex.id,
-                                 ex.span,
-                                 base_cmt,
-                                 mutbl,
-                                 scope_r,
-                                 AddrOf);
+        match mode {
+            euv::Copy => { return; }
+            euv::Move => { }
         }
-        visit::walk_expr(this, ex, ());
-      }
 
-      ast::ExprAssign(l, _) => {
-          with_assignee_loan_path(
-              this.bccx, l,
-              |lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
-                                                   ex.id, ex.span, lp, l.id));
-          visit::walk_expr(this, ex, ());
-      }
+        gather_moves::gather_move_from_pat(
+            self.bccx, &self.move_data, &self.move_error_collector,
+            consume_pat, cmt);
+    }
 
-      ast::ExprAssignOp(_, l, _) => {
-          with_assignee_loan_path(
-              this.bccx, l,
-              |lp| gather_moves::gather_move_and_assignment(this.bccx, &this.move_data,
-                                                            ex.id, ex.span, lp, l.id));
-          visit::walk_expr(this, ex, ());
-      }
+    fn borrow(&mut self,
+              borrow_id: ast::NodeId,
+              borrow_span: Span,
+              cmt: mc::cmt,
+              loan_region: ty::Region,
+              bk: ty::BorrowKind,
+              loan_cause: euv::LoanCause)
+    {
+        debug!("borrow(borrow_id={}, cmt={}, loan_region={}, \
+               bk={}, loan_cause={:?})",
+               borrow_id, cmt.repr(self.tcx()), loan_region,
+               bk, loan_cause);
 
-      ast::ExprMatch(ex_v, ref arms) => {
-        let cmt = this.bccx.cat_expr(ex_v);
-        for arm in arms.iter() {
-            for pat in arm.pats.iter() {
-                this.gather_pat(cmt.clone(), *pat, Some((arm.body.id, ex.id)));
+        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,
+              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.
             }
         }
-        visit::walk_expr(this, ex, ());
-      }
+    }
 
-      ast::ExprIndex(_, arg) |
-      ast::ExprBinary(_, _, arg)
-      if method_map.contains_key(&MethodCall::expr(ex.id)) => {
-          // Arguments in method calls are always passed by ref.
-          //
-          // Currently these do not use adjustments, so we have to
-          // hardcode this check here (note that the receiver DOES use
-          // adjustments).
-          let scope_r = ty::ReScope(ex.id);
-          let arg_cmt = this.bccx.cat_expr(arg);
-          this.guarantee_valid(arg.id,
-                               arg.span,
-                               arg_cmt,
-                               ast::MutImmutable,
-                               scope_r,
-                               AutoRef);
-          visit::walk_expr(this, ex, ());
-      }
-
-      // see explanation attached to the `root_ub` field:
-      ast::ExprWhile(cond, body) => {
-          // during the condition, can only root for the condition
-          this.push_repeating_id(cond.id);
-          this.visit_expr(cond, ());
-          this.pop_repeating_id(cond.id);
-
-          // during body, can only root for the body
-          this.push_repeating_id(body.id);
-          this.visit_block(body, ());
-          this.pop_repeating_id(body.id);
-      }
-
-      // see explanation attached to the `root_ub` field:
-      ast::ExprLoop(body, _) => {
-          this.push_repeating_id(body.id);
-          visit::walk_expr(this, ex, ());
-          this.pop_repeating_id(body.id);
-      }
-
-      ast::ExprFnBlock(..) | ast::ExprProc(..) => {
-          gather_moves::gather_captures(this.bccx, &this.move_data,
-                                        &this.move_error_collector, ex);
-          this.guarantee_captures(ex);
-          visit::walk_expr(this, ex, ());
-      }
-
-      ast::ExprInlineAsm(ref ia) => {
-          for &(_, out) in ia.outputs.iter() {
-              with_assignee_loan_path(
-                  this.bccx, out,
-                  |lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
-                                                       ex.id, ex.span, lp, out.id));
-          }
-          visit::walk_expr(this, ex, ());
-      }
-
-      ast::ExprCall(f, _) => {
-          let expr_ty = ty::expr_ty_adjusted(tcx, f);
-          match ty::get(expr_ty).sty {
-              ty::ty_closure(~ty::ClosureTy {
-                  store: ty::RegionTraitStore(..), ..
-              }) => {
-                  let scope_r = ty::ReScope(ex.id);
-                  let base_cmt = this.bccx.cat_expr(f);
-                  this.guarantee_valid_kind(f.id,
-                                            f.span,
-                                            base_cmt,
-                                            ty::UniqueImmBorrow,
-                                            scope_r,
-                                            ClosureInvocation);
-              }
-              _ => {}
-          }
-          visit::walk_expr(this, ex, ());
-      }
-
-      _ => {
-          visit::walk_expr(this, ex, ());
-      }
+    fn decl_without_init(&mut self, id: ast::NodeId, span: Span) {
+        gather_moves::gather_decl(self.bccx, &self.move_data, id, span, id);
     }
 }
 
-fn with_assignee_loan_path(bccx: &BorrowckCtxt, expr: &ast::Expr, op: |Rc<LoanPath>|) {
-    let cmt = bccx.cat_expr(expr);
-    match opt_loan_path(&cmt) {
-        Some(lp) => op(lp),
-        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.
-        }
-    }
-}
-
-
 /// Implements the A-* rules in doc.rs.
 fn check_aliasability(bccx: &BorrowckCtxt,
                       borrow_span: Span,
-                      loan_cause: LoanCause,
+                      loan_cause: euv::LoanCause,
                       cmt: mc::cmt,
                       req_kind: ty::BorrowKind)
                       -> Result<(),()> {
@@ -385,167 +206,13 @@ fn check_aliasability(bccx: &BorrowckCtxt,
 impl<'a> GatherLoanCtxt<'a> {
     pub fn tcx(&self) -> &'a ty::ctxt { self.bccx.tcx }
 
-    pub fn push_repeating_id(&mut self, id: ast::NodeId) {
-        self.repeating_ids.push(id);
-    }
-
-    pub fn pop_repeating_id(&mut self, id: ast::NodeId) {
-        let popped = self.repeating_ids.pop().unwrap();
-        assert_eq!(id, popped);
-    }
-
-    pub fn guarantee_autoderefs(&mut self,
-                                expr: &ast::Expr,
-                                autoderefs: uint) {
-        let method_map = self.bccx.tcx.method_map.borrow();
-        for i in range(0, autoderefs) {
-            match method_map.find(&MethodCall::autoderef(expr.id, i as u32)) {
-                Some(method) => {
-                    // Treat overloaded autoderefs as if an AutoRef adjustment
-                    // was applied on the base type, as that is always the case.
-                    let cmt = match self.bccx.mc().cat_expr_autoderefd(expr, i) {
-                        Ok(v) => v,
-                        Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
-                    };
-                    let self_ty = *ty::ty_fn_args(method.ty).get(0);
-                    let (m, r) = match ty::get(self_ty).sty {
-                        ty::ty_rptr(r, ref m) => (m.mutbl, r),
-                        _ => self.tcx().sess.span_bug(expr.span,
-                                format!("bad overloaded deref type {}",
-                                    method.ty.repr(self.tcx())))
-                    };
-                    self.guarantee_valid(expr.id,
-                                         expr.span,
-                                         cmt,
-                                         m,
-                                         r,
-                                         AutoRef);
-                }
-                None => {}
-            }
-        }
-    }
-
-    pub fn guarantee_adjustments(&mut self,
-                                 expr: &ast::Expr,
-                                 adjustment: &ty::AutoAdjustment) {
-        debug!("guarantee_adjustments(expr={}, adjustment={:?})",
-               expr.repr(self.tcx()), adjustment);
-        let _i = indenter();
-
-        match *adjustment {
-            ty::AutoAddEnv(..) => {
-                debug!("autoaddenv -- no autoref");
-                return;
-            }
-
-            ty::AutoDerefRef(
-                ty::AutoDerefRef {
-                    autoref: None, autoderefs }) => {
-                debug!("no autoref");
-                self.guarantee_autoderefs(expr, autoderefs);
-                return;
-            }
-
-            ty::AutoDerefRef(
-                ty::AutoDerefRef {
-                    autoref: Some(ref autoref),
-                    autoderefs}) => {
-                self.guarantee_autoderefs(expr, autoderefs);
-                let mc = self.bccx.mc();
-                let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) {
-                    Ok(v) => v,
-                    Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
-                };
-                debug!("after autoderef, cmt={}", cmt.repr(self.tcx()));
-
-                match *autoref {
-                    ty::AutoPtr(r, m) => {
-                        self.guarantee_valid(expr.id,
-                                             expr.span,
-                                             cmt,
-                                             m,
-                                             r,
-                                             AutoRef)
-                    }
-                    ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
-                        let cmt_index = mc.cat_index(expr, cmt, autoderefs+1);
-                        self.guarantee_valid(expr.id,
-                                             expr.span,
-                                             cmt_index,
-                                             m,
-                                             r,
-                                             AutoRef)
-                    }
-                    ty::AutoBorrowObj(r, m) => {
-                        let cmt_deref = mc.cat_deref_obj(expr, cmt);
-                        self.guarantee_valid(expr.id,
-                                             expr.span,
-                                             cmt_deref,
-                                             m,
-                                             r,
-                                             AutoRef)
-                    }
-                    ty::AutoUnsafe(_) => {}
-                }
-            }
-
-            ty::AutoObject(..) => {
-                // FIXME: Handle ~Trait to &Trait casts here?
-            }
-        }
-    }
-
-    fn guarantee_captures(&mut self,
-                          closure_expr: &ast::Expr) {
-        for captured_var in self.bccx.capture_map.get(&closure_expr.id).iter() {
-            match captured_var.mode {
-                moves::CapCopy | moves::CapMove => { continue; }
-                moves::CapRef => { }
-            }
-
-            let var_id = ast_util::def_id_of_def(captured_var.def).node;
-            let var_cmt = self.bccx.cat_captured_var(closure_expr.id,
-                                                     closure_expr.span,
-                                                     captured_var);
-
-            // Lookup the kind of borrow the callee requires
-            let upvar_id = ty::UpvarId { var_id: var_id,
-                                         closure_expr_id: closure_expr.id };
-            let upvar_borrow = self.tcx().upvar_borrow_map.borrow()
-                                   .get_copy(&upvar_id);
-
-            self.guarantee_valid_kind(closure_expr.id,
-                                      closure_expr.span,
-                                      var_cmt,
-                                      upvar_borrow.kind,
-                                      upvar_borrow.region,
-                                      ClosureCapture(captured_var.span));
-        }
-    }
-
-    pub fn guarantee_valid(&mut self,
-                           borrow_id: ast::NodeId,
-                           borrow_span: Span,
-                           cmt: mc::cmt,
-                           req_mutbl: ast::Mutability,
-                           loan_region: ty::Region,
-                           cause: LoanCause) {
-        self.guarantee_valid_kind(borrow_id,
-                                  borrow_span,
-                                  cmt,
-                                  ty::BorrowKind::from_mutbl(req_mutbl),
-                                  loan_region,
-                                  cause);
-    }
-
-    fn guarantee_valid_kind(&mut self,
-                            borrow_id: ast::NodeId,
-                            borrow_span: Span,
-                            cmt: mc::cmt,
-                            req_kind: ty::BorrowKind,
-                            loan_region: ty::Region,
-                            cause: LoanCause) {
+    fn guarantee_valid(&mut self,
+                       borrow_id: ast::NodeId,
+                       borrow_span: Span,
+                       cmt: mc::cmt,
+                       req_kind: ty::BorrowKind,
+                       loan_region: ty::Region,
+                       cause: euv::LoanCause) {
         /*!
          * Guarantees that `addr_of(cmt)` will be valid for the duration of
          * `static_scope_r`, or reports an error.  This may entail taking
@@ -567,11 +234,9 @@ impl<'a> GatherLoanCtxt<'a> {
             return;
         }
 
-        let root_ub = { *self.repeating_ids.last().unwrap() }; // FIXME(#5074)
-
         // 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, root_ub,
+        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.
@@ -689,7 +354,7 @@ impl<'a> GatherLoanCtxt<'a> {
 
         fn check_mutability(bccx: &BorrowckCtxt,
                             borrow_span: Span,
-                            cause: LoanCause,
+                            cause: euv::LoanCause,
                             cmt: mc::cmt,
                             req_kind: ty::BorrowKind)
                             -> Result<(),()> {
@@ -804,146 +469,6 @@ impl<'a> GatherLoanCtxt<'a> {
         }
     }
 
-    fn gather_fn_arg_patterns(&mut self,
-                              decl: &ast::FnDecl,
-                              body: &ast::Block) {
-        /*!
-         * Walks the patterns for fn arguments, checking that they
-         * do not attempt illegal moves or create refs that outlive
-         * the arguments themselves. Just a shallow wrapper around
-         * `gather_pat()`.
-         */
-
-        let mc = self.bccx.mc();
-        for arg in decl.inputs.iter() {
-            let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
-
-            let arg_cmt = mc.cat_rvalue(
-                arg.id,
-                arg.pat.span,
-                ty::ReScope(body.id), // Args live only as long as the fn body.
-                arg_ty);
-
-            self.gather_pat(arg_cmt, arg.pat, None);
-        }
-    }
-
-    fn gather_pat(&mut self,
-                  discr_cmt: mc::cmt,
-                  root_pat: @ast::Pat,
-                  arm_match_ids: Option<(ast::NodeId, ast::NodeId)>) {
-        /*!
-         * Walks patterns, examining the bindings to determine if they
-         * cause borrows (`ref` bindings, vector patterns) or
-         * moves (non-`ref` bindings with linear type).
-         */
-
-        self.bccx.cat_pattern(discr_cmt, root_pat, |cmt, pat| {
-            match pat.node {
-              ast::PatIdent(bm, _, _) if self.pat_is_binding(pat) => {
-                // Each match binding is effectively an assignment.
-                let tcx = self.bccx.tcx;
-                pat_util::pat_bindings(&tcx.def_map, pat, |_, id, span, _| {
-                    gather_moves::gather_assignment(self.bccx,
-                                                    &self.move_data,
-                                                    id,
-                                                    span,
-                                                    Rc::new(LpVar(id)),
-                                                    id);
-                });
-
-                match bm {
-                  ast::BindByRef(mutbl) => {
-                    // ref x or ref x @ p --- creates a ptr which must
-                    // remain valid for the scope of the match
-
-                    // find the region of the resulting pointer (note that
-                    // the type of such a pattern will *always* be a
-                    // region pointer)
-                    let scope_r =
-                        ty_region(self.tcx(), pat.span,
-                                  ty::node_id_to_type(self.tcx(), pat.id));
-
-                    // if the scope of the region ptr turns out to be
-                    // specific to this arm, wrap the categorization
-                    // with a cat_discr() node.  There is a detailed
-                    // discussion of the function of this node in
-                    // `lifetime.rs`:
-                    let cmt_discr = match arm_match_ids {
-                        None => cmt,
-                        Some((arm_id, match_id)) => {
-                            let arm_scope = ty::ReScope(arm_id);
-                            if self.bccx.is_subregion_of(scope_r, arm_scope) {
-                                self.bccx.cat_discr(cmt, match_id)
-                            } else {
-                                cmt
-                            }
-                        }
-                    };
-                    self.guarantee_valid(pat.id,
-                                         pat.span,
-                                         cmt_discr,
-                                         mutbl,
-                                         scope_r,
-                                         RefBinding);
-                  }
-                  ast::BindByValue(_) => {
-                      // No borrows here, but there may be moves
-                      if self.bccx.is_move(pat.id) {
-                          gather_moves::gather_move_from_pat(
-                              self.bccx, &self.move_data,
-                              &self.move_error_collector, pat, cmt);
-                      }
-                  }
-                }
-              }
-
-              ast::PatVec(_, Some(slice_pat), _) => {
-                  // The `slice_pat` here creates a slice into the
-                  // original vector.  This is effectively a borrow of
-                  // the elements of the vector being matched.
-
-                  let (slice_cmt, slice_borrow_kind, slice_r) = {
-                      match self.bccx.mc().cat_slice_pattern(cmt, slice_pat) {
-                          Ok(v) => v,
-                          Err(()) => {
-                              self.tcx().sess.span_bug(slice_pat.span,
-                                                       "Err from mc")
-                          }
-                      }
-                  };
-
-                  // Note: We declare here that the borrow occurs upon
-                  // entering the `[...]` pattern. This implies that
-                  // something like `[a, ..b]` where `a` is a move is
-                  // illegal, because the borrow is already in effect.
-                  // In fact such a move would be safe-ish, but it
-                  // effectively *requires* that we use the nulling
-                  // out semantics to indicate when a value has been
-                  // moved, which we are trying to move away from.
-                  // Otherwise, how can we indicate that the first
-                  // element in the vector has been moved?
-                  // Eventually, we could perhaps modify this rule to
-                  // permit `[..a, b]` where `b` is a move, because in
-                  // that case we can adjust the length of the
-                  // original vec accordingly, but we'd have to make
-                  // trans do the right thing, and it would only work
-                  // for `~` vectors. It seems simpler to just require
-                  // that people call `vec.pop()` or `vec.unshift()`.
-                  self.guarantee_valid(pat.id, pat.span,
-                                       slice_cmt, slice_borrow_kind, slice_r,
-                                       RefBinding);
-              }
-
-              _ => {}
-            }
-        })
-    }
-
-    pub fn pat_is_binding(&self, pat: &ast::Pat) -> bool {
-        pat_util::pat_is_binding(&self.bccx.tcx.def_map, pat)
-    }
-
     pub fn report_potential_errors(&self) {
         self.move_error_collector.report_potential_errors(self.bccx);
     }
@@ -955,7 +480,6 @@ impl<'a> GatherLoanCtxt<'a> {
 /// sure the loans being taken are sound.
 struct StaticInitializerCtxt<'a> {
     bccx: &'a BorrowckCtxt<'a>,
-    id_range: IdRange,
     item_ub: ast::NodeId,
 }
 
@@ -966,7 +490,8 @@ impl<'a> visit::Visitor<()> for StaticInitializerCtxt<'a> {
                 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, AddrOf, base_cmt, borrow_kind).is_err() {
+                if check_aliasability(self.bccx, ex.span, euv::AddrOf,
+                                      base_cmt, borrow_kind).is_err() {
                     return; // reported an error, no sense in reporting more.
                 }
             }
@@ -983,7 +508,6 @@ pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &ast::E
 
     let mut sicx = StaticInitializerCtxt {
         bccx: bccx,
-        id_range: IdRange::max(),
         item_ub: expr.id,
     };
 
diff --git a/src/librustc/middle/borrowck/gather_loans/move_error.rs b/src/librustc/middle/borrowck/gather_loans/move_error.rs
index 24d873e0ff7..4662074aa47 100644
--- a/src/librustc/middle/borrowck/gather_loans/move_error.rs
+++ b/src/librustc/middle/borrowck/gather_loans/move_error.rs
@@ -100,6 +100,7 @@ fn group_errors_with_same_origin(errors: &Vec<MoveError>)
     fn append_to_grouped_errors(grouped_errors: &mut Vec<GroupedMoveErrors>,
                                 error: &MoveError) {
         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 {
@@ -107,10 +108,12 @@ fn group_errors_with_same_origin(errors: &Vec<MoveError>)
         };
         for ge in grouped_errors.mut_iter() {
             if move_from_id == ge.move_from.id && error.move_to.is_some() {
+                debug!("appending move_to to list");
                 ge.move_to_places.push_all_move(move_to);
                 return
             }
         }
+        debug!("found a new move from location");
         grouped_errors.push(GroupedMoveErrors {
             move_from: error.move_from.clone(),
             move_to_places: move_to
diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs
index 4d0249a3011..552381e4216 100644
--- a/src/librustc/middle/borrowck/gather_loans/restrictions.rs
+++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs
@@ -13,6 +13,7 @@
  */
 
 use middle::borrowck::*;
+use euv = middle::expr_use_visitor;
 use mc = middle::mem_categorization;
 use middle::ty;
 use syntax::codemap::Span;
@@ -27,7 +28,7 @@ pub enum RestrictionResult {
 
 pub fn compute_restrictions(bccx: &BorrowckCtxt,
                             span: Span,
-                            cause: LoanCause,
+                            cause: euv::LoanCause,
                             cmt: mc::cmt,
                             loan_region: ty::Region,
                             restr: RestrictionSet) -> RestrictionResult {
@@ -50,7 +51,7 @@ struct RestrictionsContext<'a> {
     span: Span,
     cmt_original: mc::cmt,
     loan_region: ty::Region,
-    cause: LoanCause,
+    cause: euv::LoanCause,
 }
 
 impl<'a> RestrictionsContext<'a> {
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 1af0e48c46d..4efb0e44e8e 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -12,16 +12,14 @@
 
 #![allow(non_camel_case_types)]
 
-use mc = middle::mem_categorization;
-use middle::ty;
-use middle::typeck;
-use middle::moves;
 use middle::dataflow::DataFlowContext;
 use middle::dataflow::DataFlowOperator;
-use util::nodemap::{NodeMap, NodeSet};
+use euv = middle::expr_use_visitor;
+use mc = middle::mem_categorization;
+use middle::ty;
 use util::ppaux::{note_and_explain_region, Repr, UserString};
 
-use std::cell::{Cell, RefCell};
+use std::cell::{Cell};
 use std::ops::{BitOr, BitAnd};
 use std::rc::Rc;
 use std::strbuf::StrBuf;
@@ -75,13 +73,9 @@ impl<'a> Visitor<()> for BorrowckCtxt<'a> {
 }
 
 pub fn check_crate(tcx: &ty::ctxt,
-                   moves_map: &NodeSet,
-                   capture_map: &moves::CaptureMap,
                    krate: &ast::Crate) {
     let mut bccx = BorrowckCtxt {
         tcx: tcx,
-        moves_map: moves_map,
-        capture_map: capture_map,
         stats: @BorrowStats {
             loaned_paths_same: Cell::new(0),
             loaned_paths_imm: Cell::new(0),
@@ -135,7 +129,8 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
     debug!("borrowck_fn(id={})", id);
 
     // Check the body of fn items.
-    let (id_range, all_loans, move_data) =
+    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, decl, body);
     let mut loan_dfcx =
         DataFlowContext::new(this.tcx,
@@ -164,8 +159,6 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
 
 pub struct BorrowckCtxt<'a> {
     tcx: &'a ty::ctxt,
-    moves_map: &'a NodeSet,
-    capture_map: &'a moves::CaptureMap,
 
     // Statistics:
     stats: @BorrowStats
@@ -199,16 +192,7 @@ pub struct Loan {
     gen_scope: ast::NodeId,
     kill_scope: ast::NodeId,
     span: Span,
-    cause: LoanCause,
-}
-
-#[deriving(Eq)]
-pub enum LoanCause {
-    ClosureCapture(Span),
-    AddrOf,
-    AutoRef,
-    RefBinding,
-    ClosureInvocation,
+    cause: euv::LoanCause,
 }
 
 #[deriving(Eq, TotalEq, Hash)]
@@ -341,14 +325,14 @@ pub enum bckerr_code {
 #[deriving(Eq)]
 pub struct BckError {
     span: Span,
-    cause: LoanCause,
+    cause: euv::LoanCause,
     cmt: mc::cmt,
     code: bckerr_code
 }
 
 pub enum AliasableViolationKind {
     MutabilityViolation,
-    BorrowViolation(LoanCause)
+    BorrowViolation(euv::LoanCause)
 }
 
 pub enum MovedValueUseKind {
@@ -370,14 +354,8 @@ impl<'a> BorrowckCtxt<'a> {
         self.tcx.region_maps.is_subscope_of(r_sub, r_sup)
     }
 
-    pub fn is_move(&self, id: ast::NodeId) -> bool {
-        self.moves_map.contains(&id)
-    }
-
-    pub fn mc(&self) -> mc::MemCategorizationContext<&'a ty::ctxt> {
-        mc::MemCategorizationContext {
-            typer: self.tcx,
-        }
+    pub fn mc(&self) -> mc::MemCategorizationContext<'a,ty::ctxt> {
+        mc::MemCategorizationContext::new(self.tcx)
     }
 
     pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt {
@@ -439,14 +417,15 @@ impl<'a> BorrowckCtxt<'a> {
     }
 
     pub fn cat_captured_var(&self,
-                            id: ast::NodeId,
-                            span: Span,
-                            captured_var: &moves::CaptureVar) -> mc::cmt {
+                            closure_id: ast::NodeId,
+                            closure_span: Span,
+                            upvar_def: ast::Def)
+                            -> mc::cmt {
         // Create the cmt for the variable being borrowed, from the
         // caller's perspective
-        let var_id = ast_util::def_id_of_def(captured_var.def).node;
+        let var_id = ast_util::def_id_of_def(upvar_def).node;
         let var_ty = ty::node_id_to_type(self.tcx, var_id);
-        self.cat_def(id, span, var_ty, captured_var.def)
+        self.cat_def(closure_id, closure_span, var_ty, upvar_def)
     }
 
     pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt {
@@ -604,13 +583,16 @@ impl<'a> BorrowckCtxt<'a> {
                 };
 
                 match err.cause {
-                    ClosureCapture(_) => {
+                    euv::ClosureCapture(_) => {
                         format!("closure cannot assign to {}", descr)
                     }
-                    AddrOf | RefBinding | AutoRef => {
+                    euv::OverloadedOperator |
+                    euv::AddrOf |
+                    euv::RefBinding |
+                    euv::AutoRef => {
                         format!("cannot borrow {} as mutable", descr)
                     }
-                    ClosureInvocation => {
+                    euv::ClosureInvocation => {
                         self.tcx.sess.span_bug(err.span,
                             "err_mutbl with a closure invocation");
                     }
@@ -644,7 +626,7 @@ impl<'a> BorrowckCtxt<'a> {
             MutabilityViolation => {
                 "cannot assign to data"
             }
-            BorrowViolation(ClosureCapture(_)) => {
+            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
@@ -654,13 +636,14 @@ impl<'a> BorrowckCtxt<'a> {
                     span,
                     "aliasability violation with closure");
             }
-            BorrowViolation(AddrOf) |
-            BorrowViolation(AutoRef) |
-            BorrowViolation(RefBinding) => {
+            BorrowViolation(euv::OverloadedOperator) |
+            BorrowViolation(euv::AddrOf) |
+            BorrowViolation(euv::AutoRef) |
+            BorrowViolation(euv::RefBinding) => {
                 "cannot borrow data mutably"
             }
 
-            BorrowViolation(ClosureInvocation) => {
+            BorrowViolation(euv::ClosureInvocation) => {
                 "closure invocation"
             }
         };
@@ -839,34 +822,3 @@ impl Repr for LoanPath {
     }
 }
 
-///////////////////////////////////////////////////////////////////////////
-
-impl<'a> mc::Typer for &'a ty::ctxt {
-    fn tcx<'a>(&'a self) -> &'a ty::ctxt {
-        *self
-    }
-
-    fn node_ty(&self, id: ast::NodeId) -> mc::McResult<ty::t> {
-        Ok(ty::node_id_to_type(*self, id))
-    }
-
-    fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option<ty::t> {
-        self.method_map.borrow().find(&method_call).map(|method| method.ty)
-    }
-
-    fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment>> {
-        &self.adjustments
-    }
-
-    fn is_method_call(&self, id: ast::NodeId) -> bool {
-        self.method_map.borrow().contains_key(&typeck::MethodCall::expr(id))
-    }
-
-    fn temporary_scope(&self, id: ast::NodeId) -> Option<ast::NodeId> {
-        self.region_maps.temporary_scope(id)
-    }
-
-    fn upvar_borrow(&self, id: ty::UpvarId) -> ty::UpvarBorrow {
-        self.upvar_borrow_map.borrow().get_copy(&id)
-    }
-}
diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs
index efdc103d14d..2ae07e9a480 100644
--- a/src/librustc/middle/borrowck/move_data.rs
+++ b/src/librustc/middle/borrowck/move_data.rs
@@ -22,6 +22,7 @@ use collections::{HashMap, HashSet};
 use middle::borrowck::*;
 use middle::dataflow::DataFlowContext;
 use middle::dataflow::DataFlowOperator;
+use euv = middle::expr_use_visitor;
 use middle::ty;
 use syntax::ast;
 use syntax::ast_util;
@@ -357,7 +358,7 @@ impl MoveData {
                           assign_id: ast::NodeId,
                           span: Span,
                           assignee_id: ast::NodeId,
-                          is_also_move: bool) {
+                          mode: euv::MutateMode) {
         /*!
          * Adds a new record for an assignment to `lp` that occurs at
          * location `id` with the given `span`.
@@ -368,8 +369,11 @@ impl MoveData {
 
         let path_index = self.move_path(tcx, lp.clone());
 
-        if !is_also_move {
-            self.assignee_ids.borrow_mut().insert(assignee_id);
+        match mode {
+            euv::JustWrite => {
+                self.assignee_ids.borrow_mut().insert(assignee_id);
+            }
+            euv::WriteAndRead => { }
         }
 
         let assignment = Assignment {
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 2ef009e36e4..3e7d1ef6a16 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -15,7 +15,6 @@ use middle::const_eval::{eval_const_expr, const_val, const_bool, const_float};
 use middle::pat_util::*;
 use middle::ty::*;
 use middle::ty;
-use util::nodemap::NodeSet;
 use util::ppaux::ty_to_str;
 
 use std::cmp;
@@ -29,7 +28,6 @@ use syntax::visit::{Visitor, FnKind};
 
 struct MatchCheckCtxt<'a> {
     tcx: &'a ty::ctxt,
-    moves_map: &'a NodeSet
 }
 
 impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
@@ -45,11 +43,9 @@ impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
 }
 
 pub fn check_crate(tcx: &ty::ctxt,
-                   moves_map: &NodeSet,
                    krate: &Crate) {
     let mut cx = MatchCheckCtxt {
         tcx: tcx,
-        moves_map: moves_map
     };
 
     visit::walk_crate(&mut cx, krate, ());
@@ -931,22 +927,18 @@ fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat) -> bool {
 // Legality of move bindings checking
 
 fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
-                                       has_guard: bool,
-                                       pats: &[@Pat]) {
+                                   has_guard: bool,
+                                   pats: &[@Pat]) {
     let tcx = cx.tcx;
     let def_map = &tcx.def_map;
     let mut by_ref_span = None;
-    let mut any_by_move = false;
     for pat in pats.iter() {
-        pat_bindings(def_map, *pat, |bm, id, span, _path| {
+        pat_bindings(def_map, *pat, |bm, _, span, _path| {
             match bm {
                 BindByRef(_) => {
                     by_ref_span = Some(span);
                 }
                 BindByValue(_) => {
-                    if cx.moves_map.contains(&id) {
-                        any_by_move = true;
-                    }
                 }
             }
         })
@@ -975,16 +967,18 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
         }
     };
 
-    if !any_by_move { return; } // pointless micro-optimization
     for pat in pats.iter() {
         walk_pat(*pat, |p| {
             if pat_is_binding(def_map, p) {
                 match p.node {
-                    PatIdent(_, _, sub) => {
-                        if cx.moves_map.contains(&p.id) {
+                    PatIdent(BindByValue(_), _, sub) => {
+                        let pat_ty = ty::node_id_to_type(tcx, p.id);
+                        if ty::type_moves_by_default(tcx, pat_ty) {
                             check_move(p, sub);
                         }
                     }
+                    PatIdent(BindByRef(_), _, _) => {
+                    }
                     _ => {
                         cx.tcx.sess.span_bug(
                             p.span,
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index 6a2262aeaef..56284984a24 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -15,7 +15,7 @@ use middle::astencode;
 
 use middle::ty;
 use middle::typeck::astconv;
-use util::nodemap::{DefIdMap, NodeMap};
+use util::nodemap::{DefIdMap};
 
 use syntax::ast::*;
 use syntax::parse::token::InternedString;
@@ -23,7 +23,6 @@ use syntax::visit::Visitor;
 use syntax::visit;
 use syntax::{ast, ast_map, ast_util};
 
-use std::cell::RefCell;
 use std::rc::Rc;
 
 //
@@ -125,11 +124,8 @@ pub fn lookup_variant_by_id(tcx: &ty::ctxt,
             Some(&e) => return e,
             None => {}
         }
-        let maps = astencode::Maps {
-            capture_map: RefCell::new(NodeMap::new())
-        };
         let e = match csearch::maybe_get_item_ast(tcx, enum_def,
-            |a, b, c, d| astencode::decode_inlined_item(a, b, &maps, c, d)) {
+            |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
             csearch::found(ast::IIItem(item)) => match item.node {
                 ItemEnum(ast::EnumDef { variants: ref variants }, _) => {
                     variant_expr(variants.as_slice(), variant_def.node)
@@ -163,11 +159,8 @@ pub fn lookup_const_by_id(tcx: &ty::ctxt, def_id: ast::DefId)
             Some(&e) => return e,
             None => {}
         }
-        let maps = astencode::Maps {
-            capture_map: RefCell::new(NodeMap::new())
-        };
         let e = match csearch::maybe_get_item_ast(tcx, def_id,
-            |a, b, c, d| astencode::decode_inlined_item(a, b, &maps, c, d)) {
+            |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
             csearch::found(ast::IIItem(item)) => match item.node {
                 ItemStatic(_, ast::MutImmutable, const_expr) => Some(const_expr),
                 _ => None
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
new file mode 100644
index 00000000000..17f423aa592
--- /dev/null
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -0,0 +1,868 @@
+// 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.
+
+/*!
+ * A different sort of visitor for walking fn bodies.  Unlike the
+ * normal visitor, which just walks the entire body in one shot, the
+ * `ExprUseVisitor` determines how expressions are being used.
+ */
+
+use mc = middle::mem_categorization;
+use middle::freevars;
+use middle::pat_util;
+use middle::ty;
+use middle::typeck;
+use syntax::ast;
+use syntax::ast_util;
+use syntax::codemap::{Span};
+use util::ppaux::Repr;
+
+///////////////////////////////////////////////////////////////////////////
+// The Delegate trait
+//
+// This trait defines the callbacks you can expect to receiver when
+// employing the ExprUseVisitor.
+
+#[deriving(Eq)]
+pub enum LoanCause {
+    ClosureCapture(Span),
+    AddrOf,
+    AutoRef,
+    RefBinding,
+    OverloadedOperator,
+    ClosureInvocation
+}
+
+#[deriving(Eq,Show)]
+pub enum ConsumeMode {
+    Copy,    // reference to x where x has a type that copies
+    Move,    // reference to x where x has a type that moves
+}
+
+#[deriving(Eq,Show)]
+pub enum MutateMode {
+    JustWrite,    // x = y
+    WriteAndRead, // x += y
+}
+
+pub trait Delegate {
+    // The value found at `cmt` is either copied or moved, depending
+    // on mode.
+    fn consume(&mut self,
+               _consume_id: ast::NodeId,
+               _consume_span: Span,
+               _cmt: mc::cmt,
+               _mode: ConsumeMode)
+    { }
+
+    // The value found at `cmt` is either copied or moved via the
+    // pattern binding `consume_pat`, depending on mode.
+    fn consume_pat(&mut self,
+                   _consume_pat: &ast::Pat,
+                   _cmt: mc::cmt,
+                   _mode: ConsumeMode)
+    { }
+
+    // The value found at `borrow` is being borrowed at the point
+    // `borrow_id` for the region `loan_region` with kind `bk`.
+    fn borrow(&mut self,
+              _borrow_id: ast::NodeId,
+              _borrow_span: Span,
+              _cmt: mc::cmt,
+              _loan_region: ty::Region,
+              _bk: ty::BorrowKind,
+              _loan_cause: LoanCause)
+    { }
+
+    // The local variable `id` is declared but not initialized.
+    fn decl_without_init(&mut self,
+                         _id: ast::NodeId,
+                         _span: Span)
+    { }
+
+    // The path at `cmt` is being assigned to.
+    fn mutate(&mut self,
+              _assignment_id: ast::NodeId,
+              _assignment_span: Span,
+              _assignee_cmt: mc::cmt,
+              _mode: MutateMode)
+    { }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// The ExprUseVisitor type
+//
+// This is the code that actually walks the tree. Like
+// mem_categorization, it requires a TYPER, which is a type that
+// supplies types from the tree. After type checking is complete, you
+// can just use the tcx as the typer.
+
+pub struct ExprUseVisitor<'d,'t,D,TYPER> {
+    typer: &'t TYPER,
+    mc: mc::MemCategorizationContext<'t,TYPER>,
+    delegate: &'d mut D,
+}
+
+// If the TYPER results in an error, it's because the type check
+// failed (or will fail, when the error is uncovered and reported
+// during writeback). In this case, we just ignore this part of the
+// code.
+//
+// Note that this macro appears similar to try!(), but, unlike try!(),
+// it does not propagate the error.
+macro_rules! ignore_err(
+    ($inp: expr) => (
+        match $inp {
+            Ok(v) => v,
+            Err(()) => return
+        }
+    )
+)
+
+impl<'d,'t,D:Delegate,TYPER:mc::Typer> ExprUseVisitor<'d,'t,D,TYPER> {
+    pub fn new(delegate: &'d mut D,
+               typer: &'t TYPER)
+               -> ExprUseVisitor<'d,'t,D,TYPER> {
+        ExprUseVisitor { typer: typer,
+                         mc: mc::MemCategorizationContext::new(typer),
+                         delegate: delegate }
+    }
+
+    pub fn walk_fn(&mut self,
+                   decl: &ast::FnDecl,
+                   body: &ast::Block) {
+        self.walk_arg_patterns(decl, body);
+        self.walk_block(body);
+    }
+
+    fn walk_arg_patterns(&mut self,
+                         decl: &ast::FnDecl,
+                         body: &ast::Block) {
+        for arg in decl.inputs.iter() {
+            let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
+
+            let arg_cmt = self.mc.cat_rvalue(
+                arg.id,
+                arg.pat.span,
+                ty::ReScope(body.id), // Args live only as long as the fn body.
+                arg_ty);
+
+            self.walk_pat(arg_cmt, arg.pat);
+        }
+    }
+
+    fn tcx<'a>(&'a self) -> &'a ty::ctxt {
+        self.typer.tcx()
+    }
+
+    fn delegate_consume(&mut self,
+                        consume_id: ast::NodeId,
+                        consume_span: Span,
+                        cmt: mc::cmt) {
+        let mode = copy_or_move(self.tcx(), cmt.ty);
+        self.delegate.consume(consume_id, consume_span, cmt, mode);
+    }
+
+    fn consume_exprs(&mut self, exprs: &Vec<@ast::Expr>) {
+        for &expr in exprs.iter() {
+            self.consume_expr(expr);
+        }
+    }
+
+    fn consume_expr(&mut self, expr: &ast::Expr) {
+        debug!("consume_expr(expr={})", expr.repr(self.tcx()));
+
+        let cmt = ignore_err!(self.mc.cat_expr(expr));
+        self.delegate_consume(expr.id, expr.span, cmt);
+
+        match expr.node {
+            ast::ExprParen(subexpr) => {
+                // Argh but is ExprParen horrible. So, if we consume
+                // `(x)`, that generally is also consuming `x`, UNLESS
+                // there are adjustments on the `(x)` expression
+                // (e.g., autoderefs and autorefs).
+                if self.typer.adjustments().borrow().contains_key(&expr.id) {
+                    self.walk_expr(expr);
+                } else {
+                    self.consume_expr(subexpr);
+                }
+            }
+
+            _ => {
+                self.walk_expr(expr)
+            }
+        }
+    }
+
+    fn mutate_expr(&mut self,
+                   assignment_expr: &ast::Expr,
+                   expr: &ast::Expr,
+                   mode: MutateMode) {
+        let cmt = ignore_err!(self.mc.cat_expr(expr));
+        self.delegate.mutate(assignment_expr.id, assignment_expr.span, cmt, mode);
+        self.walk_expr(expr);
+    }
+
+    fn borrow_expr(&mut self,
+                   expr: &ast::Expr,
+                   r: ty::Region,
+                   bk: ty::BorrowKind,
+                   cause: LoanCause) {
+        debug!("borrow_expr(expr={}, r={}, bk={})",
+               expr.repr(self.tcx()), r.repr(self.tcx()), bk.repr(self.tcx()));
+
+        let cmt = ignore_err!(self.mc.cat_expr(expr));
+        self.delegate.borrow(expr.id, expr.span, cmt, r, bk, cause);
+
+        // Note: Unlike consume, we can ignore ExprParen. cat_expr
+        // already skips over them, and walk will uncover any
+        // attachments or whatever.
+        self.walk_expr(expr)
+    }
+
+    fn select_from_expr(&mut self, expr: &ast::Expr) {
+        self.walk_expr(expr)
+    }
+
+    fn walk_expr(&mut self, expr: &ast::Expr) {
+        debug!("walk_expr(expr={})", expr.repr(self.tcx()));
+
+        self.walk_adjustment(expr);
+
+        match expr.node {
+            ast::ExprParen(subexpr) => {
+                self.walk_expr(subexpr)
+            }
+
+            ast::ExprPath(..) => { }
+
+            ast::ExprUnary(ast::UnDeref, base) => {      // *base
+                if !self.walk_overloaded_operator(expr, base, []) {
+                    self.select_from_expr(base);
+                }
+            }
+
+            ast::ExprField(base, _, _) => {         // base.f
+                self.select_from_expr(base);
+            }
+
+            ast::ExprIndex(lhs, rhs) => {           // lhs[rhs]
+                if !self.walk_overloaded_operator(expr, lhs, [rhs]) {
+                    self.select_from_expr(lhs);
+                    self.consume_expr(rhs);
+                }
+            }
+
+            ast::ExprCall(callee, ref args) => {    // callee(args)
+                self.walk_callee(expr, callee);
+                self.consume_exprs(args);
+            }
+
+            ast::ExprMethodCall(_, _, ref args) => { // callee.m(args)
+                self.consume_exprs(args);
+            }
+
+            ast::ExprStruct(_, ref fields, opt_with) => {
+                self.walk_struct_expr(expr, fields, opt_with);
+            }
+
+            ast::ExprTup(ref exprs) => {
+                self.consume_exprs(exprs);
+            }
+
+            ast::ExprIf(cond_expr, then_blk, opt_else_expr) => {
+                self.consume_expr(cond_expr);
+                self.walk_block(then_blk);
+                for else_expr in opt_else_expr.iter() {
+                    self.consume_expr(*else_expr);
+                }
+            }
+
+            ast::ExprMatch(discr, ref arms) => {
+                // treatment of the discriminant is handled while
+                // walking the arms:
+                self.walk_expr(discr);
+                let discr_cmt = ignore_err!(self.mc.cat_expr(discr));
+                for arm in arms.iter() {
+                    self.walk_arm(discr_cmt.clone(), arm);
+                }
+            }
+
+            ast::ExprVec(ref exprs) => {
+                self.consume_exprs(exprs);
+            }
+
+            ast::ExprAddrOf(m, base) => {   // &base
+                // make sure that the thing we are pointing out stays valid
+                // for the lifetime `scope_r` of the resulting ptr:
+                let expr_ty = ty::expr_ty(self.tcx(), expr);
+                if !ty::type_is_bot(expr_ty) {
+                    let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
+                    let bk = ty::BorrowKind::from_mutbl(m);
+                    self.borrow_expr(base, r, bk, AddrOf);
+                } else {
+                    self.walk_expr(base);
+                }
+            }
+
+            ast::ExprInlineAsm(ref ia) => {
+                for &(_, input) in ia.inputs.iter() {
+                    self.consume_expr(input);
+                }
+
+                for &(_, output) in ia.outputs.iter() {
+                    self.mutate_expr(expr, output, JustWrite);
+                }
+            }
+
+            ast::ExprBreak(..) |
+            ast::ExprAgain(..) |
+            ast::ExprLit(..) => {}
+
+            ast::ExprLoop(blk, _) => {
+                self.walk_block(blk);
+            }
+
+            ast::ExprWhile(cond_expr, blk) => {
+                self.consume_expr(cond_expr);
+                self.walk_block(blk);
+            }
+
+            ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop"),
+
+            ast::ExprUnary(_, lhs) => {
+                if !self.walk_overloaded_operator(expr, lhs, []) {
+                    self.consume_expr(lhs);
+                }
+            }
+
+            ast::ExprBinary(_, lhs, rhs) => {
+                if !self.walk_overloaded_operator(expr, lhs, [rhs]) {
+                    self.consume_expr(lhs);
+                    self.consume_expr(rhs);
+                }
+            }
+
+            ast::ExprBlock(blk) => {
+                self.walk_block(blk);
+            }
+
+            ast::ExprRet(ref opt_expr) => {
+                for expr in opt_expr.iter() {
+                    self.consume_expr(*expr);
+                }
+            }
+
+            ast::ExprAssign(lhs, rhs) => {
+                self.mutate_expr(expr, lhs, JustWrite);
+                self.consume_expr(rhs);
+            }
+
+            ast::ExprCast(base, _) => {
+                self.consume_expr(base);
+            }
+
+            ast::ExprAssignOp(_, lhs, rhs) => {
+                // FIXME(#4712) --- Overloaded operators?
+                //
+                // if !self.walk_overloaded_operator(expr, [lhs, rhs])
+                // {
+                self.mutate_expr(expr, lhs, WriteAndRead);
+                self.consume_expr(rhs);
+                // }
+            }
+
+            ast::ExprRepeat(base, count) => {
+                self.consume_expr(base);
+                self.consume_expr(count);
+            }
+
+            ast::ExprFnBlock(..) |
+            ast::ExprProc(..) => {
+                self.walk_captures(expr)
+            }
+
+            ast::ExprVstore(base, _) => {
+                self.consume_expr(base);
+            }
+
+            ast::ExprBox(place, base) => {
+                self.consume_expr(place);
+                self.consume_expr(base);
+            }
+
+            ast::ExprMac(..) => {
+                self.tcx().sess.span_bug(
+                    expr.span,
+                    "macro expression remains after expansion");
+            }
+        }
+    }
+
+    fn walk_callee(&mut self, call: &ast::Expr, callee: &ast::Expr) {
+        let callee_ty = ty::expr_ty_adjusted(self.tcx(), callee);
+        debug!("walk_callee: callee={} callee_ty={}",
+               callee.repr(self.tcx()), callee_ty.repr(self.tcx()));
+        match ty::get(callee_ty).sty {
+            ty::ty_bare_fn(..) => {
+                self.consume_expr(callee);
+            }
+            ty::ty_closure(ref f) => {
+                match f.onceness {
+                    ast::Many => {
+                        self.borrow_expr(callee,
+                                         ty::ReScope(call.id),
+                                         ty::UniqueImmBorrow,
+                                         ClosureInvocation);
+                    }
+                    ast::Once => {
+                        self.consume_expr(callee);
+                    }
+                }
+            }
+            _ => {
+                self.tcx().sess.span_bug(
+                    callee.span,
+                    format!("unxpected callee type {}",
+                            callee_ty.repr(self.tcx())));
+            }
+        }
+    }
+
+    fn walk_stmt(&mut self, stmt: &ast::Stmt) {
+        match stmt.node {
+            ast::StmtDecl(decl, _) => {
+                match decl.node {
+                    ast::DeclLocal(local) => {
+                        self.walk_local(local);
+                    }
+
+                    ast::DeclItem(_) => {
+                        // we don't visit nested items in this visitor,
+                        // only the fn body we were given.
+                    }
+                }
+            }
+
+            ast::StmtExpr(expr, _) |
+            ast::StmtSemi(expr, _) => {
+                self.consume_expr(expr);
+            }
+
+            ast::StmtMac(..) => {
+                self.tcx().sess.span_bug(
+                    stmt.span,
+                    format!("unexpanded stmt macro"));
+            }
+        }
+    }
+
+    fn walk_local(&mut self, local: @ast::Local) {
+        match local.init {
+            None => {
+                let delegate = &mut self.delegate;
+                pat_util::pat_bindings(&self.typer.tcx().def_map, local.pat, |_, id, span, _| {
+                    delegate.decl_without_init(id, span);
+                })
+            }
+
+            Some(expr) => {
+                // Variable declarations with
+                // initializers are considered
+                // "assigns", which is handled by
+                // `walk_pat`:
+                self.walk_expr(expr);
+                let init_cmt = ignore_err!(self.mc.cat_expr(expr));
+                self.walk_pat(init_cmt, local.pat);
+            }
+        }
+    }
+
+    fn walk_block(&mut self, blk: &ast::Block) {
+        /*!
+         * Indicates that the value of `blk` will be consumed,
+         * meaning either copied or moved depending on its type.
+         */
+
+        debug!("walk_block(blk.id={:?})", blk.id);
+
+        for stmt in blk.stmts.iter() {
+            self.walk_stmt(*stmt);
+        }
+
+        for tail_expr in blk.expr.iter() {
+            self.consume_expr(*tail_expr);
+        }
+    }
+
+    fn walk_struct_expr(&mut self,
+                        _expr: &ast::Expr,
+                        fields: &Vec<ast::Field>,
+                        opt_with: Option<@ast::Expr>) {
+        // Consume the expressions supplying values for each field.
+        for field in fields.iter() {
+            self.consume_expr(field.expr);
+        }
+
+        let with_expr = match opt_with {
+            Some(w) => { w }
+            None => { return; }
+        };
+
+        let with_cmt = ignore_err!(self.mc.cat_expr(with_expr));
+
+        // Select just those fields of the `with`
+        // expression that will actually be used
+        let with_fields = match ty::get(with_cmt.ty).sty {
+            ty::ty_struct(did, ref substs) => {
+                ty::struct_fields(self.tcx(), did, substs)
+            }
+            _ => {
+                self.tcx().sess.span_bug(
+                    with_expr.span,
+                    format!("with expression doesn't evaluate to a struct"));
+            }
+        };
+
+        // Consume those fields of the with expression that are needed.
+        for with_field in with_fields.iter() {
+            if !contains_field_named(with_field, fields) {
+                let cmt_field = self.mc.cat_field(with_expr,
+                                                  with_cmt.clone(),
+                                                  with_field.ident,
+                                                  with_field.mt.ty);
+                self.delegate_consume(with_expr.id, with_expr.span, cmt_field);
+            }
+        }
+
+        fn contains_field_named(field: &ty::field,
+                                fields: &Vec<ast::Field>)
+                                -> bool
+        {
+            fields.iter().any(
+                |f| f.ident.node.name == field.ident.name)
+        }
+    }
+
+    // Invoke the appropriate delegate calls for anything that gets
+    // consumed or borrowed as part of the automatic adjustment
+    // process.
+    fn walk_adjustment(&mut self, expr: &ast::Expr) {
+        let typer = self.typer;
+        match typer.adjustments().borrow().find(&expr.id) {
+            None => { }
+            Some(adjustment) => {
+                match *adjustment {
+                    ty::AutoAddEnv(..) |
+                    ty::AutoObject(..) => {
+                        // Creating an object or closure consumes the
+                        // input and stores it into the resulting rvalue.
+                        debug!("walk_adjustment(AutoAddEnv|AutoObject)");
+                        let cmt_unadjusted =
+                            ignore_err!(self.mc.cat_expr_unadjusted(expr));
+                        self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
+                    }
+                    ty::AutoDerefRef(ty::AutoDerefRef {
+                        autoref: ref opt_autoref,
+                        autoderefs: n
+                    }) => {
+                        self.walk_autoderefs(expr, n);
+
+                        match *opt_autoref {
+                            None => { }
+                            Some(ref r) => {
+                                self.walk_autoref(expr, r, n);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fn walk_autoderefs(&mut self,
+                       expr: &ast::Expr,
+                       autoderefs: uint) {
+        /*!
+         * Autoderefs for overloaded Deref calls in fact reference
+         * their receiver. That is, if we have `(*x)` where `x` is of
+         * type `Rc<T>`, then this in fact is equivalent to
+         * `x.deref()`. Since `deref()` is declared with `&self`, this
+         * is an autoref of `x`.
+         */
+        debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
+
+        for i in range(0, autoderefs) {
+            let deref_id = typeck::MethodCall::autoderef(expr.id, i as u32);
+            match self.typer.node_method_ty(deref_id) {
+                None => {}
+                Some(method_ty) => {
+                    let cmt = ignore_err!(self.mc.cat_expr_autoderefd(expr, i));
+                    let self_ty = *ty::ty_fn_args(method_ty).get(0);
+                    let (m, r) = match ty::get(self_ty).sty {
+                        ty::ty_rptr(r, ref m) => (m.mutbl, r),
+                        _ => self.tcx().sess.span_bug(expr.span,
+                                format!("bad overloaded deref type {}",
+                                    method_ty.repr(self.tcx())))
+                    };
+                    let bk = ty::BorrowKind::from_mutbl(m);
+                    self.delegate.borrow(expr.id, expr.span, cmt,
+                                         r, bk, AutoRef);
+                }
+            }
+        }
+    }
+
+    fn walk_autoref(&mut self,
+                    expr: &ast::Expr,
+                    autoref: &ty::AutoRef,
+                    autoderefs: uint) {
+        debug!("walk_autoref expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
+
+        let cmt_derefd = ignore_err!(
+            self.mc.cat_expr_autoderefd(expr, autoderefs));
+
+        debug!("walk_autoref: cmt_derefd={}", cmt_derefd.repr(self.tcx()));
+
+        match *autoref {
+            ty::AutoPtr(r, m) => {
+                self.delegate.borrow(expr.id,
+                                     expr.span,
+                                     cmt_derefd,
+                                     r,
+                                     ty::BorrowKind::from_mutbl(m),
+                                     AutoRef)
+            }
+            ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
+                let cmt_index = self.mc.cat_index(expr, cmt_derefd, autoderefs+1);
+                self.delegate.borrow(expr.id,
+                                     expr.span,
+                                     cmt_index,
+                                     r,
+                                     ty::BorrowKind::from_mutbl(m),
+                                     AutoRef)
+            }
+            ty::AutoBorrowObj(r, m) => {
+                let cmt_deref = self.mc.cat_deref_obj(expr, cmt_derefd);
+                self.delegate.borrow(expr.id,
+                                     expr.span,
+                                     cmt_deref,
+                                     r,
+                                     ty::BorrowKind::from_mutbl(m),
+                                     AutoRef)
+            }
+            ty::AutoUnsafe(_) => {}
+        }
+    }
+
+    fn walk_overloaded_operator(&mut self,
+                                expr: &ast::Expr,
+                                receiver: &ast::Expr,
+                                args: &[@ast::Expr])
+                                -> bool
+    {
+        if !self.typer.is_method_call(expr.id) {
+            return false;
+        }
+
+        self.walk_expr(receiver);
+
+        // Arguments (but not receivers) to overloaded operator
+        // methods are implicitly autoref'd which sadly does not use
+        // adjustments, so we must hardcode the borrow here.
+
+        let r = ty::ReScope(expr.id);
+        let bk = ty::ImmBorrow;
+
+        for &arg in args.iter() {
+            self.borrow_expr(arg, r, bk, OverloadedOperator);
+        }
+        return true;
+    }
+
+    fn walk_arm(&mut self, discr_cmt: mc::cmt, arm: &ast::Arm) {
+        for &pat in arm.pats.iter() {
+            self.walk_pat(discr_cmt.clone(), pat);
+        }
+
+        for guard in arm.guard.iter() {
+            self.consume_expr(*guard);
+        }
+
+        self.consume_expr(arm.body);
+    }
+
+    fn walk_pat(&mut self, cmt_discr: mc::cmt, pat: @ast::Pat) {
+        debug!("walk_pat cmt_discr={} pat={}", cmt_discr.repr(self.tcx()),
+               pat.repr(self.tcx()));
+        let mc = &self.mc;
+        let typer = self.typer;
+        let tcx = typer.tcx();
+        let def_map = &self.typer.tcx().def_map;
+        let delegate = &mut self.delegate;
+        ignore_err!(mc.cat_pattern(cmt_discr, pat, |mc, cmt_pat, pat| {
+            if pat_util::pat_is_binding(def_map, pat) {
+                let tcx = typer.tcx();
+
+                debug!("binding cmt_pat={} pat={}",
+                       cmt_pat.repr(tcx),
+                       pat.repr(tcx));
+
+                // pat_ty: the type of the binding being produced.
+                let pat_ty = ty::node_id_to_type(tcx, pat.id);
+
+                // Each match binding is effectively an assignment to the
+                // binding being produced.
+                let def = def_map.borrow().get_copy(&pat.id);
+                match mc.cat_def(pat.id, pat.span, pat_ty, def) {
+                    Ok(binding_cmt) => {
+                        delegate.mutate(pat.id, pat.span, binding_cmt, JustWrite);
+                    }
+                    Err(_) => { }
+                }
+
+                // It is also a borrow or copy/move of the value being matched.
+                match pat.node {
+                    ast::PatIdent(ast::BindByRef(m), _, _) => {
+                        let (r, bk) = {
+                            (ty::ty_region(tcx, pat.span, pat_ty),
+                             ty::BorrowKind::from_mutbl(m))
+                        };
+                        delegate.borrow(pat.id, pat.span, cmt_pat,
+                                             r, bk, RefBinding);
+                    }
+                    ast::PatIdent(ast::BindByValue(_), _, _) => {
+                        let mode = copy_or_move(typer.tcx(), cmt_pat.ty);
+                        delegate.consume_pat(pat, cmt_pat, mode);
+                    }
+                    _ => {
+                        typer.tcx().sess.span_bug(
+                            pat.span,
+                            "binding pattern not an identifier");
+                    }
+                }
+            } else {
+                match pat.node {
+                    ast::PatVec(_, Some(slice_pat), _) => {
+                        // The `slice_pat` here creates a slice into
+                        // the original vector.  This is effectively a
+                        // borrow of the elements of the vector being
+                        // matched.
+
+                        let (slice_cmt, slice_mutbl, slice_r) = {
+                            match mc.cat_slice_pattern(cmt_pat, slice_pat) {
+                                Ok(v) => v,
+                                Err(()) => {
+                                    tcx.sess.span_bug(slice_pat.span,
+                                                      "Err from mc")
+                                }
+                            }
+                        };
+
+                        // Note: We declare here that the borrow
+                        // occurs upon entering the `[...]`
+                        // pattern. This implies that something like
+                        // `[a, ..b]` where `a` is a move is illegal,
+                        // because the borrow is already in effect.
+                        // In fact such a move would be safe-ish, but
+                        // it effectively *requires* that we use the
+                        // nulling out semantics to indicate when a
+                        // value has been moved, which we are trying
+                        // to move away from.  Otherwise, how can we
+                        // indicate that the first element in the
+                        // vector has been moved?  Eventually, we
+                        // could perhaps modify this rule to permit
+                        // `[..a, b]` where `b` is a move, because in
+                        // that case we can adjust the length of the
+                        // original vec accordingly, but we'd have to
+                        // make trans do the right thing, and it would
+                        // only work for `~` vectors. It seems simpler
+                        // to just require that people call
+                        // `vec.pop()` or `vec.unshift()`.
+                        let slice_bk = ty::BorrowKind::from_mutbl(slice_mutbl);
+                        delegate.borrow(pat.id, pat.span,
+                                        slice_cmt, slice_r,
+                                        slice_bk, RefBinding);
+                    }
+                    _ => { }
+                }
+            }
+        }));
+    }
+
+    fn walk_captures(&mut self, closure_expr: &ast::Expr) {
+        debug!("walk_captures({})", closure_expr.repr(self.tcx()));
+
+        let tcx = self.typer.tcx();
+        freevars::with_freevars(tcx, closure_expr.id, |freevars| {
+            match freevars::get_capture_mode(self.tcx(), closure_expr.id) {
+                freevars::CaptureByRef => {
+                    self.walk_by_ref_captures(closure_expr, freevars);
+                }
+                freevars::CaptureByValue => {
+                    self.walk_by_value_captures(closure_expr, freevars);
+                }
+            }
+        });
+    }
+
+    fn walk_by_ref_captures(&mut self,
+                            closure_expr: &ast::Expr,
+                            freevars: &[freevars::freevar_entry]) {
+        for freevar in freevars.iter() {
+            let id_var = ast_util::def_id_of_def(freevar.def).node;
+            let cmt_var = ignore_err!(self.cat_captured_var(closure_expr.id,
+                                                            closure_expr.span,
+                                                            freevar.def));
+
+            // Lookup the kind of borrow the callee requires, as
+            // inferred by regionbk
+            let upvar_id = ty::UpvarId { var_id: id_var,
+                                         closure_expr_id: closure_expr.id };
+            let upvar_borrow = self.tcx().upvar_borrow_map.borrow()
+                                   .get_copy(&upvar_id);
+
+            self.delegate.borrow(closure_expr.id,
+                                 closure_expr.span,
+                                 cmt_var,
+                                 upvar_borrow.region,
+                                 upvar_borrow.kind,
+                                 ClosureCapture(freevar.span));
+        }
+    }
+
+    fn walk_by_value_captures(&mut self,
+                              closure_expr: &ast::Expr,
+                              freevars: &[freevars::freevar_entry]) {
+        for freevar in freevars.iter() {
+            let cmt_var = ignore_err!(self.cat_captured_var(closure_expr.id,
+                                                            closure_expr.span,
+                                                            freevar.def));
+            self.delegate_consume(closure_expr.id, freevar.span, cmt_var);
+        }
+    }
+
+    fn cat_captured_var(&mut self,
+                        closure_id: ast::NodeId,
+                        closure_span: Span,
+                        upvar_def: ast::Def)
+                        -> mc::McResult<mc::cmt> {
+        // Create the cmt for the variable being borrowed, from the
+        // caller's perspective
+        let var_id = ast_util::def_id_of_def(upvar_def).node;
+        let var_ty = ty::node_id_to_type(self.tcx(), var_id);
+        self.mc.cat_def(closure_id, closure_span, var_ty, upvar_def)
+    }
+}
+
+fn copy_or_move(tcx: &ty::ctxt, ty: ty::t) -> ConsumeMode {
+    if ty::type_moves_by_default(tcx, ty) { Move } else { Copy }
+}
+
diff --git a/src/librustc/middle/freevars.rs b/src/librustc/middle/freevars.rs
index 83f6d5abfa4..8839164434a 100644
--- a/src/librustc/middle/freevars.rs
+++ b/src/librustc/middle/freevars.rs
@@ -22,6 +22,15 @@ use syntax::{ast, ast_util};
 use syntax::visit;
 use syntax::visit::Visitor;
 
+#[deriving(Show)]
+pub enum CaptureMode {
+    /// Copy/move the value from this llvm ValueRef into the environment.
+    CaptureByValue,
+
+    /// Access by reference (used for stack closures).
+    CaptureByRef
+}
+
 // A vector of defs representing the free variables referred to in a function.
 // (The def_upvar will already have been stripped).
 #[deriving(Encodable, Decodable)]
@@ -38,7 +47,6 @@ struct CollectFreevarsVisitor<'a> {
 }
 
 impl<'a> Visitor<int> for CollectFreevarsVisitor<'a> {
-
     fn visit_item(&mut self, _: &ast::Item, _: int) {
         // ignore_item
     }
@@ -133,3 +141,14 @@ pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[freevar_entry]|
         Some(d) => f(d.as_slice())
     }
 }
+
+pub fn get_capture_mode(tcx: &ty::ctxt,
+                        closure_expr_id: ast::NodeId)
+                        -> CaptureMode
+{
+    let fn_ty = ty::node_id_to_type(tcx, closure_expr_id);
+    match ty::ty_closure_store(fn_ty) {
+        ty::RegionTraitStore(..) => CaptureByRef,
+        ty::UniqTraitStore => CaptureByValue
+    }
+}
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index 02f9309bca2..92695d7a1d6 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -228,11 +228,11 @@ fn check_fn(
 
     // Check kinds on free variables:
     with_appropriate_checker(cx, fn_id, |chk| {
-        freevars::with_freevars(cx.tcx, fn_id, |r| {
-            for fv in r.iter() {
+        freevars::with_freevars(cx.tcx, fn_id, |freevars| {
+            for fv in freevars.iter() {
                 chk(cx, fv);
             }
-        })
+        });
     });
 
     visit::walk_fn(cx, fk, decl, body, sp, fn_id, ());
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 4be9992367d..43ffe3b2d9d 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -103,10 +103,10 @@
  */
 
 
+use middle::freevars;
 use middle::lint::{UnusedVariable, DeadAssignment};
 use middle::pat_util;
 use middle::ty;
-use middle::moves;
 use util::nodemap::NodeMap;
 
 use std::cast::transmute;
@@ -170,9 +170,8 @@ impl<'a> Visitor<()> for IrMaps<'a> {
 }
 
 pub fn check_crate(tcx: &ty::ctxt,
-                   capture_map: &moves::CaptureMap,
                    krate: &Crate) {
-    visit::walk_crate(&mut IrMaps(tcx, capture_map), krate, ());
+    visit::walk_crate(&mut IrMaps(tcx), krate, ());
     tcx.sess.abort_if_errors();
 }
 
@@ -245,7 +244,6 @@ enum VarKind {
 
 struct IrMaps<'a> {
     tcx: &'a ty::ctxt,
-    capture_map: &'a moves::CaptureMap,
 
     num_live_nodes: uint,
     num_vars: uint,
@@ -256,12 +254,10 @@ struct IrMaps<'a> {
     lnks: Vec<LiveNodeKind>,
 }
 
-fn IrMaps<'a>(tcx: &'a ty::ctxt,
-              capture_map: &'a moves::CaptureMap)
+fn IrMaps<'a>(tcx: &'a ty::ctxt)
               -> IrMaps<'a> {
     IrMaps {
         tcx: tcx,
-        capture_map: capture_map,
         num_live_nodes: 0,
         num_vars: 0,
         live_node_map: NodeMap::new(),
@@ -361,7 +357,7 @@ fn visit_fn(ir: &mut IrMaps,
     let _i = ::util::common::indenter();
 
     // swap in a new set of IR maps for this function body:
-    let mut fn_maps = IrMaps(ir.tcx, ir.capture_map);
+    let mut fn_maps = IrMaps(ir.tcx);
 
     unsafe {
         debug!("creating fn_maps: {}", transmute::<&IrMaps, *IrMaps>(&fn_maps));
@@ -446,13 +442,23 @@ fn visit_arm(ir: &mut IrMaps, arm: &Arm) {
     visit::walk_arm(ir, arm, ());
 }
 
+fn moved_variable_node_id_from_def(def: Def) -> Option<NodeId> {
+    match def {
+        DefBinding(nid, _) |
+        DefArg(nid, _) |
+        DefLocal(nid, _) => Some(nid),
+
+      _ => None
+    }
+}
+
 fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
     match expr.node {
       // live nodes required for uses or definitions of variables:
       ExprPath(_) => {
         let def = ir.tcx.def_map.borrow().get_copy(&expr.id);
         debug!("expr {}: path that leads to {:?}", expr.id, def);
-        if moves::moved_variable_node_id_from_def(def).is_some() {
+        if moved_variable_node_id_from_def(def).is_some() {
             ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
         }
         visit::walk_expr(ir, expr, ());
@@ -467,24 +473,33 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
         // in better error messages than just pointing at the closure
         // construction site.
         let mut call_caps = Vec::new();
-        for cv in ir.capture_map.get(&expr.id).iter() {
-            match moves::moved_variable_node_id_from_def(cv.def) {
-              Some(rv) => {
-                let cv_ln = ir.add_live_node(FreeVarNode(cv.span));
-                let is_move = match cv.mode {
-                    // var must be dead afterwards
-                    moves::CapMove => true,
+        let fv_mode = freevars::get_capture_mode(ir.tcx, expr.id);
+        freevars::with_freevars(ir.tcx, expr.id, |freevars| {
+            for fv in freevars.iter() {
+                match moved_variable_node_id_from_def(fv.def) {
+                    Some(rv) => {
+                        let fv_ln = ir.add_live_node(FreeVarNode(fv.span));
+                        let fv_id = ast_util::def_id_of_def(fv.def).node;
+                        let fv_ty = ty::node_id_to_type(ir.tcx, fv_id);
+                        let is_move = match fv_mode {
+                            // var must be dead afterwards
+                            freevars::CaptureByValue => {
+                                ty::type_moves_by_default(ir.tcx, fv_ty)
+                            }
 
-                    // var can still be used
-                    moves::CapCopy | moves::CapRef => false
-                };
-                call_caps.push(CaptureInfo {ln: cv_ln,
-                                            is_move: is_move,
-                                            var_nid: rv});
-              }
-              None => {}
+                            // var can still be used
+                            freevars::CaptureByRef => {
+                                false
+                            }
+                        };
+                        call_caps.push(CaptureInfo {ln: fv_ln,
+                                                    is_move: is_move,
+                                                    var_nid: rv});
+                    }
+                    None => {}
+                }
             }
-        }
+        });
         ir.set_captures(expr.id, call_caps);
 
         visit::walk_expr(ir, expr, ());
@@ -1270,7 +1285,7 @@ impl<'a> Liveness<'a> {
     fn access_path(&mut self, expr: &Expr, succ: LiveNode, acc: uint)
                    -> LiveNode {
         let def = self.ir.tcx.def_map.borrow().get_copy(&expr.id);
-        match moves::moved_variable_node_id_from_def(def) {
+        match moved_variable_node_id_from_def(def) {
           Some(nid) => {
             let ln = self.live_node(expr.id, expr.span);
             if acc != 0u {
@@ -1497,7 +1512,7 @@ impl<'a> Liveness<'a> {
                 self.warn_about_dead_assign(expr.span, expr.id, ln, var);
               }
               def => {
-                match moves::moved_variable_node_id_from_def(def) {
+                match moved_variable_node_id_from_def(def) {
                   Some(nid) => {
                     let ln = self.live_node(expr.id, expr.span);
                     let var = self.variable(nid, expr.span);
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 35124aa9916..57f6e9632b1 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -241,8 +241,8 @@ impl ast_node for ast::Pat {
     fn span(&self) -> Span { self.span }
 }
 
-pub struct MemCategorizationContext<TYPER> {
-    pub typer: TYPER
+pub struct MemCategorizationContext<'t,TYPER> {
+    typer: &'t TYPER
 }
 
 pub type McResult<T> = Result<T, ()>;
@@ -349,8 +349,12 @@ macro_rules! if_ok(
     )
 )
 
-impl<TYPER:Typer> MemCategorizationContext<TYPER> {
-    fn tcx<'a>(&'a self) -> &'a ty::ctxt {
+impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
+    pub fn new(typer: &'t TYPER) -> MemCategorizationContext<'t,TYPER> {
+        MemCategorizationContext { typer: typer }
+    }
+
+    fn tcx(&self) -> &'t ty::ctxt {
         self.typer.tcx()
     }
 
@@ -418,7 +422,9 @@ impl<TYPER:Typer> MemCategorizationContext<TYPER> {
         }
     }
 
-    pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, autoderefs: uint)
+    pub fn cat_expr_autoderefd(&self,
+                               expr: &ast::Expr,
+                               autoderefs: uint)
                                -> McResult<cmt> {
         let mut cmt = if_ok!(self.cat_expr_unadjusted(expr));
         for deref in range(1u, autoderefs + 1) {
@@ -456,7 +462,9 @@ impl<TYPER:Typer> MemCategorizationContext<TYPER> {
             self.cat_def(expr.id, expr.span, expr_ty, def)
           }
 
-          ast::ExprParen(e) => self.cat_expr_unadjusted(e),
+          ast::ExprParen(e) => {
+            self.cat_expr(e)
+          }
 
           ast::ExprAddrOf(..) | ast::ExprCall(..) |
           ast::ExprAssign(..) | ast::ExprAssignOp(..) |
diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs
deleted file mode 100644
index 1e44e1ab7f9..00000000000
--- a/src/librustc/middle/moves.rs
+++ /dev/null
@@ -1,655 +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.
-
-/*!
-
-# Moves Computation
-
-The goal of this file is to compute which
-expressions/patterns/captures correspond to *moves*.  This is
-generally a function of the context in which the expression appears as
-well as the expression's type.
-
-## Examples
-
-We will use the following fragment of code to explain the various
-considerations.  Note that in this code `x` is used after it has been
-moved here.  This is not relevant to this pass, though the information
-we compute would later be used to detect this error (see the section
-Enforcement of Moves, below).
-
-    struct Foo { a: int, b: ~int }
-    let x: Foo = ...;
-    let w = (x {Read}).a;      // Read
-    let y = (x {Move}).b;      // Move
-    let z = copy (x {Read}).b; // Read
-
-Let's look at these examples one by one.  In the first case, `w`, the
-expression being assigned is `x.a`, which has `int` type.  In that
-case, the value is read, and the container (`x`) is also read.
-
-In the second case, `y`, `x.b` is being assigned which has type
-`~int`.  Because this type moves by default, that will be a move
-reference.  Whenever we move from a compound expression like `x.b` (or
-`x[b]` or `*x` or `{x}[b].c`, etc), this invalidates all containing
-expressions since we do not currently permit "incomplete" variables
-where part of them has been moved and part has not.  In this case,
-this means that the reference to `x` is also a move.  We'll see later,
-though, that these kind of "partial moves", where part of the
-expression has been moved, are classified and stored somewhat
-differently.
-
-The final example (`z`) is `copy x.b`: in this case, although the
-expression being assigned has type `~int`, there are no moves
-involved.
-
-### Patterns
-
-For each binding in a match or let pattern, we also compute a read
-or move designation.  A move binding means that the value will be
-moved from the value being matched.  As a result, the expression
-being matched (aka, the 'discriminant') is either moved or read
-depending on whether the bindings move the value they bind to out of
-the discriminant.
-
-For examples, consider this match expression:
-
-    match x {Move} {
-      Foo { a: a {Read}, b: b {Move} } => {...}
-    }
-
-Here, the binding `b` is value (not ref) mode, and `b` has type
-`~int`, and therefore the discriminant expression `x` would be
-incomplete so it also considered moved.
-
-In the following two examples, in contrast, the mode of `b` is either
-`copy` or `ref` and hence the overall result is a read:
-
-    match x {Read} {
-      Foo { a: a {Read}, b: copy b {Read} } => {...}
-    }
-
-    match x {Read} {
-      Foo { a: a {Read}, b: ref b {Read} } => {...}
-    }
-
-Similar reasoning can be applied to `let` expressions:
-
-    let Foo { a: a {Read}, b: b {Move} } = x {Move};
-    let Foo { a: a {Read}, b: copy b {Read} } = x {Read};
-    let Foo { a: a {Read}, b: ref b  {Read} } = x {Read};
-
-## Output
-
-The pass results in the struct `MoveMaps` which contains several
-maps:
-
-`moves_map` is a set containing the id of every *outermost expression* or
-*binding* that causes a move.  Note that `moves_map` only contains the *outermost
-expressions* that are moved.  Therefore, if you have a use of `x.b`,
-as in the example `y` above, the expression `x.b` would be in the
-`moves_map` but not `x`.  The reason for this is that, for most
-purposes, it's only the outermost expression that is needed.  The
-borrow checker and trans, for example, only care about the outermost
-expressions that are moved.  It is more efficient therefore just to
-store those entries.
-
-Finally, the `capture_map` maps from the node_id of a closure
-expression to an array of `CaptureVar` structs detailing which
-variables are captured and how (by ref, by copy, by move).
-
-## Enforcement of Moves
-
-The enforcement of moves is done by the borrow checker.  Please see
-the section "Moves and initialization" in `middle/borrowck/doc.rs`.
-
-## Distributive property
-
-Copies are "distributive" over parenthesization, but blocks are
-considered rvalues.  What this means is that, for example, neither
-`a.clone()` nor `(a).clone()` will move `a` (presuming that `a` has a
-linear type and `clone()` takes its self by reference), but
-`{a}.clone()` will move `a`, as would `(if cond {a} else {b}).clone()`
-and so on.
-
-*/
-
-
-use middle::pat_util::{pat_bindings};
-use middle::freevars;
-use middle::ty;
-use middle::typeck::MethodCall;
-use util::ppaux;
-use util::ppaux::Repr;
-use util::common::indenter;
-use util::ppaux::UserString;
-use util::nodemap::{NodeMap, NodeSet};
-
-use std::rc::Rc;
-use syntax::ast::*;
-use syntax::ast_util;
-use syntax::visit;
-use syntax::visit::Visitor;
-use syntax::codemap::Span;
-
-#[deriving(Eq, Encodable, Decodable)]
-pub enum CaptureMode {
-    CapCopy, // Copy the value into the closure.
-    CapMove, // Move the value into the closure.
-    CapRef,  // Reference directly from parent stack frame (used by `||`).
-}
-
-#[deriving(Encodable, Decodable)]
-pub struct CaptureVar {
-    pub def: Def,         // Variable being accessed free
-    pub span: Span,       // Location of an access to this variable
-    pub mode: CaptureMode // How variable is being accessed
-}
-
-pub type CaptureMap = NodeMap<Rc<Vec<CaptureVar>>>;
-
-/** See the section Output on the module comment for explanation. */
-#[deriving(Clone)]
-pub struct MoveMaps {
-    pub moves_map: NodeSet,
-    /**
-     * Set of variable node-ids that are moved.
-     *
-     * pub Note: The `moves_map` stores expression ids that are moves,
-     * whereas this set stores the ids of the variables that are
-     * moved at some point */
-    pub capture_map: CaptureMap
-}
-
-#[deriving(Clone)]
-struct VisitContext<'a> {
-    tcx: &'a ty::ctxt,
-    move_maps: MoveMaps
-}
-
-#[deriving(Eq)]
-enum UseMode {
-    Move,        // This value or something owned by it is moved.
-    Read         // Read no matter what the type.
-}
-
-impl<'a> visit::Visitor<()> for VisitContext<'a> {
-    fn visit_fn(&mut self, fk: &visit::FnKind, fd: &FnDecl,
-                b: &Block, s: Span, n: NodeId, _: ()) {
-        compute_modes_for_fn(self, fk, fd, b, s, n);
-    }
-    fn visit_expr(&mut self, ex: &Expr, _: ()) {
-        compute_modes_for_expr(self, ex);
-    }
-    fn visit_local(&mut self, l: &Local, _: ()) {
-        compute_modes_for_local(self, l);
-    }
-    // FIXME(#10894) should continue recursing
-    fn visit_ty(&mut self, _t: &Ty, _: ()) {}
-}
-
-pub fn compute_moves(tcx: &ty::ctxt, krate: &Crate) -> MoveMaps {
-    let mut visit_cx = VisitContext {
-        tcx: tcx,
-        move_maps: MoveMaps {
-            moves_map: NodeSet::new(),
-            capture_map: NodeMap::new()
-        }
-    };
-    visit::walk_crate(&mut visit_cx, krate, ());
-    visit_cx.move_maps
-}
-
-pub fn moved_variable_node_id_from_def(def: Def) -> Option<NodeId> {
-    match def {
-        DefBinding(nid, _) |
-        DefArg(nid, _) |
-        DefLocal(nid, _) => Some(nid),
-
-      _ => None
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Expressions
-
-fn compute_modes_for_local<'a>(cx: &mut VisitContext,
-                               local: &Local) {
-    cx.use_pat(local.pat);
-    for &init in local.init.iter() {
-        cx.use_expr(init, Read);
-    }
-}
-
-fn compute_modes_for_fn(cx: &mut VisitContext,
-                        fk: &visit::FnKind,
-                        decl: &FnDecl,
-                        body: &Block,
-                        span: Span,
-                        id: NodeId) {
-    for a in decl.inputs.iter() {
-        cx.use_pat(a.pat);
-    }
-    visit::walk_fn(cx, fk, decl, body, span, id, ());
-}
-
-fn compute_modes_for_expr(cx: &mut VisitContext,
-                          expr: &Expr)
-{
-    cx.consume_expr(expr);
-}
-
-impl<'a> VisitContext<'a> {
-    pub fn consume_exprs(&mut self, exprs: &[@Expr]) {
-        for expr in exprs.iter() {
-            self.consume_expr(*expr);
-        }
-    }
-
-    pub fn consume_expr(&mut self, expr: &Expr) {
-        /*!
-         * Indicates that the value of `expr` will be consumed,
-         * meaning either copied or moved depending on its type.
-         */
-
-        debug!("consume_expr(expr={})",
-               expr.repr(self.tcx));
-
-        let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
-        if ty::type_moves_by_default(self.tcx, expr_ty) {
-            self.move_maps.moves_map.insert(expr.id);
-            self.use_expr(expr, Move);
-        } else {
-            self.use_expr(expr, Read);
-        };
-    }
-
-    pub fn consume_block(&mut self, blk: &Block) {
-        /*!
-         * Indicates that the value of `blk` will be consumed,
-         * meaning either copied or moved depending on its type.
-         */
-
-        debug!("consume_block(blk.id={:?})", blk.id);
-
-        for stmt in blk.stmts.iter() {
-            self.visit_stmt(*stmt, ());
-        }
-
-        for tail_expr in blk.expr.iter() {
-            self.consume_expr(*tail_expr);
-        }
-    }
-
-    pub fn use_expr(&mut self,
-                    expr: &Expr,
-                    expr_mode: UseMode) {
-        /*!
-         * Indicates that `expr` is used with a given mode.  This will
-         * in turn trigger calls to the subcomponents of `expr`.
-         */
-
-        debug!("use_expr(expr={}, mode={:?})",
-               expr.repr(self.tcx),
-               expr_mode);
-
-        // `expr_mode` refers to the post-adjustment value.  If one of
-        // those adjustments is to take a reference, then it's only
-        // reading the underlying expression, not moving it.
-        let comp_mode = match self.tcx.adjustments.borrow().find(&expr.id) {
-            Some(adjustment) => {
-                match *adjustment {
-                    ty::AutoDerefRef(ty::AutoDerefRef {
-                        autoref: Some(_),
-                        ..
-                    }) => Read,
-                    _ => expr_mode,
-                }
-            }
-            _ => expr_mode,
-        };
-
-        debug!("comp_mode = {:?}", comp_mode);
-
-        match expr.node {
-            ExprUnary(UnDeref, base) => {      // *base
-                if !self.use_overloaded_operator(expr, base, []) {
-                    // Moving out of *base moves out of base.
-                    self.use_expr(base, comp_mode);
-                }
-            }
-
-            ExprField(base, _, _) => {         // base.f
-                // Moving out of base.f moves out of base.
-                self.use_expr(base, comp_mode);
-            }
-
-            ExprIndex(lhs, rhs) => {           // lhs[rhs]
-                if !self.use_overloaded_operator(expr, lhs, [rhs]) {
-                    self.use_expr(lhs, comp_mode);
-                    self.consume_expr(rhs);
-                }
-            }
-
-            ExprCall(callee, ref args) => {    // callee(args)
-                // Figure out whether the called function is consumed.
-                let mode = match ty::get(ty::expr_ty(self.tcx, callee)).sty {
-                    ty::ty_closure(ref cty) => {
-                        match cty.onceness {
-                        Once => Move,
-                        Many => Read,
-                        }
-                    },
-                    ty::ty_bare_fn(..) => Read,
-                    ref x =>
-                        self.tcx.sess.span_bug(callee.span,
-                            format!("non-function type in moves for expr_call: {:?}", x)),
-                };
-                // Note we're not using consume_expr, which uses type_moves_by_default
-                // to determine the mode, for this. The reason is that while stack
-                // closures should be noncopyable, they shouldn't move by default;
-                // calling a closure should only consume it if it's once.
-                if mode == Move {
-                    self.move_maps.moves_map.insert(callee.id);
-                }
-                self.use_expr(callee, mode);
-                self.use_fn_args(args.as_slice());
-            }
-
-            ExprMethodCall(_, _, ref args) => { // callee.m(args)
-                self.use_fn_args(args.as_slice());
-            }
-
-            ExprStruct(_, ref fields, opt_with) => {
-                for field in fields.iter() {
-                    self.consume_expr(field.expr);
-                }
-
-                for with_expr in opt_with.iter() {
-                    // If there are any fields whose type is move-by-default,
-                    // then `with` is consumed, otherwise it is only read
-                    let with_ty = ty::expr_ty(self.tcx, *with_expr);
-                    let with_fields = match ty::get(with_ty).sty {
-                        ty::ty_struct(did, ref substs) => {
-                            ty::struct_fields(self.tcx, did, substs)
-                        }
-                        ref r => {
-                           self.tcx.sess.span_bug(
-                                with_expr.span,
-                                format!("bad base expr type in record: {:?}", r))
-                        }
-                    };
-
-                    // The `with` expr must be consumed if it contains
-                    // any fields which (1) were not explicitly
-                    // specified and (2) have a type that
-                    // moves-by-default:
-                    let consume_with = with_fields.iter().any(|tf| {
-                        !fields.iter().any(|f| f.ident.node.name == tf.ident.name) &&
-                            ty::type_moves_by_default(self.tcx, tf.mt.ty)
-                    });
-
-                    fn has_dtor(tcx: &ty::ctxt, ty: ty::t) -> bool {
-                        use middle::ty::{get,ty_struct,ty_enum};
-                        match get(ty).sty {
-                            ty_struct(did, _) | ty_enum(did, _) => ty::has_dtor(tcx, did),
-                            _ => false,
-                        }
-                    }
-
-                    if consume_with {
-                        if has_dtor(self.tcx, with_ty) {
-                            self.tcx.sess.span_err(with_expr.span,
-                                                   format!("cannot move out of type `{}`, \
-                                                         which defines the `Drop` trait",
-                                                        with_ty.user_string(self.tcx)));
-                        }
-                        self.consume_expr(*with_expr);
-                    } else {
-                        self.use_expr(*with_expr, Read);
-                    }
-                }
-            }
-
-            ExprTup(ref exprs) => {
-                self.consume_exprs(exprs.as_slice());
-            }
-
-            ExprIf(cond_expr, then_blk, opt_else_expr) => {
-                self.consume_expr(cond_expr);
-                self.consume_block(then_blk);
-                for else_expr in opt_else_expr.iter() {
-                    self.consume_expr(*else_expr);
-                }
-            }
-
-            ExprMatch(discr, ref arms) => {
-                for arm in arms.iter() {
-                    self.consume_arm(arm);
-                }
-
-                // The discriminant may, in fact, be partially moved
-                // if there are by-move bindings, but borrowck deals
-                // with that itself.
-                self.use_expr(discr, Read);
-            }
-
-            ExprParen(base) => {
-                // Note: base is not considered a *component* here, so
-                // use `expr_mode` not `comp_mode`.
-                self.use_expr(base, expr_mode);
-            }
-
-            ExprVec(ref exprs) => {
-                self.consume_exprs(exprs.as_slice());
-            }
-
-            ExprAddrOf(_, base) => {   // &base
-                self.use_expr(base, Read);
-            }
-
-            ExprPath(..) |
-            ExprInlineAsm(..) |
-            ExprBreak(..) |
-            ExprAgain(..) |
-            ExprLit(..) => {}
-
-            ExprLoop(blk, _) => {
-                self.consume_block(blk);
-            }
-
-            ExprWhile(cond_expr, blk) => {
-                self.consume_expr(cond_expr);
-                self.consume_block(blk);
-            }
-
-            ExprForLoop(..) => fail!("non-desugared expr_for_loop"),
-
-            ExprUnary(_, lhs) => {
-                if !self.use_overloaded_operator(expr, lhs, []) {
-                    self.consume_expr(lhs);
-                }
-            }
-
-            ExprBinary(_, lhs, rhs) => {
-                if !self.use_overloaded_operator(expr, lhs, [rhs]) {
-                    self.consume_expr(lhs);
-                    self.consume_expr(rhs);
-                }
-            }
-
-            ExprBlock(blk) => {
-                self.consume_block(blk);
-            }
-
-            ExprRet(ref opt_expr) => {
-                for expr in opt_expr.iter() {
-                    self.consume_expr(*expr);
-                }
-            }
-
-            ExprAssign(lhs, rhs) => {
-                self.use_expr(lhs, Read);
-                self.consume_expr(rhs);
-            }
-
-            ExprCast(base, _) => {
-                self.consume_expr(base);
-            }
-
-            ExprAssignOp(_, lhs, rhs) => {
-                // FIXME(#4712) --- Overloaded operators?
-                //
-                // if !self.use_overloaded_operator(expr, DoDerefArgs, lhs, [rhs])
-                // {
-                self.consume_expr(lhs);
-                self.consume_expr(rhs);
-                // }
-            }
-
-            ExprRepeat(base, count) => {
-                self.consume_expr(base);
-                self.consume_expr(count);
-            }
-
-            ExprFnBlock(ref decl, body) |
-            ExprProc(ref decl, body) => {
-                for a in decl.inputs.iter() {
-                    self.use_pat(a.pat);
-                }
-                let cap_vars = self.compute_captures(expr.id);
-                self.move_maps.capture_map.insert(expr.id, Rc::new(cap_vars));
-                self.consume_block(body);
-            }
-
-            ExprVstore(base, _) => {
-                self.use_expr(base, comp_mode);
-            }
-
-            ExprBox(place, base) => {
-                self.use_expr(place, comp_mode);
-                self.use_expr(base, comp_mode);
-            }
-
-            ExprMac(..) => {
-                self.tcx.sess.span_bug(
-                    expr.span,
-                    "macro expression remains after expansion");
-            }
-        }
-    }
-
-    pub fn use_overloaded_operator(&mut self,
-                                   expr: &Expr,
-                                   receiver_expr: @Expr,
-                                   arg_exprs: &[@Expr])
-                                   -> bool {
-        let method_call = MethodCall::expr(expr.id);
-        if !self.tcx.method_map.borrow().contains_key(&method_call) {
-            return false;
-        }
-
-        self.use_fn_arg(receiver_expr);
-
-        // for overloaded operatrs, we are always passing in a
-        // reference, so it's always read mode:
-        for arg_expr in arg_exprs.iter() {
-            self.use_expr(*arg_expr, Read);
-        }
-
-        return true;
-    }
-
-    pub fn consume_arm(&mut self, arm: &Arm) {
-        for pat in arm.pats.iter() {
-            self.use_pat(*pat);
-        }
-
-        for guard in arm.guard.iter() {
-            self.consume_expr(*guard);
-        }
-
-        self.consume_expr(arm.body);
-    }
-
-    pub fn use_pat(&mut self, pat: @Pat) {
-        /*!
-         *
-         * Decides whether each binding in a pattern moves the value
-         * into itself or not based on its type and annotation.
-         */
-
-        pat_bindings(&self.tcx.def_map, pat, |bm, id, _span, path| {
-            let binding_moves = match bm {
-                BindByRef(_) => false,
-                BindByValue(_) => {
-                    let pat_ty = ty::node_id_to_type(self.tcx, id);
-                    debug!("pattern {:?} {} type is {}",
-                           id,
-                           ast_util::path_to_ident(path).repr(self.tcx),
-                           pat_ty.repr(self.tcx));
-                    ty::type_moves_by_default(self.tcx, pat_ty)
-                }
-            };
-
-            debug!("pattern binding {:?}: bm={:?}, binding_moves={}",
-                   id, bm, binding_moves);
-
-            if binding_moves {
-                self.move_maps.moves_map.insert(id);
-            }
-        })
-    }
-
-    pub fn use_fn_args(&mut self, arg_exprs: &[@Expr]) {
-        //! Uses the argument expressions.
-        for arg_expr in arg_exprs.iter() {
-            self.use_fn_arg(*arg_expr);
-        }
-    }
-
-    pub fn use_fn_arg(&mut self, arg_expr: @Expr) {
-        //! Uses the argument.
-        self.consume_expr(arg_expr)
-    }
-
-    fn compute_captures(&mut self, fn_expr_id: NodeId) -> Vec<CaptureVar> {
-        debug!("compute_capture_vars(fn_expr_id={:?})", fn_expr_id);
-        let _indenter = indenter();
-
-        let fn_ty = ty::node_id_to_type(self.tcx, fn_expr_id);
-        freevars::with_freevars(self.tcx, fn_expr_id, |freevars| {
-            match ty::ty_closure_store(fn_ty) {
-                ty::RegionTraitStore(..) => {
-                    // || captures everything by ref
-                    freevars.iter()
-                            .map(|fvar| CaptureVar {def: fvar.def, span: fvar.span, mode: CapRef})
-                            .collect()
-                }
-                ty::UniqTraitStore => {
-                    // proc captures by copy or by move depending on type
-                    freevars.iter()
-                            .map(|fvar| {
-                        let fvar_def_id = ast_util::def_id_of_def(fvar.def).node;
-                        let fvar_ty = ty::node_id_to_type(self.tcx, fvar_def_id);
-                        debug!("fvar_def_id={:?} fvar_ty={}",
-                            fvar_def_id, ppaux::ty_to_str(self.tcx, fvar_ty));
-                        let mode = if ty::type_moves_by_default(self.tcx, fvar_ty) {
-                            CapMove
-                        } else {
-                            CapCopy
-                        };
-                        CaptureVar {def: fvar.def, span: fvar.span, mode:mode}
-                    }).collect()
-                }
-            }
-        })
-    }
-}
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index fda51e744ce..e5ddf07b682 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -2073,7 +2073,7 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
     }
 
     let encode_inlined_item: encoder::EncodeInlinedItem =
-        |ecx, ebml_w, ii| astencode::encode_inlined_item(ecx, ebml_w, ii, &cx.maps);
+        |ecx, ebml_w, ii| astencode::encode_inlined_item(ecx, ebml_w, ii);
 
     let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item);
     let metadata = encoder::encode_metadata(encode_parms, krate);
@@ -2103,7 +2103,7 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
 pub fn trans_crate(krate: ast::Crate,
                    analysis: CrateAnalysis,
                    output: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
-    let CrateAnalysis { ty_cx: tcx, exp_map2, maps, reachable, .. } = analysis;
+    let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, .. } = analysis;
 
     // Before we touch LLVM, make sure that multithreading is enabled.
     unsafe {
@@ -2135,7 +2135,7 @@ pub fn trans_crate(krate: ast::Crate,
     // 1. http://llvm.org/bugs/show_bug.cgi?id=11479
     let llmod_id = link_meta.crateid.name + ".rs";
 
-    let ccx = CrateContext::new(llmod_id, tcx, exp_map2, maps,
+    let ccx = CrateContext::new(llmod_id, tcx, exp_map2,
                                 Sha256::new(), link_meta, reachable);
     {
         let _icx = push_ctxt("text");
diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs
index df17fb516a7..a846848f494 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -13,8 +13,8 @@ use back::abi;
 use back::link::mangle_internal_name_by_path_and_seq;
 use driver::session::FullDebugInfo;
 use lib::llvm::ValueRef;
+use middle::freevars;
 use middle::lang_items::ClosureExchangeMallocFnLangItem;
-use middle::moves;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::common::*;
@@ -98,35 +98,14 @@ use syntax::ast_util;
 //
 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-pub enum EnvAction {
-    /// Copy the value from this llvm ValueRef into the environment.
-    EnvCopy,
-
-    /// Move the value from this llvm ValueRef into the environment.
-    EnvMove,
-
-    /// Access by reference (used for stack closures).
-    EnvRef
-}
-
 pub struct EnvValue {
-    action: EnvAction,
+    action: freevars::CaptureMode,
     datum: Datum<Lvalue>
 }
 
-impl EnvAction {
-    pub fn to_str(&self) -> ~str {
-        match *self {
-            EnvCopy => "EnvCopy".to_owned(),
-            EnvMove => "EnvMove".to_owned(),
-            EnvRef => "EnvRef".to_owned()
-        }
-    }
-}
-
 impl EnvValue {
     pub fn to_str(&self, ccx: &CrateContext) -> ~str {
-        format!("{}({})", self.action.to_str(), self.datum.to_str(ccx))
+        format!("{}({})", self.action, self.datum.to_str(ccx))
     }
 }
 
@@ -140,8 +119,8 @@ pub fn mk_closure_tys(tcx: &ty::ctxt,
     // converted to ptrs.
     let bound_tys = bound_values.iter().map(|bv| {
         match bv.action {
-            EnvCopy | EnvMove => bv.datum.ty,
-            EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
+            freevars::CaptureByValue => bv.datum.ty,
+            freevars::CaptureByRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
         }
     }).collect();
     let cdata_ty = ty::mk_tup(tcx, bound_tys);
@@ -232,10 +211,10 @@ pub fn store_environment<'a>(
         let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]);
 
         match bv.action {
-            EnvCopy | EnvMove => {
+            freevars::CaptureByValue => {
                 bcx = bv.datum.store_to(bcx, bound_data);
             }
-            EnvRef => {
+            freevars::CaptureByRef => {
                 Store(bcx, bv.datum.to_llref(), bound_data);
             }
         }
@@ -247,9 +226,11 @@ pub fn store_environment<'a>(
 // Given a context and a list of upvars, build a closure. This just
 // collects the upvars and packages them up for store_environment.
 fn build_closure<'a>(bcx0: &'a Block<'a>,
-                     cap_vars: &[moves::CaptureVar],
+                     freevar_mode: freevars::CaptureMode,
+                     freevars: &Vec<freevars::freevar_entry>,
                      store: ty::TraitStore)
-                     -> ClosureResult<'a> {
+                     -> ClosureResult<'a>
+{
     let _icx = push_ctxt("closure::build_closure");
 
     // If we need to, package up the iterator body to call
@@ -257,28 +238,9 @@ fn build_closure<'a>(bcx0: &'a Block<'a>,
 
     // Package up the captured upvars
     let mut env_vals = Vec::new();
-    for cap_var in cap_vars.iter() {
-        debug!("Building closure: captured variable {:?}", *cap_var);
-        let datum = expr::trans_local_var(bcx, cap_var.def);
-        match cap_var.mode {
-            moves::CapRef => {
-                let is_region_closure = match store {
-                    ty::RegionTraitStore(..) => true,
-                    ty::UniqTraitStore => false
-                };
-                assert!(is_region_closure);
-                env_vals.push(EnvValue {action: EnvRef,
-                                        datum: datum});
-            }
-            moves::CapCopy => {
-                env_vals.push(EnvValue {action: EnvCopy,
-                                        datum: datum});
-            }
-            moves::CapMove => {
-                env_vals.push(EnvValue {action: EnvMove,
-                                        datum: datum});
-            }
-        }
+    for freevar in freevars.iter() {
+        let datum = expr::trans_local_var(bcx, freevar.def);
+        env_vals.push(EnvValue {action: freevar_mode, datum: datum});
     }
 
     store_environment(bcx, env_vals, store)
@@ -287,13 +249,15 @@ fn build_closure<'a>(bcx0: &'a Block<'a>,
 // Given an enclosing block context, a new function context, a closure type,
 // and a list of upvars, generate code to load and populate the environment
 // with the upvars and type descriptors.
-fn load_environment<'a>(bcx: &'a Block<'a>, cdata_ty: ty::t,
-                        cap_vars: &[moves::CaptureVar],
-                        store: ty::TraitStore) -> &'a Block<'a> {
+fn load_environment<'a>(bcx: &'a Block<'a>,
+                        cdata_ty: ty::t,
+                        freevars: &Vec<freevars::freevar_entry>,
+                        store: ty::TraitStore)
+                        -> &'a Block<'a> {
     let _icx = push_ctxt("closure::load_environment");
 
     // Don't bother to create the block if there's nothing to load
-    if cap_vars.len() == 0 {
+    if freevars.len() == 0 {
         return bcx;
     }
 
@@ -312,13 +276,13 @@ fn load_environment<'a>(bcx: &'a Block<'a>, cdata_ty: ty::t,
 
     // Populate the upvars from the environment
     let mut i = 0u;
-    for cap_var in cap_vars.iter() {
+    for freevar in freevars.iter() {
         let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
         match store {
             ty::RegionTraitStore(..) => { upvarptr = Load(bcx, upvarptr); }
             ty::UniqTraitStore => {}
         }
-        let def_id = ast_util::def_id_of_def(cap_var.def);
+        let def_id = ast_util::def_id_of_def(freevar.def);
 
         bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvarptr);
 
@@ -330,7 +294,7 @@ fn load_environment<'a>(bcx: &'a Block<'a>, cdata_ty: ty::t,
                 env_pointer_alloca,
                 i,
                 store,
-                cap_var.span);
+                freevar.span);
         }
 
         i += 1u;
@@ -395,15 +359,19 @@ pub fn trans_expr_fn<'a>(
     // set an inline hint for all closures
     set_inline_hint(llfn);
 
-    let cap_vars = ccx.maps.capture_map.borrow().get_copy(&id);
+    let freevar_mode = freevars::get_capture_mode(tcx, id);
+    let freevars: Vec<freevars::freevar_entry> =
+        freevars::with_freevars(
+            tcx, id,
+            |fv| fv.iter().map(|&fv| fv).collect());
+
     let ClosureResult {llbox, cdata_ty, bcx} =
-        build_closure(bcx, cap_vars.as_slice(), store);
+        build_closure(bcx, freevar_mode, &freevars, store);
     trans_closure(ccx, decl, body, llfn,
                   bcx.fcx.param_substs, id,
                   [], ty::ty_fn_ret(fty),
-                  |bcx| load_environment(bcx, cdata_ty, cap_vars.as_slice(), store));
+                  |bcx| load_environment(bcx, cdata_ty, &freevars, store));
     fill_fn_pair(bcx, dest_addr, llfn, llbox);
-
     bcx
 }
 
diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs
index 119750cd9ce..f46c3232326 100644
--- a/src/librustc/middle/trans/context.rs
+++ b/src/librustc/middle/trans/context.rs
@@ -15,7 +15,6 @@ use lib::llvm::{ContextRef, ModuleRef, ValueRef};
 use lib::llvm::{llvm, TargetData, TypeNames};
 use lib::llvm::mk_target_data;
 use metadata::common::LinkMeta;
-use middle::astencode;
 use middle::resolve;
 use middle::trans::adt;
 use middle::trans::base;
@@ -113,7 +112,6 @@ pub struct CrateContext {
     pub type_hashcodes: RefCell<HashMap<ty::t, ~str>>,
     pub all_llvm_symbols: RefCell<HashSet<~str>>,
     pub tcx: ty::ctxt,
-    pub maps: astencode::Maps,
     pub stats: Stats,
     pub int_type: Type,
     pub opaque_vec_type: Type,
@@ -131,7 +129,6 @@ impl CrateContext {
     pub fn new(name: &str,
                tcx: ty::ctxt,
                emap2: resolve::ExportMap2,
-               maps: astencode::Maps,
                symbol_hasher: Sha256,
                link_meta: LinkMeta,
                reachable: NodeSet)
@@ -195,7 +192,6 @@ impl CrateContext {
                 type_hashcodes: RefCell::new(HashMap::new()),
                 all_llvm_symbols: RefCell::new(HashSet::new()),
                 tcx: tcx,
-                maps: maps,
                 stats: Stats {
                     n_static_tydescs: Cell::new(0u),
                     n_glues_created: Cell::new(0u),
diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs
index b96d71366e0..fa4a62319db 100644
--- a/src/librustc/middle/trans/inline.rs
+++ b/src/librustc/middle/trans/inline.rs
@@ -40,7 +40,7 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
     let csearch_result =
         csearch::maybe_get_item_ast(
             ccx.tcx(), fn_id,
-            |a,b,c,d| astencode::decode_inlined_item(a, b, &ccx.maps, c, d));
+            |a,b,c,d| astencode::decode_inlined_item(a, b, c, d));
     return match csearch_result {
         csearch::not_found => {
             ccx.external.borrow_mut().insert(fn_id, None);
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 30d75eda3ef..aa460cc9c18 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -13,6 +13,7 @@
 use back::svh::Svh;
 use driver::session::Session;
 use metadata::csearch;
+use mc = middle::mem_categorization;
 use middle::const_eval;
 use middle::lang_items::{ExchangeHeapLangItem, OpaqueStructLangItem};
 use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem};
@@ -508,7 +509,7 @@ pub struct UpvarId {
     pub closure_expr_id: ast::NodeId,
 }
 
-#[deriving(Clone, Eq, TotalEq, Hash)]
+#[deriving(Clone, Eq, TotalEq, Hash, Show)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
     ImmBorrow,
@@ -4779,3 +4780,33 @@ impl BorrowKind {
         }
     }
 }
+
+impl mc::Typer for ty::ctxt {
+    fn tcx<'a>(&'a self) -> &'a ty::ctxt {
+        self
+    }
+
+    fn node_ty(&self, id: ast::NodeId) -> mc::McResult<ty::t> {
+        Ok(ty::node_id_to_type(self, id))
+    }
+
+    fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option<ty::t> {
+        self.method_map.borrow().find(&method_call).map(|method| method.ty)
+    }
+
+    fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment>> {
+        &self.adjustments
+    }
+
+    fn is_method_call(&self, id: ast::NodeId) -> bool {
+        self.method_map.borrow().contains_key(&typeck::MethodCall::expr(id))
+    }
+
+    fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<ast::NodeId> {
+        self.region_maps.temporary_scope(rvalue_id)
+    }
+
+    fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow {
+        self.upvar_borrow_map.borrow().get_copy(&upvar_id)
+    }
+}
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index 9f36e903e58..4922ed116d8 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -261,7 +261,7 @@ impl<'a> Rcx<'a> {
     }
 }
 
-impl<'a, 'b> mc::Typer for &'a Rcx<'b> {
+impl<'fcx> mc::Typer for Rcx<'fcx> {
     fn tcx<'a>(&'a self) -> &'a ty::ctxt {
         self.fcx.tcx()
     }
@@ -638,7 +638,7 @@ fn check_expr_fn_block(rcx: &mut Rcx,
         ty::ty_closure(~ty::ClosureTy {store: ty::RegionTraitStore(..), ..}) => {
             freevars::with_freevars(tcx, expr.id, |freevars| {
                 propagate_upupvar_borrow_kind(rcx, expr, freevars);
-            });
+            })
         }
         _ => ()
     }
@@ -754,9 +754,9 @@ fn constrain_callee(rcx: &mut Rcx,
                 ty::RegionTraitStore(r, _) => {
                     // While we're here, link the closure's region with a unique
                     // immutable borrow (gathered later in borrowck)
-                    let mc = mc::MemCategorizationContext { typer: &*rcx };
+                    let mc = mc::MemCategorizationContext::new(rcx);
                     let expr_cmt = ignore_err!(mc.cat_expr(callee_expr));
-                    link_region(mc.typer, callee_expr.span, call_region,
+                    link_region(rcx, callee_expr.span, call_region,
                                 ty::UniqueImmBorrow, expr_cmt);
                     r
                 }
@@ -880,9 +880,9 @@ fn constrain_autoderefs(rcx: &mut Rcx,
                                 method.ty.repr(rcx.tcx())))
                 };
                 {
-                    let mc = mc::MemCategorizationContext { typer: &*rcx };
+                    let mc = mc::MemCategorizationContext::new(rcx);
                     let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i));
-                    link_region(mc.typer, deref_expr.span, r,
+                    link_region(rcx, deref_expr.span, r,
                                 ty::BorrowKind::from_mutbl(m), self_cmt);
                 }
 
@@ -1038,7 +1038,7 @@ fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr,
     debug!("link_addr_of(base=?)");
 
     let cmt = {
-        let mc = mc::MemCategorizationContext { typer: &*rcx };
+        let mc = mc::MemCategorizationContext::new(rcx);
         ignore_err!(mc.cat_expr(base))
     };
     link_region_from_node_type(rcx, expr.span, expr.id, mutability, cmt);
@@ -1056,9 +1056,9 @@ fn link_local(rcx: &Rcx, local: &ast::Local) {
         None => { return; }
         Some(expr) => expr,
     };
-    let mc = mc::MemCategorizationContext { typer: rcx };
+    let mc = mc::MemCategorizationContext::new(rcx);
     let discr_cmt = ignore_err!(mc.cat_expr(init_expr));
-    link_pattern(mc, discr_cmt, local.pat);
+    link_pattern(rcx, mc, discr_cmt, local.pat);
 }
 
 fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) {
@@ -1069,17 +1069,18 @@ fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) {
      */
 
     debug!("regionck::for_match()");
-    let mc = mc::MemCategorizationContext { typer: rcx };
+    let mc = mc::MemCategorizationContext::new(rcx);
     let discr_cmt = ignore_err!(mc.cat_expr(discr));
-    debug!("discr_cmt={}", discr_cmt.repr(mc.typer.tcx()));
+    debug!("discr_cmt={}", discr_cmt.repr(rcx.tcx()));
     for arm in arms.iter() {
         for &root_pat in arm.pats.iter() {
-            link_pattern(mc, discr_cmt.clone(), root_pat);
+            link_pattern(rcx, mc, discr_cmt.clone(), root_pat);
         }
     }
 }
 
-fn link_pattern(mc: mc::MemCategorizationContext<&Rcx>,
+fn link_pattern(rcx: &Rcx,
+                mc: mc::MemCategorizationContext<Rcx>,
                 discr_cmt: mc::cmt,
                 root_pat: &ast::Pat) {
     /*!
@@ -1092,7 +1093,7 @@ fn link_pattern(mc: mc::MemCategorizationContext<&Rcx>,
                 // `ref x` pattern
                 ast::PatIdent(ast::BindByRef(mutbl), _, _) => {
                     link_region_from_node_type(
-                        mc.typer, sub_pat.span, sub_pat.id,
+                        rcx, sub_pat.span, sub_pat.id,
                         mutbl, sub_cmt);
                 }
 
@@ -1100,7 +1101,7 @@ fn link_pattern(mc: mc::MemCategorizationContext<&Rcx>,
                 ast::PatVec(_, Some(slice_pat), _) => {
                     match mc.cat_slice_pattern(sub_cmt, slice_pat) {
                         Ok((slice_cmt, slice_mutbl, slice_r)) => {
-                            link_region(mc.typer, sub_pat.span, slice_r,
+                            link_region(rcx, sub_pat.span, slice_r,
                                         ty::BorrowKind::from_mutbl(slice_mutbl),
                                         slice_cmt);
                         }
@@ -1122,25 +1123,25 @@ fn link_autoref(rcx: &Rcx,
      */
 
     debug!("link_autoref(autoref={:?})", autoref);
-    let mc = mc::MemCategorizationContext { typer: rcx };
+    let mc = mc::MemCategorizationContext::new(rcx);
     let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs));
-    debug!("expr_cmt={}", expr_cmt.repr(mc.typer.tcx()));
+    debug!("expr_cmt={}", expr_cmt.repr(rcx.tcx()));
 
     match *autoref {
         ty::AutoPtr(r, m) => {
-            link_region(mc.typer, expr.span, r,
+            link_region(rcx, expr.span, r,
                         ty::BorrowKind::from_mutbl(m), expr_cmt);
         }
 
         ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
             let cmt_index = mc.cat_index(expr, expr_cmt, autoderefs+1);
-            link_region(mc.typer, expr.span, r,
+            link_region(rcx, expr.span, r,
                         ty::BorrowKind::from_mutbl(m), cmt_index);
         }
 
         ty::AutoBorrowObj(r, m) => {
             let cmt_deref = mc.cat_deref_obj(expr, expr_cmt);
-            link_region(mc.typer, expr.span, r,
+            link_region(rcx, expr.span, r,
                         ty::BorrowKind::from_mutbl(m), cmt_deref);
         }
 
@@ -1160,10 +1161,10 @@ fn link_by_ref(rcx: &Rcx,
     let tcx = rcx.tcx();
     debug!("link_by_ref(expr={}, callee_scope={})",
            expr.repr(tcx), callee_scope);
-    let mc = mc::MemCategorizationContext { typer: rcx };
+    let mc = mc::MemCategorizationContext::new(rcx);
     let expr_cmt = ignore_err!(mc.cat_expr(expr));
     let region_min = ty::ReScope(callee_scope);
-    link_region(mc.typer, expr.span, region_min, ty::ImmBorrow, expr_cmt);
+    link_region(rcx, expr.span, region_min, ty::ImmBorrow, expr_cmt);
 }
 
 fn link_region_from_node_type(rcx: &Rcx,
@@ -1306,9 +1307,9 @@ fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx,
      * expression.
      */
 
-    let mc = mc::MemCategorizationContext { typer: rcx };
+    let mc = mc::MemCategorizationContext::new(rcx);
     let cmt = ignore_err!(mc.cat_expr(lhs));
-    adjust_upvar_borrow_kind_for_mut(mc.typer, cmt);
+    adjust_upvar_borrow_kind_for_mut(rcx, cmt);
 }
 
 fn adjust_upvar_borrow_kind_for_mut(rcx: &Rcx,
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index adb97af7490..0fb9a2491db 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -469,7 +469,6 @@ impl<'a, O: IdVisitingOperation> Visitor<()> for IdVisitor<'a, O> {
         visit::walk_pat(self, pattern, env)
     }
 
-
     fn visit_expr(&mut self, expression: &Expr, env: ()) {
         self.operation.visit_id(expression.id);
         visit::walk_expr(self, expression, env)
@@ -590,6 +589,30 @@ pub fn compute_id_range_for_inlined_item(item: &InlinedItem) -> IdRange {
     visitor.result.get()
 }
 
+pub fn compute_id_range_for_fn_body(fk: &visit::FnKind,
+                                    decl: &FnDecl,
+                                    body: &Block,
+                                    sp: Span,
+                                    id: NodeId)
+                                    -> IdRange
+{
+    /*!
+     * Computes the id range for a single fn body,
+     * ignoring nested items.
+     */
+
+    let visitor = IdRangeComputingVisitor {
+        result: Cell::new(IdRange::max())
+    };
+    let mut id_visitor = IdVisitor {
+        operation: &visitor,
+        pass_through_items: false,
+        visited_outermost: false,
+    };
+    id_visitor.visit_fn(fk, decl, body, sp, id, ());
+    visitor.result.get()
+}
+
 pub fn is_item_impl(item: @ast::Item) -> bool {
     match item.node {
         ItemImpl(..) => true,
diff --git a/src/test/compile-fail/moves-sru-moved-field.rs b/src/test/compile-fail/moves-sru-moved-field.rs
index a4cbb30f93f..3306d19186c 100644
--- a/src/test/compile-fail/moves-sru-moved-field.rs
+++ b/src/test/compile-fail/moves-sru-moved-field.rs
@@ -25,13 +25,13 @@ fn test0(f: Foo, g: Noncopyable, h: Noncopyable) {
 fn test1(f: Foo, g: Noncopyable, h: Noncopyable) {
     // copying move-by-default fields from `f`, so move:
     let _b = Foo {noncopyable: g, ..f};
-    let _c = Foo {noncopyable: h, ..f}; //~ ERROR use of moved value: `f`
+    let _c = Foo {noncopyable: h, ..f}; //~ ERROR use of partially moved value: `f`
 }
 
 fn test2(f: Foo, g: Noncopyable) {
     // move non-copyable field
     let _b = Foo {copied: 22, moved: ~23, ..f};
-    let _c = Foo {noncopyable: g, ..f}; //~ ERROR use of moved value: `f`
+    let _c = Foo {noncopyable: g, ..f}; //~ ERROR use of partially moved value: `f`
 }
 
 fn main() {}