diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 477fc5e1c0f..5f86745f3f2 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -360,8 +360,9 @@ pub fn phase_3_run_analysis_passes(sess: Session,
             plugin::build::find_plugin_registrar(
                 sess.diagnostic(), krate)));
 
-    let freevars = time(time_passes, "freevar finding", (), |_|
-                        freevars::annotate_freevars(&def_map, krate));
+    let (freevars, capture_modes) =
+        time(time_passes, "freevar finding", (), |_|
+             freevars::annotate_freevars(&def_map, krate));
 
     let region_map = time(time_passes, "region resolution", (), |_|
                           middle::region::resolve_crate(&sess, krate));
@@ -372,8 +373,15 @@ pub fn phase_3_run_analysis_passes(sess: Session,
     let stability_index = time(time_passes, "stability index", (), |_|
                                stability::Index::build(krate));
 
-    let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
-                            freevars, region_map, lang_items, stability_index);
+    let ty_cx = ty::mk_ctxt(sess,
+                            def_map,
+                            named_region_map,
+                            ast_map,
+                            freevars,
+                            capture_modes,
+                            region_map,
+                            lang_items,
+                            stability_index);
 
     // passes are timed inside typeck
     typeck::check_crate(&ty_cx, trait_map, krate);
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 8ed471ec58a..633ad829349 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -141,9 +141,10 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f
     tag_table_capture_map = 0x53,
     tag_table_unboxed_closure_type = 0x54,
     tag_table_upvar_borrow_map = 0x55,
+    tag_table_capture_modes = 0x56,
 }
 static first_astencode_tag: uint = tag_ast as uint;
