about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/metadata/common.rs2
-rw-r--r--src/librustc/middle/astencode.rs66
-rw-r--r--src/librustc/middle/expr_use_visitor.rs63
-rw-r--r--src/librustc/middle/mem_categorization.rs235
-rw-r--r--src/librustc/middle/ty.rs160
-rw-r--r--src/librustc/util/ppaux.rs9
-rw-r--r--src/librustc_driver/driver.rs2
-rw-r--r--src/librustc_driver/test.rs3
-rw-r--r--src/librustc_resolve/lib.rs9
-rw-r--r--src/librustc_trans/trans/base.rs12
-rw-r--r--src/librustc_trans/trans/closure.rs39
-rw-r--r--src/librustc_trans/trans/common.rs9
-rw-r--r--src/librustc_typeck/check/mod.rs12
-rw-r--r--src/librustc_typeck/check/regionck.rs61
-rw-r--r--src/librustc_typeck/check/upvar.rs75
-rw-r--r--src/librustc_typeck/check/writeback.rs22
-rw-r--r--src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs6
-rw-r--r--src/test/compile-fail/unboxed-closure-illegal-move.rs10
-rw-r--r--src/test/compile-fail/unboxed-closures-mutate-upvar.rs62
19 files changed, 418 insertions, 439 deletions
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index aa2be7153ad..6c1a8a6f54b 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -140,7 +140,7 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f
     tag_table_moves_map = 0x52,
     tag_table_capture_map = 0x53,
     tag_table_closures = 0x54,
-    tag_table_upvar_borrow_map = 0x55,
+    tag_table_upvar_capture_map = 0x55,
     tag_table_capture_modes = 0x56,
     tag_table_object_cast_map = 0x57,
 }
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 52417c3e66c..902c029fef4 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -518,10 +518,6 @@ fn encode_freevar_entry(rbml_w: &mut Encoder, fv: &ty::Freevar) {
     (*fv).encode(rbml_w).unwrap();
 }
 
-fn encode_capture_mode(rbml_w: &mut Encoder, cm: ast::CaptureClause) {
-    cm.encode(rbml_w).unwrap();
-}
-
 trait rbml_decoder_helper {
     fn read_freevar_entry(&mut self, dcx: &DecodeContext)
                           -> ty::Freevar;
@@ -559,6 +555,15 @@ impl tr for ty::UpvarBorrow {
     }
 }
 
+impl tr for ty::UpvarCapture {
+    fn tr(&self, dcx: &DecodeContext) -> ty::UpvarCapture {
+        match *self {
+            ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue,
+            ty::UpvarCapture::ByRef(ref data) => ty::UpvarCapture::ByRef(data.tr(dcx)),
+        }
+    }
+}
+
 // ______________________________________________________________________
 // Encoding and decoding of MethodCallee
 
@@ -1210,34 +1215,20 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
         });
 
         for freevar in fv.iter() {
-            match tcx.capture_mode(id) {
-                ast::CaptureByRef => {
-                    rbml_w.tag(c::tag_table_upvar_borrow_map, |rbml_w| {
-                        rbml_w.id(id);
-                        rbml_w.tag(c::tag_table_val, |rbml_w| {
-                            let var_id = freevar.def.def_id().node;
-                            let upvar_id = ty::UpvarId {
-                                var_id: var_id,
-                                closure_expr_id: id
-                            };
-                            let upvar_borrow = tcx.upvar_borrow_map.borrow()[upvar_id].clone();
-                            var_id.encode(rbml_w);
-                            upvar_borrow.encode(rbml_w);
-                        })
-                    })
-                }
-                _ => {}
-            }
-        }
-    }
-
-    for &cm in tcx.capture_modes.borrow().get(&id).iter() {
-        rbml_w.tag(c::tag_table_capture_modes, |rbml_w| {
-            rbml_w.id(id);
-            rbml_w.tag(c::tag_table_val, |rbml_w| {
-                encode_capture_mode(rbml_w, *cm);
+            rbml_w.tag(c::tag_table_upvar_capture_map, |rbml_w| {
+                rbml_w.id(id);
+                rbml_w.tag(c::tag_table_val, |rbml_w| {
+                    let var_id = freevar.def.def_id().node;
+                    let upvar_id = ty::UpvarId {
+                        var_id: var_id,
+                        closure_expr_id: id
+                    };
+                    let upvar_capture = tcx.upvar_capture_map.borrow()[upvar_id].clone();
+                    var_id.encode(rbml_w);
+                    upvar_capture.encode(rbml_w);
+                })
             })
-        })
+        }
     }
 
     let lid = ast::DefId { krate: ast::LOCAL_CRATE, node: id };
@@ -1911,21 +1902,14 @@ fn decode_side_tables(dcx: &DecodeContext,
                         }).unwrap().into_iter().collect();
                         dcx.tcx.freevars.borrow_mut().insert(id, fv_info);
                     }
-                    c::tag_table_upvar_borrow_map => {
+                    c::tag_table_upvar_capture_map => {
                         let var_id: ast::NodeId = Decodable::decode(val_dsr).unwrap();
                         let upvar_id = ty::UpvarId {
                             var_id: dcx.tr_id(var_id),
                             closure_expr_id: id
                         };
-                        let ub: ty::UpvarBorrow = Decodable::decode(val_dsr).unwrap();
-                        dcx.tcx.upvar_borrow_map.borrow_mut().insert(upvar_id, ub.tr(dcx));
-                    }
-                    c::tag_table_capture_modes => {
-                        let capture_mode = val_dsr.read_capture_mode();
-                        dcx.tcx
-                           .capture_modes
-                           .borrow_mut()
-                           .insert(id, capture_mode);
+                        let ub: ty::UpvarCapture = Decodable::decode(val_dsr).unwrap();
+                        dcx.tcx.upvar_capture_map.borrow_mut().insert(upvar_id, ub.tr(dcx));
                     }
                     c::tag_table_tcache => {
                         let type_scheme = val_dsr.read_type_scheme(dcx);
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 0d543ca7beb..7ef6ba3c190 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -1208,53 +1208,32 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
         debug!("walk_captures({})", closure_expr.repr(self.tcx()));
 
         ty::with_freevars(self.tcx(), closure_expr.id, |freevars| {
-            match self.tcx().capture_mode(closure_expr.id) {
-                ast::CaptureByRef => {
-                    self.walk_by_ref_captures(closure_expr, freevars);
-                }
-                ast::CaptureByValue => {
-                    self.walk_by_value_captures(closure_expr, freevars);
+            for freevar in freevars.iter() {
+                let id_var = freevar.def.def_id().node;
+                let upvar_id = ty::UpvarId { var_id: id_var,
+                                             closure_expr_id: closure_expr.id };
+                let upvar_capture = self.typer.upvar_capture(upvar_id).unwrap();
+                let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id,
+                                                                   closure_expr.span,
+                                                                   freevar.def));
+                match upvar_capture {
+                    ty::UpvarCapture::ByValue => {
+                        let mode = copy_or_move(self.typer, &cmt_var, CaptureMove);
+                        self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode);
+                    }
+                    ty::UpvarCapture::ByRef(upvar_borrow) => {
+                        self.delegate.borrow(closure_expr.id,
+                                             closure_expr.span,
+                                             cmt_var,
+                                             upvar_borrow.region,
+                                             upvar_borrow.kind,
+                                             ClosureCapture(freevar.span));
+                    }
                 }
             }
         });
     }
 
-    fn walk_by_ref_captures(&mut self,
-                            closure_expr: &ast::Expr,
-                            freevars: &[ty::Freevar]) {
-        for freevar in freevars.iter() {
-            let id_var = freevar.def.def_id().node;
-            let cmt_var = return_if_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.typer.upvar_borrow(upvar_id).unwrap();
-
-            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: &[ty::Freevar]) {
-        for freevar in freevars.iter() {
-            let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id,
-                                                               closure_expr.span,
-                                                               freevar.def));
-            let mode = copy_or_move(self.typer, &cmt_var, CaptureMove);
-            self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode);
-        }
-    }
-
     fn cat_captured_var(&mut self,
                         closure_id: ast::NodeId,
                         closure_span: Span,
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 1be1bfa6730..5fe5a4c7eb2 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -277,9 +277,7 @@ pub trait Typer<'tcx> : ty::ClosureTyper<'tcx> {
     fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment<'tcx>>>;
     fn is_method_call(&self, id: ast::NodeId) -> bool;
     fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<region::CodeExtent>;
-    fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarBorrow>;
-    fn capture_mode(&self, closure_expr_id: ast::NodeId)
-                    -> ast::CaptureClause;
+    fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture>;
 }
 
 impl MutabilityCategory {
@@ -595,8 +593,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
               match ty.sty {
                   ty::ty_closure(closure_id, _, _) => {
                       let kind = self.typer.closure_kind(closure_id);
-                      let mode = self.typer.capture_mode(fn_node_id);
-                      self.cat_upvar(id, span, var_id, fn_node_id, kind, mode)
+                      self.cat_upvar(id, span, var_id, fn_node_id, kind)
                   }
                   _ => {
                       self.tcx().sess.span_bug(
@@ -628,10 +625,13 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                  span: Span,
                  var_id: ast::NodeId,
                  fn_node_id: ast::NodeId,
-                 kind: ty::ClosureKind,
-                 mode: ast::CaptureClause)
-                 -> McResult<cmt<'tcx>> {
-        // An upvar can have up to 3 components.  The base is a
+                 kind: ty::ClosureKind)
+                 -> McResult<cmt<'tcx>>
+    {
+        // An upvar can have up to 3 components. We translate first to a
+        // `cat_upvar`, which is itself a fiction -- it represents the reference to the
+        // field from the environment.
+        //
         // `cat_upvar`.  Next, we add a deref through the implicit
         // environment pointer with an anonymous free region 'env and
         // appropriate borrow kind for closure kinds that take self by
@@ -650,135 +650,130 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
         // Fn             | copied -> &'env      | upvar -> &'env -> &'up bk
         // FnMut          | copied -> &'env mut  | upvar -> &'env mut -> &'up bk
         // FnOnce         | copied               | upvar -> &'up bk
-        let var_ty = try!(self.node_ty(var_id));
 
         let upvar_id = ty::UpvarId { var_id: var_id,
                                      closure_expr_id: fn_node_id };
+        let var_ty = try!(self.node_ty(var_id));
 
         // Mutability of original variable itself
         let var_mutbl = MutabilityCategory::from_local(self.tcx(), var_id);
 
-        // Construct information about env pointer dereference, if any
-        let mutbl = match kind {
-            ty::FnOnceClosureKind => None, // None, env is by-value
-            ty::FnMutClosureKind => match mode { // Depends on capture type
-                ast::CaptureByValue => Some(var_mutbl), // Mutable if the original var is
-                ast::CaptureByRef => Some(McDeclared) // Mutable regardless
-            },
-            ty::FnClosureKind => Some(McImmutable) // Never mutable
+        // Construct the upvar. This represents access to the field
+        // from the environment (perhaps we should eventually desugar
+        // this field further, but it will do for now).
+        let cmt_result = cmt_ {
+            id: id,
+            span: span,
+            cat: cat_upvar(Upvar {id: upvar_id, kind: kind}),
+            mutbl: var_mutbl,
+            ty: var_ty,
+            note: NoteNone
         };
-        let env_info = mutbl.map(|env_mutbl| {
-            // Look up the node ID of the closure body so we can construct
-            // a free region within it
-            let fn_body_id = {
-                let fn_expr = match self.tcx().map.find(fn_node_id) {
-                    Some(ast_map::NodeExpr(e)) => e,
-                    _ => unreachable!()
-                };
 
-                match fn_expr.node {
-                    ast::ExprClosure(_, _, _, ref body) => body.id,
-                    _ => unreachable!()
-                }
-            };
-
-            // Region of environment pointer
-            let env_region = ty::ReFree(ty::FreeRegion {
-                scope: region::CodeExtent::from_node_id(fn_body_id),
-                bound_region: ty::BrEnv
-            });
-
-            let env_ptr = BorrowedPtr(if env_mutbl.is_mutable() {
-                ty::MutBorrow
-            } else {
-                ty::ImmBorrow
-            }, env_region);
-
-            (env_mutbl, env_ptr)
-        });
+        // If this is a `FnMut` or `Fn` closure, then the above is
+        // conceptually a `&mut` or `&` reference, so we have to add a
+        // deref.
+        let cmt_result = match kind {
+            ty::FnOnceClosureKind => {
+                cmt_result
+            }
+            ty::FnMutClosureKind => {
+                self.env_deref(id, span, upvar_id, var_mutbl, ty::MutBorrow, cmt_result)
+            }
+            ty::FnClosureKind => {
+                self.env_deref(id, span, upvar_id, var_mutbl, ty::ImmBorrow, cmt_result)
+            }
+        };
 
-        // First, switch by capture mode
-        Ok(match mode {
-            ast::CaptureByValue => {
-                let mut base = cmt_ {
+        // If this is a by-ref capture, then the upvar we loaded is
+        // actually a reference, so we have to add an implicit deref
+        // for that.
+        let upvar_id = ty::UpvarId { var_id: var_id,
+                                     closure_expr_id: fn_node_id };
+        let upvar_capture = self.typer.upvar_capture(upvar_id).unwrap();
+        let cmt_result = match upvar_capture {
+            ty::UpvarCapture::ByValue => {
+                cmt_result
+            }
+            ty::UpvarCapture::ByRef(upvar_borrow) => {
+                let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region);
+                cmt_ {
                     id: id,
                     span: span,
-                    cat: cat_upvar(Upvar {
-                        id: upvar_id,
-                        kind: kind
-                    }),
-                    mutbl: var_mutbl,
+                    cat: cat_deref(Rc::new(cmt_result), 0, ptr),
+                    mutbl: MutabilityCategory::from_borrow_kind(upvar_borrow.kind),
                     ty: var_ty,
-                    note: NoteNone
-                };
-
-                match env_info {
-                    Some((env_mutbl, env_ptr)) => {
-                        // We need to add the env deref.  This means
-                        // that the above is actually immutable and
-                        // has a ref type.  However, nothing should
-                        // actually look at the type, so we can get
-                        // away with stuffing a `ty_err` in there
-                        // instead of bothering to construct a proper
-                        // one.
-                        base.mutbl = McImmutable;
-                        base.ty = self.tcx().types.err;
-                        Rc::new(cmt_ {
-                            id: id,
-                            span: span,
-                            cat: cat_deref(Rc::new(base), 0, env_ptr),
-                            mutbl: env_mutbl,
-                            ty: var_ty,
-                            note: NoteClosureEnv(upvar_id)
-                        })
-                    }
-                    None => Rc::new(base)
+                    note: NoteUpvarRef(upvar_id)
                 }
-            },
-            ast::CaptureByRef => {
-                // The type here is actually a ref (or ref of a ref),
-                // but we can again get away with not constructing one
-                // properly since it will never be used.
-                let mut base = cmt_ {
-                    id: id,
-                    span: span,
-                    cat: cat_upvar(Upvar {
-                        id: upvar_id,
-                        kind: kind
-                    }),
-                    mutbl: McImmutable,
-                    ty: self.tcx().types.err,
-                    note: NoteNone
-                };
+            }
+        };
 
-                match env_info {
-                    Some((env_mutbl, env_ptr)) => {
-                        base = cmt_ {
-                            id: id,
-                            span: span,
-                            cat: cat_deref(Rc::new(base), 0, env_ptr),
-                            mutbl: env_mutbl,
-                            ty: self.tcx().types.err,
-                            note: NoteClosureEnv(upvar_id)
-                        };
-                    }
-                    None => {}
-                }
+        Ok(Rc::new(cmt_result))
+    }
 
-                // Look up upvar borrow so we can get its region
-                let upvar_borrow = self.typer.upvar_borrow(upvar_id).unwrap();
-                let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region);
+    fn env_deref(&self,
+                 id: ast::NodeId,
+                 span: Span,
+                 upvar_id: ty::UpvarId,
+                 upvar_mutbl: MutabilityCategory,
+                 env_borrow_kind: ty::BorrowKind,
+                 cmt_result: cmt_<'tcx>)
+                 -> cmt_<'tcx>
+    {
+        // Look up the node ID of the closure body so we can construct
+        // a free region within it
+        let fn_body_id = {
+            let fn_expr = match self.tcx().map.find(upvar_id.closure_expr_id) {
+                Some(ast_map::NodeExpr(e)) => e,
+                _ => unreachable!()
+            };
 
-                Rc::new(cmt_ {
-                    id: id,
-                    span: span,
-                    cat: cat_deref(Rc::new(base), 0, ptr),
-                    mutbl: MutabilityCategory::from_borrow_kind(upvar_borrow.kind),
-                    ty: var_ty,
-                    note: NoteUpvarRef(upvar_id)
-                })
+            match fn_expr.node {
+                ast::ExprClosure(_, _, _, ref body) => body.id,
+                _ => unreachable!()
             }
-        })
+        };
+
+        // Region of environment pointer
+        let env_region = ty::ReFree(ty::FreeRegion {
+            scope: region::CodeExtent::from_node_id(fn_body_id),
+            bound_region: ty::BrEnv
+        });
+
+        let env_ptr = BorrowedPtr(env_borrow_kind, env_region);
+
+        let var_ty = cmt_result.ty;
+
+        // We need to add the env deref.  This means
+        // that the above is actually immutable and
+        // has a ref type.  However, nothing should
+        // actually look at the type, so we can get
+        // away with stuffing a `ty_err` in there
+        // instead of bothering to construct a proper
+        // one.
+        let cmt_result = cmt_ {
+            mutbl: McImmutable,
+            ty: self.tcx().types.err,
+            ..cmt_result
+        };
+
+        let mut deref_mutbl = MutabilityCategory::from_borrow_kind(env_borrow_kind);
+
+        // Issue #18335. If variable is declared as immutable, override the
+        // mutability from the environment and substitute an `&T` anyway.
+        match upvar_mutbl {
+            McImmutable => { deref_mutbl = McImmutable; }
+            McDeclared | McInherited => { }
+        }
+
+        cmt_ {
+            id: id,
+            span: span,
+            cat: cat_deref(Rc::new(cmt_result), 0, env_ptr),
+            mutbl: deref_mutbl,
+            ty: var_ty,
+            note: NoteClosureEnv(upvar_id)
+        }
     }
 
     pub fn cat_rvalue_node(&self,
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 44615f19d94..9958d015a8c 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -777,7 +777,7 @@ pub struct ctxt<'tcx> {
     pub populated_external_traits: RefCell<DefIdSet>,
 
     /// Borrows
-    pub upvar_borrow_map: RefCell<UpvarBorrowMap>,
+    pub upvar_capture_map: RefCell<UpvarCaptureMap>,
 
     /// These two caches are used by const_eval when decoding external statics
     /// and variants that are found.
@@ -803,9 +803,6 @@ pub struct ctxt<'tcx> {
     /// Maps any item's def-id to its stability index.
     pub stability: RefCell<stability::Index>,
 
-    /// Maps closures to their capture clauses.
-    pub capture_modes: RefCell<CaptureModeMap>,
-
     /// Maps def IDs to true if and only if they're associated types.
     pub associated_types: RefCell<DefIdMap<bool>>,
 
@@ -1247,60 +1244,31 @@ pub enum BorrowKind {
     MutBorrow
 }
 
-/// Information describing the borrowing of an upvar. This is computed
-/// during `typeck`, specifically by `regionck`. The general idea is
-/// that the compiler analyses treat closures like:
-///
-///     let closure: &'e fn() = || {
-///        x = 1;   // upvar x is assigned to
-///        use(y);  // upvar y is read
-///        foo(&z); // upvar z is borrowed immutably
-///     };
-///
-/// as if they were "desugared" to something loosely like:
-///
-///     struct Vars<'x,'y,'z> { x: &'x mut int,
-///                             y: &'y const int,
-///                             z: &'z int }
-///     let closure: &'e fn() = {
-///         fn f(env: &Vars) {
-///             *env.x = 1;
-///             use(*env.y);
-///             foo(env.z);
-///         }
-///         let env: &'e mut Vars<'x,'y,'z> = &mut Vars { x: &'x mut x,
-///                                                       y: &'y const y,
-///                                                       z: &'z z };
-///         (env, f)
-///     };
-///
-/// This is basically what happens at runtime. The closure is basically
-/// an existentially quantified version of the `(env, f)` pair.
-///
-/// This data structure indicates the region and mutability of a single
-/// one of the `x...z` borrows.
-///
-/// It may not be obvious why each borrowed variable gets its own
-/// lifetime (in the desugared version of the example, these are indicated
-/// by the lifetime parameters `'x`, `'y`, and `'z` in the `Vars` definition).
-/// Each such lifetime must encompass the lifetime `'e` of the closure itself,
-/// but need not be identical to it. The reason that this makes sense:
-///
-/// - Callers are only permitted to invoke the closure, and hence to
-///   use the pointers, within the lifetime `'e`, so clearly `'e` must
-///   be a sublifetime of `'x...'z`.
-/// - The closure creator knows which upvars were borrowed by the closure
-///   and thus `x...z` will be reserved for `'x...'z` respectively.
-/// - Through mutation, the borrowed upvars can actually escape
-///   the closure, so sometimes it is necessary for them to be larger
-///   than the closure lifetime itself.
+/// Information describing the capture of an upvar. This is computed
+/// during `typeck`, specifically by `regionck`.
+#[derive(PartialEq, Clone, RustcEncodable, RustcDecodable, Debug, Copy)]
+pub enum UpvarCapture {
+    /// Upvar is captured by value. This is always true when the
+    /// closure is labeled `move`, but can also be true in other cases
+    /// depending on inference.
+    ByValue,
+
+    /// Upvar is captured by reference.
+    ByRef(UpvarBorrow),
+}
+
 #[derive(PartialEq, Clone, RustcEncodable, RustcDecodable, Debug, Copy)]
 pub struct UpvarBorrow {
+    /// The kind of borrow: by-ref upvars have access to shared
+    /// immutable borrows, which are not part of the normal language
+    /// syntax.
     pub kind: BorrowKind,
+
+    /// Region of the resulting reference.
     pub region: ty::Region,
 }
 
-pub type UpvarBorrowMap = FnvHashMap<UpvarId, UpvarBorrow>;
+pub type UpvarCaptureMap = FnvHashMap<UpvarId, UpvarCapture>;
 
 impl Region {
     pub fn is_bound(&self) -> bool {
@@ -1320,7 +1288,7 @@ impl Region {
 }
 
 #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash,
-           RustcEncodable, RustcDecodable, Debug, Copy)]
+         RustcEncodable, RustcDecodable, Debug, Copy)]
 /// A "free" region `fr` can be interpreted as "some region
 /// at least as big as the scope `fr.scope`".
 pub struct FreeRegion {
@@ -1329,7 +1297,7 @@ pub struct FreeRegion {
 }
 
 #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash,
-           RustcEncodable, RustcDecodable, Debug, Copy)]
+         RustcEncodable, RustcDecodable, Debug, Copy)]
 pub enum BoundRegion {
     /// An anonymous region parameter for a given fn (&T)
     BrAnon(u32),
@@ -2359,7 +2327,6 @@ pub fn mk_ctxt<'tcx>(s: Session,
                      named_region_map: resolve_lifetime::NamedRegionMap,
                      map: ast_map::Map<'tcx>,
                      freevars: RefCell<FreevarMap>,
-                     capture_modes: RefCell<CaptureModeMap>,
                      region_maps: middle::region::RegionMaps,
                      lang_items: middle::lang_items::LanguageItems,
                      stability: stability::Index) -> ctxt<'tcx>
@@ -2413,7 +2380,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
         used_mut_nodes: RefCell::new(NodeSet()),
         populated_external_types: RefCell::new(DefIdSet()),
         populated_external_traits: RefCell::new(DefIdSet()),
-        upvar_borrow_map: RefCell::new(FnvHashMap()),
+        upvar_capture_map: RefCell::new(FnvHashMap()),
         extern_const_statics: RefCell::new(DefIdMap()),
         extern_const_variants: RefCell::new(DefIdMap()),
         method_map: RefCell::new(FnvHashMap()),
@@ -2422,7 +2389,6 @@ pub fn mk_ctxt<'tcx>(s: Session,
         node_lint_levels: RefCell::new(FnvHashMap()),
         transmute_restrictions: RefCell::new(Vec::new()),
         stability: RefCell::new(stability),
-        capture_modes: capture_modes,
         associated_types: RefCell::new(DefIdMap()),
         selection_cache: traits::SelectionCache::new(),
         repr_hint_cache: RefCell::new(DefIdMap()),
@@ -5646,7 +5612,6 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>,
     // implemented.
     assert!(closure_id.krate == ast::LOCAL_CRATE);
     let tcx = typer.tcx();
-    let capture_mode = tcx.capture_modes.borrow()[closure_id.node].clone();
     match tcx.freevars.borrow().get(&closure_id.node) {
         None => Some(vec![]),
         Some(ref freevars) => {
@@ -5659,43 +5624,38 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>,
                         };
                         let freevar_ty = freevar_ty.subst(tcx, substs);
 
-                        match capture_mode {
-                            ast::CaptureByValue => {
-                                Some(ClosureUpvar { def: freevar.def,
-                                                    span: freevar.span,
-                                                    ty: freevar_ty })
+                        let upvar_id = ty::UpvarId {
+                            var_id: freevar_def_id.node,
+                            closure_expr_id: closure_id.node
+                        };
+
+                        let captured_freevar_ty = match typer.upvar_capture(upvar_id) {
+                            Some(UpvarCapture::ByValue) => {
+                                freevar_ty
                             }
 
-                            ast::CaptureByRef => {
-                                let upvar_id = ty::UpvarId {
-                                    var_id: freevar_def_id.node,
-                                    closure_expr_id: closure_id.node
-                                };
+                            Some(UpvarCapture::ByRef(borrow)) => {
+                                mk_rptr(tcx,
+                                        tcx.mk_region(borrow.region),
+                                        ty::mt {
+                                            ty: freevar_ty,
+                                            mutbl: borrow.kind.to_mutbl_lossy(),
+                                        })
+                            }
 
-                                // FIXME
-                                let freevar_ref_ty = match typer.upvar_borrow(upvar_id) {
-                                    Some(borrow) => {
-                                        mk_rptr(tcx,
-                                                tcx.mk_region(borrow.region),
-                                                ty::mt {
-                                                    ty: freevar_ty,
-                                                    mutbl: borrow.kind.to_mutbl_lossy(),
-                                                })
-                                    }
-                                    None => {
-                                        // FIXME(#16640) we should really return None here;
-                                        // but that requires better inference integration,
-                                        // for now gin up something.
-                                        freevar_ty
-                                    }
-                                };
-                                Some(ClosureUpvar {
-                                    def: freevar.def,
-                                    span: freevar.span,
-                                    ty: freevar_ref_ty,
-                                })
+                            None => {
+                                // FIXME(#16640) we should really return None here;
+                                // but that requires better inference integration,
+                                // for now gin up something.
+                                freevar_ty
                             }
-                        }
+                        };
+
+                        Some(ClosureUpvar {
+                            def: freevar.def,
+                            span: freevar.span,
+                            ty: captured_freevar_ty,
+                        })
                     })
                     .collect()
         }
@@ -6449,14 +6409,13 @@ impl BorrowKind {
 }
 
 impl<'tcx> ctxt<'tcx> {
-    pub fn capture_mode(&self, closure_expr_id: ast::NodeId)
-                    -> ast::CaptureClause {
-        self.capture_modes.borrow()[closure_expr_id].clone()
-    }
-
     pub fn is_method_call(&self, expr_id: ast::NodeId) -> bool {
         self.method_map.borrow().contains_key(&MethodCall::expr(expr_id))
     }
+
+    pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture> {
+        Some(self.upvar_capture_map.borrow()[upvar_id].clone())
+    }
 }
 
 impl<'a,'tcx> mc::Typer<'tcx> for ParameterEnvironment<'a,'tcx> {
@@ -6494,13 +6453,8 @@ impl<'a,'tcx> mc::Typer<'tcx> for ParameterEnvironment<'a,'tcx> {
         self.tcx.region_maps.temporary_scope(rvalue_id)
     }
 