-static last_astencode_tag: uint = tag_table_upvar_borrow_map as uint;
+static last_astencode_tag: uint = tag_table_capture_modes as uint;
 impl astencode_tag {
     pub fn from_uint(value : uint) -> Option<astencode_tag> {
         let is_a_tag = first_astencode_tag <= value && value <= last_astencode_tag;
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 33b663dea15..e31af8b58b9 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -18,8 +18,8 @@ use driver::session::Session;
 use metadata::decoder;
 use middle::def;
 use e = metadata::encoder;
+use middle::freevars::{CaptureMode, freevar_entry};
 use middle::freevars;
-use middle::freevars::freevar_entry;
 use middle::region;
 use metadata::tydecode;
 use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter,
@@ -530,9 +530,14 @@ fn encode_freevar_entry(rbml_w: &mut Encoder, fv: &freevar_entry) {
     (*fv).encode(rbml_w).unwrap();
 }
 
+fn encode_capture_mode(rbml_w: &mut Encoder, cm: CaptureMode) {
+    cm.encode(rbml_w).unwrap();
+}
+
 trait rbml_decoder_helper {
     fn read_freevar_entry(&mut self, xcx: &ExtendedDecodeContext)
                           -> freevar_entry;
+    fn read_capture_mode(&mut self) -> CaptureMode;
 }
 
 impl<'a> rbml_decoder_helper for reader::Decoder<'a> {
@@ -541,6 +546,11 @@ impl<'a> rbml_decoder_helper for reader::Decoder<'a> {
         let fv: freevar_entry = Decodable::decode(self).unwrap();
         fv.tr(xcx)
     }
+
+    fn read_capture_mode(&mut self) -> CaptureMode {
+        let cm: CaptureMode = Decodable::decode(self).unwrap();
+        cm
+    }
 }
 
 impl tr for freevar_entry {
@@ -1096,6 +1106,15 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
         }
     }
 
+    for &cm in tcx.capture_modes.borrow().find(&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);
+            })
+        })
+    }
+
     let lid = ast::DefId { krate: ast::LOCAL_CRATE, node: id };
     for &pty in tcx.tcache.borrow().find(&lid).iter() {
         rbml_w.tag(c::tag_table_tcache, |rbml_w| {
@@ -1509,6 +1528,13 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext,
                         let ub: ty::UpvarBorrow = Decodable::decode(val_dsr).unwrap();
                         dcx.tcx.upvar_borrow_map.borrow_mut().insert(upvar_id, ub.tr(xcx));
                     }
+                    c::tag_table_capture_modes => {
+                        let capture_mode = val_dsr.read_capture_mode();
+                        dcx.tcx
+                           .capture_modes
+                           .borrow_mut()
+                           .insert(id, capture_mode);
+                    }
                     c::tag_table_tcache => {
                         let pty = val_dsr.read_polytype(xcx);
                         let lid = ast::DefId { krate: ast::LOCAL_CRATE, node: id };
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 6c5352ed796..fd1369439c9 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -291,7 +291,7 @@ pub fn closure_to_block(closure_id: ast::NodeId,
     match tcx.map.get(closure_id) {
         ast_map::NodeExpr(expr) => match expr.node {
             ast::ExprProc(_decl, block) |
-            ast::ExprFnBlock(_decl, block) => { block.id }
+            ast::ExprFnBlock(_, _decl, block) => { block.id }
             _ => fail!("encountered non-closure id: {}", closure_id)
         },
         _ => fail!("encountered non-expr id: {}", closure_id)
diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs
index 61a2e840730..f973b33ef2c 100644
--- a/src/librustc/middle/check_loop.rs
+++ b/src/librustc/middle/check_loop.rs
@@ -46,9 +46,9 @@ impl<'a> Visitor<Context> for CheckLoopVisitor<'a> {
                 self.visit_expr(&**e, cx);
                 self.visit_block(&**b, Loop);
             }
-            ast::ExprFnBlock(_, ref b) |
+            ast::ExprFnBlock(_, _, ref b) |
             ast::ExprProc(_, ref b) |
-            ast::ExprUnboxedFn(_, ref b) => {
+            ast::ExprUnboxedFn(_, _, ref b) => {
                 self.visit_block(&**b, Closure);
             }
             ast::ExprBreak(_) => self.require_loop("break", cx, e.span),
diff --git a/src/librustc/middle/freevars.rs b/src/librustc/middle/freevars.rs
index eda567f7d18..b092d45e039 100644
--- a/src/librustc/middle/freevars.rs
+++ b/src/librustc/middle/freevars.rs
@@ -17,14 +17,14 @@ use middle::def;
 use middle::mem_categorization::Typer;
 use middle::resolve;
 use middle::ty;
-use util::nodemap::{DefIdSet, NodeMap, NodeSet};
+use util::nodemap::{NodeMap, NodeSet};
 
+use syntax::ast;
 use syntax::codemap::Span;
-use syntax::{ast};
-use syntax::visit;
 use syntax::visit::Visitor;
+use syntax::visit;
 
-#[deriving(Show)]
+#[deriving(Clone, Decodable, Encodable, Show)]
 pub enum CaptureMode {
     /// Copy/move the value from this llvm ValueRef into the environment.
     CaptureByValue,
@@ -43,12 +43,13 @@ pub struct freevar_entry {
 
 pub type freevar_map = NodeMap<Vec<freevar_entry>>;
 
-pub type UnboxedClosureList = DefIdSet;
+pub type CaptureModeMap = NodeMap<CaptureMode>;
 
 struct CollectFreevarsVisitor<'a> {
     seen: NodeSet,
     refs: Vec<freevar_entry>,
     def_map: &'a resolve::DefMap,
+    capture_mode_map: &'a mut CaptureModeMap,
 }
 
 impl<'a> Visitor<int> for CollectFreevarsVisitor<'a> {
@@ -58,8 +59,27 @@ impl<'a> Visitor<int> for CollectFreevarsVisitor<'a> {
 
     fn visit_expr(&mut self, expr: &ast::Expr, depth: int) {
         match expr.node {
-            ast::ExprFnBlock(..) | ast::ExprProc(..) |
-            ast::ExprUnboxedFn(..) => {
+            ast::ExprProc(..) => {
+                self.capture_mode_map.insert(expr.id, CaptureByValue);
+                visit::walk_expr(self, expr, depth + 1)
+            }
+            ast::ExprFnBlock(_, _, _) => {
+                // NOTE(stage0): After snapshot, change to:
+                //
+                //let capture_mode = match capture_clause {
+                //    ast::CaptureByValue => CaptureByValue,
+                //    ast::CaptureByRef => CaptureByRef,
+                //};
+                let capture_mode = CaptureByRef;
+                self.capture_mode_map.insert(expr.id, capture_mode);
+                visit::walk_expr(self, expr, depth + 1)
+            }
+            ast::ExprUnboxedFn(capture_clause, _, _) => {
+                let capture_mode = match capture_clause {
+                    ast::CaptureByValue => CaptureByValue,
+                    ast::CaptureByRef => CaptureByRef,
+                };
+                self.capture_mode_map.insert(expr.id, capture_mode);
                 visit::walk_expr(self, expr, depth + 1)
             }
             ast::ExprPath(..) => {
@@ -91,8 +111,6 @@ impl<'a> Visitor<int> for CollectFreevarsVisitor<'a> {
             _ => visit::walk_expr(self, expr, depth)
         }
     }
-
-
 }
 
 // Searches through part of the AST for all references to locals or
@@ -100,26 +118,34 @@ impl<'a> Visitor<int> for CollectFreevarsVisitor<'a> {
 // Since we want to be able to collect upvars in some arbitrary piece
 // of the AST, we take a walker function that we invoke with a visitor
 // in order to start the search.
-fn collect_freevars(def_map: &resolve::DefMap, blk: &ast::Block) -> Vec<freevar_entry> {
+fn collect_freevars(def_map: &resolve::DefMap,
+                    blk: &ast::Block,
+                    capture_mode_map: &mut CaptureModeMap)
+                    -> Vec<freevar_entry> {
     let mut v = CollectFreevarsVisitor {
         seen: NodeSet::new(),
         refs: Vec::new(),
         def_map: def_map,
+        capture_mode_map: &mut *capture_mode_map,
     };
 
     v.visit_block(blk, 1);
+
     v.refs
 }
 
 struct AnnotateFreevarsVisitor<'a> {
     def_map: &'a resolve::DefMap,
     freevars: freevar_map,
+    capture_mode_map: CaptureModeMap,
 }
 
 impl<'a> Visitor<()> for AnnotateFreevarsVisitor<'a> {
     fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl,
                 blk: &ast::Block, s: Span, nid: ast::NodeId, _: ()) {
-        let vars = collect_freevars(self.def_map, blk);
+        let vars = collect_freevars(self.def_map,
+                                    blk,
+                                    &mut self.capture_mode_map);
         self.freevars.insert(nid, vars);
         visit::walk_fn(self, fk, fd, blk, s, ());
     }
@@ -131,14 +157,20 @@ impl<'a> Visitor<()> for AnnotateFreevarsVisitor<'a> {
 // node of interest rather than building up the free variables in
 // one pass. This could be improved upon if it turns out to matter.
 pub fn annotate_freevars(def_map: &resolve::DefMap, krate: &ast::Crate)
-                         -> freevar_map {
+                         -> (freevar_map, CaptureModeMap) {
     let mut visitor = AnnotateFreevarsVisitor {
         def_map: def_map,
         freevars: NodeMap::new(),
+        capture_mode_map: NodeMap::new(),
     };
     visit::walk_crate(&mut visitor, krate, ());
 
-    visitor.freevars
+    let AnnotateFreevarsVisitor {
+        freevars,
+        capture_mode_map,
+        ..
+    } = visitor;
+    (freevars, capture_mode_map)
 }
 
 pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[freevar_entry]| -> T) -> T {
@@ -148,10 +180,7 @@ pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[freevar_entry]|
     }
 }
 
-pub fn get_capture_mode<T: Typer>(tcx: &T, closure_expr_id: ast::NodeId) -> CaptureMode {
-    let fn_ty = tcx.node_ty(closure_expr_id).ok().expect("couldn't find closure ty?");
-    match ty::ty_closure_store(fn_ty) {
-        ty::RegionTraitStore(..) => CaptureByRef,
-        ty::UniqTraitStore => CaptureByValue
-    }
+pub fn get_capture_mode<T:Typer>(tcx: &T, closure_expr_id: ast::NodeId)
+                        -> CaptureMode {
+    tcx.capture_mode(closure_expr_id)
 }
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 1c31b671a94..1e484178788 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -965,9 +965,9 @@ impl<'a> Liveness<'a> {
               self.propagate_through_expr(&**e, succ)
           }
 
-          ExprFnBlock(_, ref blk) |
+          ExprFnBlock(_, _, ref blk) |
           ExprProc(_, ref blk) |
-          ExprUnboxedFn(_, ref blk) => {
+          ExprUnboxedFn(_, _, ref blk) => {
               debug!("{} is an ExprFnBlock, ExprProc, or ExprUnboxedFn",
                      expr_to_string(expr));
 
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 39a7b4aa3d6..6ad8bc0c1e9 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -63,6 +63,7 @@
 #![allow(non_camel_case_types)]
 
 use middle::def;
+use middle::freevars;
 use middle::ty;
 use middle::typeck;
 use util::nodemap::NodeMap;
@@ -270,6 +271,8 @@ pub trait Typer {
     fn is_method_call(&self, id: ast::NodeId) -> bool;
     fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<ast::NodeId>;
     fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow;
+    fn capture_mode(&self, closure_expr_id: ast::NodeId)
+                    -> freevars::CaptureMode;
 }
 
 impl MutabilityCategory {
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 95c04ad6607..1e4cbdbdb6e 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -5287,9 +5287,9 @@ impl<'a> Resolver<'a> {
                 visit::walk_expr(self, expr, ());
             }
 
-            ExprFnBlock(fn_decl, block) |
+            ExprFnBlock(_, fn_decl, block) |
             ExprProc(fn_decl, block) |
-            ExprUnboxedFn(fn_decl, block) => {
+            ExprUnboxedFn(_, fn_decl, block) => {
                 self.resolve_function(FunctionRibKind(expr.id, block.id),
                                       Some(fn_decl), NoTypeParameters,
                                       block);
diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs
index c4373a023cc..4a58a8bba99 100644
--- a/src/librustc/middle/save/mod.rs
+++ b/src/librustc/middle/save/mod.rs
@@ -1237,7 +1237,7 @@ impl<'l> Visitor<DxrVisitorEnv> for DxrVisitor<'l> {
                                             "Expected struct type, but not ty_struct"),
                 }
             },
-            ast::ExprFnBlock(decl, body) => {
+            ast::ExprFnBlock(_, decl, body) => {
                 if generated_code(body.span) {
                     return
                 }
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 35d32d99525..b7bb383ad3c 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -1306,7 +1306,9 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
         }
         Some(ast_map::NodeExpr(e)) => {
             match e.node {
-                ast::ExprFnBlock(_, blk) | ast::ExprProc(_, blk) | ast::ExprUnboxedFn(_, blk) => {
+                ast::ExprFnBlock(_, _, blk) |
+                ast::ExprProc(_, blk) |
+                ast::ExprUnboxedFn(_, _, blk) => {
                     let mut explicit = CheckForNestedReturnsVisitor { found: false };
                     let mut implicit = CheckForNestedReturnsVisitor { found: false };
                     visit::walk_expr(&mut explicit, &*e, false);
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 3b89c73b31d..8b36270ee54 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -18,6 +18,7 @@ use llvm::{ValueRef, BasicBlockRef, BuilderRef};
 use llvm::{True, False, Bool};
 use mc = middle::mem_categorization;
 use middle::def;
+use middle::freevars;
 use middle::lang_items::LangItem;
 use middle::subst;
 use middle::subst::Subst;
@@ -516,6 +517,11 @@ impl<'a> mc::Typer for Block<'a> {
     fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow {
         self.tcx().upvar_borrow_map.borrow().get_copy(&upvar_id)
     }
+
+    fn capture_mode(&self, closure_expr_id: ast::NodeId)
+                    -> freevars::CaptureMode {
+        self.tcx().capture_modes.borrow().get_copy(&closure_expr_id)
+    }
 }
 
 pub struct Result<'a> {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index c27b0bb8cb1..ad1c5bf6ef1 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -1150,9 +1150,9 @@ pub fn create_function_debug_context(cx: &CrateContext,
         }
         ast_map::NodeExpr(ref expr) => {
             match expr.node {
-                ast::ExprFnBlock(fn_decl, top_level_block) |
+                ast::ExprFnBlock(_, fn_decl, top_level_block) |
                 ast::ExprProc(fn_decl, top_level_block) |
-                ast::ExprUnboxedFn(fn_decl, top_level_block) => {
+                ast::ExprUnboxedFn(_, fn_decl, top_level_block) => {
                     let name = format!("fn{}", token::gensym("fn"));
                     let name = token::str_to_ident(name.as_slice());
                     (name, fn_decl,
@@ -3618,9 +3618,9 @@ fn populate_scope_map(cx: &CrateContext,
                 })
             }
 
-            ast::ExprFnBlock(ref decl, ref block) |
+            ast::ExprFnBlock(_, ref decl, ref block) |
             ast::ExprProc(ref decl, ref block) |
-            ast::ExprUnboxedFn(ref decl, ref block) => {
+            ast::ExprUnboxedFn(_, ref decl, ref block) => {
                 with_new_scope(cx,
                                block.span,
                                scope_stack,
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 1dad6e3cb18..7cd2bd631f0 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -782,7 +782,7 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>,
         ast::ExprVec(..) | ast::ExprRepeat(..) => {
             tvec::trans_fixed_vstore(bcx, expr, expr, dest)
         }
-        ast::ExprFnBlock(ref decl, ref body) |
+        ast::ExprFnBlock(_, ref decl, ref body) |
         ast::ExprProc(ref decl, ref body) => {
             let expr_ty = expr_ty(bcx, expr);
             let store = ty::ty_closure_store(expr_ty);
@@ -790,7 +790,7 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>,
                    expr_to_string(expr), expr_ty.repr(tcx));
             closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest)
         }
-        ast::ExprUnboxedFn(decl, body) => {
+        ast::ExprUnboxedFn(_, decl, body) => {
             closure::trans_unboxed_closure(bcx, &*decl, &*body, expr.id, dest)
         }
         ast::ExprCall(ref f, ref args) => {
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index a4588da1bd7..3346b475267 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -18,14 +18,15 @@ use lint;
 use middle::const_eval;
 use middle::def;
 use middle::dependency_format;
+use middle::freevars::CaptureModeMap;
+use middle::freevars;
 use middle::lang_items::{FnMutTraitLangItem, OpaqueStructLangItem};
 use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem};
-use middle::freevars;
 use middle::resolve;
 use middle::resolve_lifetime;
-use middle::subst;
-use middle::subst::{Subst, Substs, VecPerParamSpace};
 use middle::stability;
+use middle::subst::{Subst, Substs, VecPerParamSpace};
+use middle::subst;
 use middle::ty;
 use middle::typeck;
 use middle::typeck::MethodCall;
@@ -384,6 +385,9 @@ pub struct ctxt {
 
     /// 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>,
 }
 
 pub enum tbox_flag {
@@ -1057,6 +1061,7 @@ pub fn mk_ctxt(s: Session,
                named_region_map: resolve_lifetime::NamedRegionMap,
                map: ast_map::Map,
                freevars: freevars::freevar_map,
+               capture_modes: freevars::CaptureModeMap,
                region_maps: middle::region::RegionMaps,
                lang_items: middle::lang_items::LanguageItems,
                stability: stability::Index)
@@ -1115,7 +1120,8 @@ pub fn mk_ctxt(s: Session,
         unboxed_closure_types: RefCell::new(DefIdMap::new()),
         node_lint_levels: RefCell::new(HashMap::new()),
         transmute_restrictions: RefCell::new(Vec::new()),
-        stability: RefCell::new(stability)
+        stability: RefCell::new(stability),
+        capture_modes: RefCell::new(capture_modes),
     }
 }
 
@@ -4862,6 +4868,11 @@ impl mc::Typer for ty::ctxt {
     fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow {
         self.upvar_borrow_map.borrow().get_copy(&upvar_id)
     }
+
+    fn capture_mode(&self, closure_expr_id: ast::NodeId)
+                    -> freevars::CaptureMode {
+        self.capture_modes.borrow().get_copy(&closure_expr_id)
+    }
 }
 
 /// The category of explicit self.
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index aa38ff68f24..33acb521780 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -3390,7 +3390,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
       ast::ExprMatch(ref discrim, ref arms) => {
         _match::check_match(fcx, expr, &**discrim, arms.as_slice());
       }
-      ast::ExprFnBlock(ref decl, ref body) => {
+      ast::ExprFnBlock(_, ref decl, ref body) => {
         let region = astconv::opt_ast_region_to_region(fcx,
                                                        fcx.infcx(),
                                                        expr.span,
@@ -3402,7 +3402,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                       body.clone(),
                       expected);
       }
-      ast::ExprUnboxedFn(ref decl, ref body) => {
+      ast::ExprUnboxedFn(_, ref decl, ref body) => {
         check_unboxed_closure(fcx,
                               expr,
                               &**decl,
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index d0431de81a3..47f5c45dde2 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -290,6 +290,11 @@ impl<'fcx> mc::Typer for Rcx<'fcx> {
     fn upvar_borrow(&self, id: ty::UpvarId) -> ty::UpvarBorrow {
         self.fcx.inh.upvar_borrow_map.borrow().get_copy(&id)
     }
+
+    fn capture_mode(&self, closure_expr_id: ast::NodeId)
+                    -> freevars::CaptureMode {
+        self.tcx().capture_modes.borrow().get_copy(&closure_expr_id)
+    }
 }
 
 pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) {
@@ -587,9 +592,9 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
             visit::walk_expr(rcx, expr, ());
         }
 
-        ast::ExprFnBlock(_, ref body) |
+        ast::ExprFnBlock(_, _, ref body) |
         ast::ExprProc(_, ref body) |
-        ast::ExprUnboxedFn(_, ref body) => {
+        ast::ExprUnboxedFn(_, _, ref body) => {
             check_expr_fn_block(rcx, expr, &**body);
         }
 
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index d94ac103ceb..6e6ebd181fb 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -132,9 +132,9 @@ impl<'cx> Visitor<()> for WritebackCx<'cx> {
                                     MethodCall::expr(e.id));
 
         match e.node {
-            ast::ExprFnBlock(ref decl, _) |
+            ast::ExprFnBlock(_, ref decl, _) |
             ast::ExprProc(ref decl, _) |
-            ast::ExprUnboxedFn(ref decl, _) => {
+            ast::ExprUnboxedFn(_, ref decl, _) => {
                 for input in decl.inputs.iter() {
                     let _ = self.visit_node_id(ResolvingExpr(e.span),
                                                input.id);
diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs
index 637af96b632..dd00fc62079 100644
--- a/src/librustc/middle/typeck/infer/test.rs
+++ b/src/librustc/middle/typeck/infer/test.rs
@@ -124,12 +124,20 @@ fn test_env(_test_name: &str,
     let lang_items = lang_items::collect_language_items(&krate, &sess);
     let resolve::CrateMap { def_map: def_map, .. } =
         resolve::resolve_crate(&sess, &lang_items, &krate);
-    let freevars_map = freevars::annotate_freevars(&def_map, &krate);
+    let (freevars_map, captures_map) = freevars::annotate_freevars(&def_map,
+                                                                   &krate);
     let named_region_map = resolve_lifetime::krate(&sess, &krate);
     let region_map = region::resolve_crate(&sess, &krate);
     let stability_index = stability::Index::build(&krate);
-    let tcx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
-                          freevars_map, region_map, lang_items, stability_index);
+    let tcx = ty::mk_ctxt(sess,
+                          def_map,
+                          named_region_map,
+                          ast_map,
+                          freevars_map,
+                          captures_map,
+                          region_map,
+                          lang_items,
+                          stability_index);
     let infcx = infer::new_infer_ctxt(&tcx);
     let env = Env {krate: krate,
                    tcx: &tcx,
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 629b21875c9..2f2347d8db8 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -520,9 +520,9 @@ pub enum Expr_ {
     // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
     ExprLoop(P<Block>, Option<Ident>),
     ExprMatch(Gc<Expr>, Vec<Arm>),
-    ExprFnBlock(P<FnDecl>, P<Block>),
+    ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>),
     ExprProc(P<FnDecl>, P<Block>),
-    ExprUnboxedFn(P<FnDecl>, P<Block>),
+    ExprUnboxedFn(CaptureClause, P<FnDecl>, P<Block>),
     ExprBlock(P<Block>),
 
     ExprAssign(Gc<Expr>, Gc<Expr>),
@@ -553,6 +553,12 @@ pub enum Expr_ {
     ExprParen(Gc<Expr>)
 }
 
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub enum CaptureClause {
+    CaptureByValue,
+    CaptureByRef,
+}
+
 /// When the main rust parser encounters a syntax-extension invocation, it
 /// parses the arguments to the invocation as a token-tree. This is a very
 /// loose structure, such that all sorts of different AST-fragments can
diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs
index a522f805543..83af390c4ed 100644
--- a/src/libsyntax/ast_map/blocks.rs
+++ b/src/libsyntax/ast_map/blocks.rs
@@ -206,7 +206,7 @@ impl FnLikeNode {
             },
             ast_map::NodeMethod(ref m) => method(&**m),
             ast_map::NodeExpr(ref e) => match e.node {
-                ast::ExprFnBlock(ref decl, ref block) =>
+                ast::ExprFnBlock(_, ref decl, ref block) =>
                     closure(ClosureParts::new(*decl, *block, e.id, e.span)),
                 ast::ExprProc(ref decl, ref block) =>
                     closure(ClosureParts::new(*decl, *block, e.id, e.span)),
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 0e687c02c1d..f7eddca4b7a 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -876,14 +876,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
 
     fn lambda_fn_decl(&self, span: Span,
                       fn_decl: P<ast::FnDecl>, blk: P<ast::Block>) -> Gc<ast::Expr> {
-        self.expr(span, ast::ExprFnBlock(fn_decl, blk))
+        self.expr(span, ast::ExprFnBlock(ast::CaptureByRef, fn_decl, blk))
     }
     fn lambda(&self, span: Span, ids: Vec<ast::Ident> , blk: P<ast::Block>) -> Gc<ast::Expr> {
         let fn_decl = self.fn_decl(
             ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(),
             self.ty_infer(span));
 
-        self.expr(span, ast::ExprFnBlock(fn_decl, blk))
+        self.expr(span, ast::ExprFnBlock(ast::CaptureByRef, fn_decl, blk))
     }
     fn lambda0(&self, span: Span, blk: P<ast::Block>) -> Gc<ast::Expr> {
         self.lambda(span, Vec::new(), blk)
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 808532d55bb..d918b28d4dc 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -74,10 +74,12 @@ fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
             fld.cx.expr(e.span, ast::ExprForLoop(pat, head, body, opt_ident))
         }
 
-        ast::ExprFnBlock(fn_decl, block) => {
+        ast::ExprFnBlock(capture_clause, fn_decl, block) => {
             let (rewritten_fn_decl, rewritten_block)
                 = expand_and_rename_fn_decl_and_block(&*fn_decl, block, fld);
-            let new_node = ast::ExprFnBlock(rewritten_fn_decl, rewritten_block);
+            let new_node = ast::ExprFnBlock(capture_clause,
+                                            rewritten_fn_decl,
+                                            rewritten_block);
             box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
         }
 
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 80325c64349..02f1f4646a4 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1094,16 +1094,18 @@ pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> {
             ExprMatch(folder.fold_expr(expr),
                       arms.iter().map(|x| folder.fold_arm(x)).collect())
         }
-        ExprFnBlock(ref decl, ref body) => {
-            ExprFnBlock(folder.fold_fn_decl(&**decl),
+        ExprFnBlock(capture_clause, ref decl, ref body) => {
+            ExprFnBlock(capture_clause,
+                        folder.fold_fn_decl(&**decl),
                         folder.fold_block(body.clone()))
         }
         ExprProc(ref decl, ref body) => {
             ExprProc(folder.fold_fn_decl(&**decl),
                      folder.fold_block(body.clone()))
         }
-        ExprUnboxedFn(ref decl, ref body) => {
-            ExprUnboxedFn(folder.fold_fn_decl(&**decl),
+        ExprUnboxedFn(capture_clause, ref decl, ref body) => {
+            ExprUnboxedFn(capture_clause,
+                          folder.fold_fn_decl(&**decl),
                           folder.fold_block(*body))
         }
         ExprBlock(ref blk) => ExprBlock(folder.fold_block(*blk)),
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 08d96f5b008..f0920603ad1 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -17,6 +17,7 @@ use ast::{Provided, Public, FnStyle};
 use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
 use ast::{BiBitAnd, BiBitOr, BiBitXor, Block};
 use ast::{BlockCheckMode, UnBox};
+use ast::{CaptureByRef, CaptureByValue, CaptureClause};
 use ast::{Crate, CrateConfig, Decl, DeclItem};
 use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf};
 use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
@@ -1985,7 +1986,7 @@ impl<'a> Parser<'a> {
                                     ExprBlock(blk));
             },
             token::BINOP(token::OR) |  token::OROR => {
-                return self.parse_lambda_expr();
+                return self.parse_lambda_expr(CaptureByValue);
             },
             // FIXME #13626: Should be able to stick in
             // token::SELF_KEYWORD_NAME
@@ -2036,6 +2037,9 @@ impl<'a> Parser<'a> {
                 hi = self.last_span.hi;
             },
             _ => {
+                if self.eat_keyword(keywords::Ref) {
+                    return self.parse_lambda_expr(CaptureByRef);
+                }
                 if self.eat_keyword(keywords::Proc) {
                     let decl = self.parse_proc_decl();
                     let body = self.parse_expr();
@@ -2696,7 +2700,8 @@ impl<'a> Parser<'a> {
     }
 
     // `|args| expr`
-    pub fn parse_lambda_expr(&mut self) -> Gc<Expr> {
+    pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause)
+                             -> Gc<Expr> {
         let lo = self.span.lo;
         let (decl, is_unboxed) = self.parse_fn_block_decl();
         let body = self.parse_expr();
@@ -2710,9 +2715,13 @@ impl<'a> Parser<'a> {
         });
 
         if is_unboxed {
-            self.mk_expr(lo, body.span.hi, ExprUnboxedFn(decl, fakeblock))
+            self.mk_expr(lo,
+                         body.span.hi,
+                         ExprUnboxedFn(capture_clause, decl, fakeblock))
         } else {
-            self.mk_expr(lo, body.span.hi, ExprFnBlock(decl, fakeblock))
+            self.mk_expr(lo,
+                         body.span.hi,
+                         ExprFnBlock(capture_clause, decl, fakeblock))
         }
     }
 
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 9d4b7343c8a..3ef9d96e3b6 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1437,7 +1437,9 @@ impl<'a> State<'a> {
                 }
                 try!(self.bclose_(expr.span, indent_unit));
             }
-            ast::ExprFnBlock(ref decl, ref body) => {
+            ast::ExprFnBlock(capture_clause, ref decl, ref body) => {
+                try!(self.print_capture_clause(capture_clause));
+
                 // in do/for blocks we don't want to show an empty
                 // argument list, but at this point we don't know which
                 // we are inside.
@@ -1467,7 +1469,9 @@ impl<'a> State<'a> {
                 // empty box to satisfy the close.
                 try!(self.ibox(0));
             }
-            ast::ExprUnboxedFn(ref decl, ref body) => {
+            ast::ExprUnboxedFn(capture_clause, ref decl, ref body) => {
+                try!(self.print_capture_clause(capture_clause));
+
                 // in do/for blocks we don't want to show an empty
                 // argument list, but at this point we don't know which
                 // we are inside.
@@ -2030,6 +2034,14 @@ impl<'a> State<'a> {
         self.maybe_print_comment(decl.output.span.lo)
     }
 
+    pub fn print_capture_clause(&mut self, capture_clause: ast::CaptureClause)
+                                -> IoResult<()> {
+        match capture_clause {
+            ast::CaptureByValue => Ok(()),
+            ast::CaptureByRef => self.word_space("ref"),
+        }
+    }
+
     pub fn print_proc_args(&mut self, decl: &ast::FnDecl) -> IoResult<()> {
         try!(word(&mut self.s, "proc"));
         try!(word(&mut self.s, "("));
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 647e81db1f1..372cee9ad09 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -787,7 +787,7 @@ pub fn walk_expr<E: Clone, V: Visitor<E>>(visitor: &mut V, expression: &Expr, en
                 visitor.visit_arm(arm, env.clone())
             }
         }
-        ExprFnBlock(ref function_declaration, ref body) => {
+        ExprFnBlock(_, ref function_declaration, ref body) => {
             visitor.visit_fn(&FkFnBlock,
                              &**function_declaration,
                              &**body,
@@ -795,7 +795,7 @@ pub fn walk_expr<E: Clone, V: Visitor<E>>(visitor: &mut V, expression: &Expr, en
                              expression.id,
                              env.clone())
         }
-        ExprUnboxedFn(ref function_declaration, ref body) => {
+        ExprUnboxedFn(_, ref function_declaration, ref body) => {
             visitor.visit_fn(&FkFnBlock,
                              &**function_declaration,
                              &**body,
diff --git a/src/test/run-pass/capture-clauses-boxed-closures.rs b/src/test/run-pass/capture-clauses-boxed-closures.rs
new file mode 100644
index 00000000000..c88b44925d0
--- /dev/null
+++ b/src/test/run-pass/capture-clauses-boxed-closures.rs
@@ -0,0 +1,23 @@
+// 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.
+
+fn each<T>(x: &[T], f: |&T|) {
+    for val in x.iter() {
+        f(val)
+    }
+}
+
+fn main() {
+    let mut sum = 0u;
+    let elems = [ 1u, 2, 3, 4, 5 ];
+    each(elems, ref |val| sum += *val);
+    assert_eq!(sum, 15);
+}
+
diff --git a/src/test/run-pass/capture-clauses-unboxed-closures.rs b/src/test/run-pass/capture-clauses-unboxed-closures.rs
new file mode 100644
index 00000000000..99e6d6e7a4f
--- /dev/null
+++ b/src/test/run-pass/capture-clauses-unboxed-closures.rs
@@ -0,0 +1,29 @@
+// 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.
+
+// ignore-test
+//
+// This is ignored because it depends on #16122.
+
+#![feature(overloaded_calls, unboxed_closures)]
+
+fn each<'a,T,F:|&mut: &'a T|>(x: &'a [T], mut f: F) {
+    for val in x.iter() {
+        f(val)
+    }
+}
+
+fn main() {
+    let mut sum = 0u;
+    let elems = [ 1u, 2, 3, 4, 5 ];
+    each(elems, ref |&mut: val: &uint| sum += *val);
+    assert_eq!(sum, 15);
+}
+