-    fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarBorrow> {
-        Some(self.tcx.upvar_borrow_map.borrow()[upvar_id].clone())
-    }
-
-    fn capture_mode(&self, closure_expr_id: ast::NodeId)
-                    -> ast::CaptureClause {
-        self.tcx.capture_mode(closure_expr_id)
+    fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture> {
+        self.tcx.upvar_capture(upvar_id)
     }
 
     fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 1be99a8e569..15a91fd0f8d 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -1292,6 +1292,15 @@ impl<'tcx> Repr<'tcx> for ty::UpvarBorrow {
     }
 }
 
+impl<'tcx> Repr<'tcx> for ty::UpvarCapture {
+    fn repr(&self, tcx: &ctxt) -> String {
+        match *self {
+            ty::UpvarCapture::ByValue => format!("ByValue"),
+            ty::UpvarCapture::ByRef(ref data) => format!("ByRef({})", data.repr(tcx)),
+        }
+    }
+}
+
 impl<'tcx> Repr<'tcx> for ty::IntVid {
     fn repr(&self, _tcx: &ctxt) -> String {
         format!("{:?}", self)
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 6e76519ce23..fc64cf9470a 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -560,7 +560,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
     let resolve::CrateMap {
         def_map,
         freevars,
-        capture_mode_map,
         export_map,
         trait_map,
         external_exports,
@@ -606,7 +605,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
                             named_region_map,
                             ast_map,
                             freevars,
-                            capture_mode_map,
                             region_map,
                             lang_items,
                             stability_index);
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 52d7415a523..45ff1c4537c 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -121,7 +121,7 @@ fn test_env<F>(source_string: &str,
 
     // run just enough stuff to build a tcx:
     let lang_items = lang_items::collect_language_items(krate, &sess);
-    let resolve::CrateMap { def_map, freevars, capture_mode_map, .. } =
+    let resolve::CrateMap { def_map, freevars, .. } =
         resolve::resolve_crate(&sess, &ast_map, &lang_items, krate, resolve::MakeGlobMap::No);
     let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map);
     let region_map = region::resolve_crate(&sess, krate);
@@ -132,7 +132,6 @@ fn test_env<F>(source_string: &str,
                           named_region_map,
                           ast_map,
                           freevars,
-                          capture_mode_map,
                           region_map,
                           lang_items,
                           stability_index);
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 000426771a8..c099762036a 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -62,7 +62,7 @@ use rustc::middle::lang_items::LanguageItems;
 use rustc::middle::pat_util::pat_bindings;
 use rustc::middle::privacy::*;
 use rustc::middle::subst::{ParamSpace, FnSpace, TypeSpace};
-use rustc::middle::ty::{CaptureModeMap, Freevar, FreevarMap, TraitMap, GlobMap};
+use rustc::middle::ty::{Freevar, FreevarMap, TraitMap, GlobMap};
 use rustc::util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap};
 use rustc::util::lev_distance::lev_distance;
 
@@ -900,7 +900,6 @@ struct Resolver<'a, 'tcx:'a> {
     def_map: DefMap,
     freevars: RefCell<FreevarMap>,
     freevars_seen: RefCell<NodeMap<NodeSet>>,
-    capture_mode_map: CaptureModeMap,
     export_map: ExportMap,
     trait_map: TraitMap,
     external_exports: ExternalExports,
@@ -974,7 +973,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             def_map: RefCell::new(NodeMap()),
             freevars: RefCell::new(NodeMap()),
             freevars_seen: RefCell::new(NodeMap()),
-            capture_mode_map: NodeMap(),
             export_map: NodeMap(),
             trait_map: NodeMap(),
             used_imports: HashSet::new(),
@@ -4523,8 +4521,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 visit::walk_expr(self, expr);
             }
 
-            ExprClosure(capture_clause, _, ref fn_decl, ref block) => {
-                self.capture_mode_map.insert(expr.id, capture_clause);
+            ExprClosure(_, _, ref fn_decl, ref block) => {
                 self.resolve_function(ClosureRibKind(expr.id),
                                       Some(&**fn_decl), NoTypeParameters,
                                       &**block);
@@ -4835,7 +4832,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 pub struct CrateMap {
     pub def_map: DefMap,
     pub freevars: RefCell<FreevarMap>,
-    pub capture_mode_map: RefCell<CaptureModeMap>,
     pub export_map: ExportMap,
     pub trait_map: TraitMap,
     pub external_exports: ExternalExports,
@@ -4875,7 +4871,6 @@ pub fn resolve_crate<'a, 'tcx>(session: &'a Session,
     CrateMap {
         def_map: resolver.def_map,
         freevars: resolver.freevars,
-        capture_mode_map: RefCell::new(resolver.capture_mode_map),
         export_map: resolver.export_map,
         trait_map: resolver.trait_map,
         external_exports: resolver.external_exports,
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 1195b9f084b..42bbbff22cd 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -1789,7 +1789,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     fcx = new_fn_ctxt(ccx,
                       llfndecl,
                       fn_ast_id,
-                      closure_env.kind != closure::NotClosure,
+                      closure_env.kind != closure::ClosureKind::NotClosure,
                       output_type,
                       param_substs,
                       Some(body.span),
@@ -1809,12 +1809,12 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                    .map(|arg| node_id_type(bcx, arg.id))
                    .collect::<Vec<_>>();
     let monomorphized_arg_types = match closure_env.kind {
-        closure::NotClosure => {
+        closure::ClosureKind::NotClosure => {
             monomorphized_arg_types
         }
 
         // Tuple up closure argument types for the "rust-call" ABI.
-        closure::Closure(..) => {
+        closure::ClosureKind::Closure => {
             vec![ty::mk_tup(ccx.tcx(), monomorphized_arg_types)]
         }
     };
@@ -1836,13 +1836,13 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     };
 
     bcx = match closure_env.kind {
-        closure::NotClosure => {
+        closure::ClosureKind::NotClosure => {
             copy_args_to_allocas(bcx,
                                  arg_scope,
                                  &decl.inputs[],
                                  arg_datums)
         }
-        closure::Closure(..) => {
+        closure::ClosureKind::Closure => {
             copy_closure_args_to_allocas(
                 bcx,
                 arg_scope,
@@ -1932,7 +1932,7 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                   attrs,
                   output_type,
                   abi,
-                  closure::ClosureEnv::new(&[], closure::NotClosure));
+                  closure::ClosureEnv::new(&[], closure::ClosureKind::NotClosure));
 }
 
 pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index c1914ce94aa..99ae0dc1007 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub use self::ClosureKind::*;
-
 use back::link::mangle_internal_name_by_path_and_seq;
 use middle::mem_categorization::Typer;
 use trans::adt;
@@ -33,9 +31,9 @@ use syntax::ast_util;
 
 fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                         arg_scope_id: ScopeId,
-                                        freevar_mode: ast::CaptureClause,
                                         freevars: &[ty::Freevar])
-                                        -> Block<'blk, 'tcx> {
+                                        -> Block<'blk, 'tcx>
+{
     let _icx = push_ctxt("closure::load_closure_environment");
 
     // Special case for small by-value selfs.
@@ -65,18 +63,21 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     };
 
     for (i, freevar) in freevars.iter().enumerate() {
+        let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(),
+                                     closure_expr_id: closure_id.node };
+        let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap();
         let mut upvar_ptr = GEPi(bcx, llenv, &[0, i]);
-        let captured_by_ref = match freevar_mode {
-            ast::CaptureByRef => {
+        let captured_by_ref = match upvar_capture {
+            ty::UpvarCapture::ByValue => false,
+            ty::UpvarCapture::ByRef(..) => {
                 upvar_ptr = Load(bcx, upvar_ptr);
                 true
             }
-            ast::CaptureByValue => false
         };
         let def_id = freevar.def.def_id();
         bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr);
 
-        if kind == ty::FnOnceClosureKind && freevar_mode == ast::CaptureByValue {
+        if kind == ty::FnOnceClosureKind && !captured_by_ref {
             bcx.fcx.schedule_drop_mem(arg_scope_id,
                                       upvar_ptr,
                                       node_id_type(bcx, def_id.node))
@@ -99,8 +100,7 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 #[derive(PartialEq)]
 pub enum ClosureKind<'tcx> {
     NotClosure,
-    // See load_closure_environment.
-    Closure(ast::CaptureClause)
+    Closure,
 }
 
 pub struct ClosureEnv<'a, 'tcx> {
@@ -125,9 +125,9 @@ impl<'a, 'tcx> ClosureEnv<'a, 'tcx> {
         }
 
         match self.kind {
-            NotClosure => bcx,
-            Closure(freevar_mode) => {
-                load_closure_environment(bcx, arg_scope, freevar_mode, self.freevars)
+            ClosureKind::NotClosure => bcx,
+            ClosureKind::Closure => {
+                load_closure_environment(bcx, arg_scope, self.freevars)
             }
         }
     }
@@ -212,7 +212,6 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
     let freevars: Vec<ty::Freevar> =
         ty::with_freevars(bcx.tcx(), id, |fv| fv.iter().map(|&fv| fv).collect());
-    let freevar_mode = bcx.tcx().capture_mode(id);
 
     let sig = ty::erase_late_bound_regions(bcx.tcx(), &function_type.sig);
 
@@ -225,8 +224,7 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                   &[],
                   sig.output,
                   function_type.abi,
-                  ClosureEnv::new(&freevars[],
-                                  Closure(freevar_mode)));
+                  ClosureEnv::new(&freevars[], ClosureKind::Closure));
 
     // Don't hoist this to the top of the function. It's perfectly legitimate
     // to have a zero-size closure (in which case dest will be `Ignore`) and
@@ -249,11 +247,14 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                                    dest_addr,
                                                    0,
                                                    i);
-        match freevar_mode {
-            ast::CaptureByValue => {
+        let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(),
+                                     closure_expr_id: id };
+        let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap();
+        match upvar_capture {
+            ty::UpvarCapture::ByValue => {
                 bcx = datum.store_to(bcx, upvar_slot_dest);
             }
-            ast::CaptureByRef => {
+            ty::UpvarCapture::ByRef(..) => {
                 Store(bcx, datum.to_llref(), upvar_slot_dest);
             }
         }
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 7d4e6aed876..a0dbc9c40a6 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -679,13 +679,8 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> {
         self.tcx().region_maps.temporary_scope(rvalue_id)
     }
 
-    fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarBorrow> {
-        Some(self.tcx().upvar_borrow_map.borrow()[upvar_id].clone())
-    }
-
-    fn capture_mode(&self, closure_expr_id: ast::NodeId)
-                    -> ast::CaptureClause {
-        self.tcx().capture_modes.borrow()[closure_expr_id].clone()
+    fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture> {
+        Some(self.tcx().upvar_capture_map.borrow()[upvar_id].clone())
     }
 
     fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 1f04cab572a..0c4bec48dd4 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -160,7 +160,7 @@ pub struct Inherited<'a, 'tcx: 'a> {
     item_substs: RefCell<NodeMap<ty::ItemSubsts<'tcx>>>,
     adjustments: RefCell<NodeMap<ty::AutoAdjustment<'tcx>>>,
     method_map: MethodMap<'tcx>,
-    upvar_borrow_map: RefCell<ty::UpvarBorrowMap>,
+    upvar_capture_map: RefCell<ty::UpvarCaptureMap>,
     closures: RefCell<DefIdMap<ty::Closure<'tcx>>>,
     object_cast_map: ObjectCastMap<'tcx>,
 
@@ -330,12 +330,8 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> {
     fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<CodeExtent> {
         self.param_env().temporary_scope(rvalue_id)
     }
-    fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarBorrow> {
-        self.inh.upvar_borrow_map.borrow().get(&upvar_id).cloned()
-    }
-    fn capture_mode(&self, closure_expr_id: ast::NodeId)
-                    -> ast::CaptureClause {
-        self.ccx.tcx.capture_mode(closure_expr_id)
+    fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture> {
+        self.inh.upvar_capture_map.borrow().get(&upvar_id).cloned()
     }
 }
 
@@ -378,7 +374,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
             adjustments: RefCell::new(NodeMap()),
             method_map: RefCell::new(FnvHashMap()),
             object_cast_map: RefCell::new(NodeMap()),
-            upvar_borrow_map: RefCell::new(FnvHashMap()),
+            upvar_capture_map: RefCell::new(FnvHashMap()),
             closures: RefCell::new(DefIdMap()),
             fn_sig_map: RefCell::new(NodeMap()),
             fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 15602505d90..d1fee578218 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -742,16 +742,9 @@ fn check_expr_fn_block(rcx: &mut Rcx,
 
     match function_type.sty {
         ty::ty_closure(_, region, _) => {
-            if tcx.capture_modes.borrow()[expr.id].clone() == ast::CaptureByRef {
-                ty::with_freevars(tcx, expr.id, |freevars| {
-                    if !freevars.is_empty() {
-                        // Variables being referenced must be constrained and registered
-                        // in the upvar borrow map
-                        constrain_free_variables_in_by_ref_closure(
-                            rcx, *region, expr, freevars);
-                    }
-                })
-            }
+            ty::with_freevars(tcx, expr.id, |freevars| {
+                constrain_captured_variables(rcx, *region, expr, freevars);
+            })
         }
         _ => { }
     }
@@ -799,14 +792,14 @@ fn check_expr_fn_block(rcx: &mut Rcx,
             let raw_var_ty = rcx.resolve_node_type(var_node_id);
             let upvar_id = ty::UpvarId { var_id: var_node_id,
                                          closure_expr_id: expr.id };
-            let var_ty = match rcx.fcx.inh.upvar_borrow_map.borrow().get(&upvar_id) {
-                Some(upvar_borrow) => {
+            let var_ty = match rcx.fcx.inh.upvar_capture_map.borrow()[upvar_id] {
+                ty::UpvarCapture::ByRef(ref upvar_borrow) => {
                     ty::mk_rptr(rcx.tcx(),
                                 rcx.tcx().mk_region(upvar_borrow.region),
                                 ty::mt { mutbl: upvar_borrow.kind.to_mutbl_lossy(),
                                          ty: raw_var_ty })
                 }
-                None => raw_var_ty
+                ty::UpvarCapture::ByValue => raw_var_ty,
             };
 
             // Check that the type meets the criteria of the existential bounds:
@@ -824,17 +817,17 @@ fn check_expr_fn_block(rcx: &mut Rcx,
 
     /// Make sure that all free variables referenced inside the closure outlive the closure's
     /// lifetime bound. Also, create an entry in the upvar_borrows map with a region.
-    fn constrain_free_variables_in_by_ref_closure(
+    fn constrain_captured_variables(
         rcx: &mut Rcx,
         region_bound: ty::Region,
         expr: &ast::Expr,
         freevars: &[ty::Freevar])
     {
         let tcx = rcx.fcx.ccx.tcx;
-        debug!("constrain_free_variables({}, {})",
+        debug!("constrain_captured_variables({}, {})",
                region_bound.repr(tcx), expr.repr(tcx));
         for freevar in freevars.iter() {
-            debug!("freevar def is {:?}", freevar.def);
+            debug!("constrain_captured_variables: freevar.def={:?}", freevar.def);
 
             // Identify the variable being closed over and its node-id.
             let def = freevar.def;
@@ -846,16 +839,20 @@ fn check_expr_fn_block(rcx: &mut Rcx,
             let upvar_id = ty::UpvarId { var_id: var_node_id,
                                          closure_expr_id: expr.id };
 
-            let upvar_borrow = rcx.fcx.inh.upvar_borrow_map.borrow()[upvar_id];
-
-            rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
-                            region_bound, upvar_borrow.region);
-
-            // Guarantee that the closure does not outlive the variable itself.
-            let enclosing_region = region_of_def(rcx.fcx, def);
-            debug!("enclosing_region = {}", enclosing_region.repr(tcx));
-            rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
-                            region_bound, enclosing_region);
+            match rcx.fcx.inh.upvar_capture_map.borrow()[upvar_id] {
+                ty::UpvarCapture::ByValue => { }
+                ty::UpvarCapture::ByRef(upvar_borrow) => {
+                    rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
+                                    region_bound, upvar_borrow.region);
+
+                    // Guarantee that the closure does not outlive the variable itself.
+                    let enclosing_region = region_of_def(rcx.fcx, def);
+                    debug!("constrain_captured_variables: enclosing_region = {}",
+                           enclosing_region.repr(tcx));
+                    rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
+                                    region_bound, enclosing_region);
+                }
+            }
         }
     }
 }
@@ -1333,22 +1330,20 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
     // Detect by-ref upvar `x`:
     let cause = match note {
         mc::NoteUpvarRef(ref upvar_id) => {
-            let mut upvar_borrow_map =
-                rcx.fcx.inh.upvar_borrow_map.borrow_mut();
-            match upvar_borrow_map.get_mut(upvar_id) {
-                Some(upvar_borrow) => {
+            let upvar_capture_map = rcx.fcx.inh.upvar_capture_map.borrow_mut();
+            match upvar_capture_map.get(upvar_id) {
+                Some(&ty::UpvarCapture::ByRef(ref upvar_borrow)) => {
                     // The mutability of the upvar may have been modified
                     // by the above adjustment, so update our local variable.
                     ref_kind = upvar_borrow.kind;
 
                     infer::ReborrowUpvar(span, *upvar_id)
                 }
-                None => {
+                _ => {
                     rcx.tcx().sess.span_bug(
                         span,
                         &format!("Illegal upvar id: {}",
-                                upvar_id.repr(
-                                    rcx.tcx()))[]);
+                                upvar_id.repr(rcx.tcx()))[]);
                 }
             }
         }
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 7fc0a83e34a..22e4716f4bd 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -121,25 +121,29 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
                      capture_clause: ast::CaptureClause,
                      _body: &ast::Block)
     {
-        match capture_clause {
-            ast::CaptureByValue => {}
-            _ => {
-                ty::with_freevars(self.tcx(), expr.id, |freevars| {
-                    for freevar in freevars.iter() {
-                        let var_node_id = freevar.def.local_node_id();
-                        let upvar_id = ty::UpvarId { var_id: var_node_id,
-                                                     closure_expr_id: expr.id };
-                        debug!("seed upvar_id {:?}", upvar_id);
+        ty::with_freevars(self.tcx(), expr.id, |freevars| {
+            for freevar in freevars.iter() {
+                let var_node_id = freevar.def.local_node_id();
+                let upvar_id = ty::UpvarId { var_id: var_node_id,
+                                             closure_expr_id: expr.id };
+                debug!("seed upvar_id {:?}", upvar_id);
+
+                let capture_kind = match capture_clause {
+                    ast::CaptureByValue => {
+                        ty::UpvarCapture::ByValue
+                    }
+                    ast::CaptureByRef => {
                         let origin = UpvarRegion(upvar_id, expr.span);
                         let freevar_region = self.infcx().next_region_var(origin);
                         let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
                                                              region: freevar_region };
-                        self.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
-                                                                          upvar_borrow);
+                        ty::UpvarCapture::ByRef(upvar_borrow)
                     }
-                });
+                };
+
+                self.fcx.inh.upvar_capture_map.borrow_mut().insert(upvar_id, capture_kind);
             }
-        }
+        });
     }
 }
 
@@ -150,7 +154,7 @@ struct AdjustBorrowKind<'a,'tcx:'a> {
     fcx: &'a FnCtxt<'a,'tcx>
 }
 
-impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
+impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
     fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
         AdjustBorrowKind { fcx: fcx }
     }
@@ -195,8 +199,8 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
                     // upvar, then we need to modify the
                     // borrow_kind of the upvar to make sure it
                     // is inferred to mutable if necessary
-                    let mut upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow_mut();
-                    let ub = &mut upvar_borrow_map[upvar_id];
+                    let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
+                    let ub = &mut upvar_capture_map[upvar_id];
                     self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
                 } else {
                     // assignment to deref of an `&mut`
@@ -237,7 +241,7 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
                     // upvar, then we need to modify the
                     // borrow_kind of the upvar to make sure it
                     // is inferred to unique if necessary
-                    let mut ub = self.fcx.inh.upvar_borrow_map.borrow_mut();
+                    let mut ub = self.fcx.inh.upvar_capture_map.borrow_mut();
                     let ub = &mut ub[upvar_id];
                     self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
                 } else {
@@ -262,23 +266,30 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
     /// some particular use.
     fn adjust_upvar_borrow_kind(&self,
                                 upvar_id: ty::UpvarId,
-                                upvar_borrow: &mut ty::UpvarBorrow,
+                                upvar_capture: &mut ty::UpvarCapture,
                                 kind: ty::BorrowKind) {
-        debug!("adjust_upvar_borrow_kind: id={:?} kind=({:?} -> {:?})",
-               upvar_id, upvar_borrow.kind, kind);
-
-        match (upvar_borrow.kind, kind) {
-            // Take RHS:
-            (ty::ImmBorrow, ty::UniqueImmBorrow) |
-            (ty::ImmBorrow, ty::MutBorrow) |
-            (ty::UniqueImmBorrow, ty::MutBorrow) => {
-                upvar_borrow.kind = kind;
+        debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
+               upvar_id, upvar_capture, kind);
+
+        match *upvar_capture {
+            ty::UpvarCapture::ByValue => {
+                // Upvar is already by-value, the strongest criteria.
             }
-            // Take LHS:
-            (ty::ImmBorrow, ty::ImmBorrow) |
-            (ty::UniqueImmBorrow, ty::ImmBorrow) |
-            (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
-            (ty::MutBorrow, _) => {
+            ty::UpvarCapture::ByRef(ref mut upvar_borrow) => {
+                match (upvar_borrow.kind, kind) {
+                    // Take RHS:
+                    (ty::ImmBorrow, ty::UniqueImmBorrow) |
+                    (ty::ImmBorrow, ty::MutBorrow) |
+                    (ty::UniqueImmBorrow, ty::MutBorrow) => {
+                        upvar_borrow.kind = kind;
+                    }
+                    // Take LHS:
+                    (ty::ImmBorrow, ty::ImmBorrow) |
+                    (ty::UniqueImmBorrow, ty::ImmBorrow) |
+                    (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
+                    (ty::MutBorrow, _) => {
+                    }
+                }
             }
         }
     }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index a5a600c5748..f7e1afed8fc 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -182,16 +182,20 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             return;
         }
 
-        for (upvar_id, upvar_borrow) in self.fcx.inh.upvar_borrow_map.borrow().iter() {
-            let r = upvar_borrow.region;
-            let r = self.resolve(&r, ResolvingUpvar(*upvar_id));
-            let new_upvar_borrow = ty::UpvarBorrow { kind: upvar_borrow.kind,
-                                                     region: r };
-            debug!("Upvar borrow for {} resolved to {}",
+        for (upvar_id, upvar_capture) in self.fcx.inh.upvar_capture_map.borrow().iter() {
+            let new_upvar_capture = match *upvar_capture {
+                ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue,
+                ty::UpvarCapture::ByRef(ref upvar_borrow) => {
+                    let r = upvar_borrow.region;
+                    let r = self.resolve(&r, ResolvingUpvar(*upvar_id));
+                    ty::UpvarCapture::ByRef(
+                        ty::UpvarBorrow { kind: upvar_borrow.kind, region: r })
+                }
+            };
+            debug!("Upvar capture for {} resolved to {}",
                    upvar_id.repr(self.tcx()),
-                   new_upvar_borrow.repr(self.tcx()));
-            self.fcx.tcx().upvar_borrow_map.borrow_mut().insert(
-                *upvar_id, new_upvar_borrow);
+                   new_upvar_capture.repr(self.tcx()));
+            self.fcx.tcx().upvar_capture_map.borrow_mut().insert(*upvar_id, new_upvar_capture);
         }
     }
 
diff --git a/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs b/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs
index 010ddb792cc..9b9edce243b 100644
--- a/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs
+++ b/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs
@@ -58,8 +58,10 @@ fn test6() {
 fn test7() {
     fn foo<F>(_: F) where F: FnMut(Box<FnMut(isize)>, isize) {}
     let mut f = |&mut: g: Box<FnMut(isize)>, b: isize| {};
-    f(box |a| { //~ ERROR: cannot borrow `f` as immutable because it is also borrowed as mutable
-        foo(f); //~ ERROR: cannot move out of captured outer variable
+    f(box |a| {
+        foo(f);
+        //~^ ERROR cannot move `f` into closure because it is borrowed
+        //~| ERROR cannot move out of captured outer variable in an `FnMut` closure
     }, 3);
 }
 
diff --git a/src/test/compile-fail/unboxed-closure-illegal-move.rs b/src/test/compile-fail/unboxed-closure-illegal-move.rs
index d489c3a64fa..1312b42fb82 100644
--- a/src/test/compile-fail/unboxed-closure-illegal-move.rs
+++ b/src/test/compile-fail/unboxed-closure-illegal-move.rs
@@ -19,24 +19,24 @@ fn main() {
     // By-ref cases
     {
         let x = box 0us;
-        let f = |&:| drop(x); //~ cannot move
+        let f = |&:| drop(x); //~ ERROR cannot move
     }
     {
         let x = box 0us;
-        let f = |&mut:| drop(x); //~ cannot move
+        let f = |&mut:| drop(x); //~ ERROR cannot move
     }
     {
         let x = box 0us;
-        let f = |:| drop(x); //~ cannot move
+        let f = |:| drop(x); // OK -- FnOnce
     }
     // By-value cases
     {
         let x = box 0us;
-        let f = move |&:| drop(x); //~ cannot move
+        let f = move |&:| drop(x); //~ ERROR cannot move
     }
     {
         let x = box 0us;
-        let f = move |&mut:| drop(x); //~ cannot move
+        let f = move |&mut:| drop(x); //~ ERROR cannot move
     }
     {
         let x = box 0us;
diff --git a/src/test/compile-fail/unboxed-closures-mutate-upvar.rs b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs
new file mode 100644
index 00000000000..96c7948dcb0
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs
@@ -0,0 +1,62 @@
+// Copyright 2015 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.
+
+// Test that we cannot mutate an outer variable that is not declared
+// as `mut` through a closure. Also test that we CAN mutate a moved copy,
+// unless this is a `Fn` closure. Issue #16749.
+
+use std::mem;
+
+fn a() {
+    let n = 0u8;
+    let mut f = |&mut:| { //~ ERROR closure cannot assign
+        n += 1;
+    };
+}
+
+fn b() {
+    let mut n = 0u8;
+    let mut f = |&mut:| {
+        n += 1; // OK
+    };
+}
+
+fn c() {
+    let n = 0u8;
+    let mut f = move |&mut:| {
+        // If we just did a straight-forward desugaring, this would
+        // compile, but we do something a bit more subtle, and hence
+        // we get an error.
+        n += 1; //~ ERROR cannot assign
+    };
+}
+
+fn d() {
+    let mut n = 0u8;
+    let mut f = move |&mut:| {
+        n += 1; // OK
+    };
+}
+
+fn e() {
+    let n = 0u8;
+    let mut f = move |&:| {
+        n += 1; //~ ERROR cannot assign
+    };
+}
+
+fn f() {
+    let mut n = 0u8;
+    let mut f = move |&:| {
+        n += 1; //~ ERROR cannot assign
+    };
+}
+
+fn main() { }