about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-08-14 16:36:19 +0000
committerbors <bors@rust-lang.org>2014-08-14 16:36:19 +0000
commit404978ea722c0257cc763540c93243e8a21f82ed (patch)
treec6106879c326924dc3d39ce71c0a797901bafe04
parent56b86aaf35d96ec8cf8555205426eaed62427963 (diff)
parent8d272321417df3a954802e42a66adda87ade5a49 (diff)
downloadrust-404978ea722c0257cc763540c93243e8a21f82ed.tar.gz
rust-404978ea722c0257cc763540c93243e8a21f82ed.zip
auto merge of #16122 : pcwalton/rust/lifetimes-in-unboxed-closures, r=pnkfelix
This patch primarily does two things: (1) it prevents lifetimes from
leaking out of unboxed closures; (2) it allows unboxed closure type
notation, call notation, and construction notation to construct closures
matching any of the three traits.

This breaks code that looked like:

    let mut f;
    {
        let x = &5i;
        f = |&mut:| *x + 10;
    }

Change this code to avoid having a reference escape. For example:

    {
        let x = &5i;
        let mut f; // <-- move here to avoid dangling reference
        f = |&mut:| *x + 10;
    }

I believe this is enough to consider unboxed closures essentially
implemented. Further issues (for example, higher-rank lifetimes) should
be filed as followups.

Closes #14449.

[breaking-change]

r? @pnkfelix
-rw-r--r--src/librustc/front/feature_gate.rs2
-rw-r--r--src/librustc/metadata/common.rs12
-rw-r--r--src/librustc/metadata/encoder.rs21
-rw-r--r--src/librustc/metadata/tydecode.rs3
-rw-r--r--src/librustc/metadata/tyencode.rs3
-rw-r--r--src/librustc/middle/astencode.rs81
-rw-r--r--src/librustc/middle/borrowck/mod.rs5
-rw-r--r--src/librustc/middle/check_loop.rs2
-rw-r--r--src/librustc/middle/freevars.rs2
-rw-r--r--src/librustc/middle/kind.rs2
-rw-r--r--src/librustc/middle/liveness.rs2
-rw-r--r--src/librustc/middle/mem_categorization.rs17
-rw-r--r--src/librustc/middle/resolve.rs2
-rw-r--r--src/librustc/middle/trans/adt.rs2
-rw-r--r--src/librustc/middle/trans/base.rs62
-rw-r--r--src/librustc/middle/trans/closure.rs63
-rw-r--r--src/librustc/middle/trans/common.rs7
-rw-r--r--src/librustc/middle/trans/debuginfo.rs6
-rw-r--r--src/librustc/middle/trans/expr.rs2
-rw-r--r--src/librustc/middle/trans/meth.rs2
-rw-r--r--src/librustc/middle/trans/type_of.rs2
-rw-r--r--src/librustc/middle/ty.rs62
-rw-r--r--src/librustc/middle/ty_fold.rs4
-rw-r--r--src/librustc/middle/typeck/astconv.rs28
-rw-r--r--src/librustc/middle/typeck/check/method.rs68
-rw-r--r--src/librustc/middle/typeck/check/mod.rs37
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs20
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs49
-rw-r--r--src/librustc/middle/typeck/check/writeback.rs28
-rw-r--r--src/librustc/middle/typeck/coherence.rs6
-rw-r--r--src/librustc/middle/typeck/infer/combine.rs6
-rw-r--r--src/librustc/middle/typeck/variance.rs7
-rw-r--r--src/libsyntax/ast.rs10
-rw-r--r--src/libsyntax/fold.rs5
-rw-r--r--src/libsyntax/parse/parser.rs167
-rw-r--r--src/libsyntax/print/pprust.rs67
-rw-r--r--src/libsyntax/visit.rs2
-rw-r--r--src/test/compile-fail/borrowck-unboxed-closures.rs29
-rw-r--r--src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs24
-rw-r--r--src/test/compile-fail/unboxed-closures-wrong-trait.rs22
-rw-r--r--src/test/run-pass/unboxed-closures-all-traits.rs31
-rw-r--r--src/test/run-pass/unboxed-closures-drop.rs127
-rw-r--r--src/test/run-pass/unboxed-closures-single-word-env.rs34
43 files changed, 835 insertions, 298 deletions
diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs
index 99855c7345c..fdfbdea245b 100644
--- a/src/librustc/front/feature_gate.rs
+++ b/src/librustc/front/feature_gate.rs
@@ -314,7 +314,7 @@ impl<'a> Visitor<()> for Context<'a> {
 
             },
             ast::TyBox(_) => { self.gate_box(t.span); }
-            ast::TyUnboxedFn(_) => {
+            ast::TyUnboxedFn(..) => {
                 self.gate_feature("unboxed_closure_sugar",
                                   t.span,
                                   "unboxed closure trait sugar is experimental");
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 633ad829349..7d0a21b2f8e 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -139,7 +139,7 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f
     tag_table_adjustments = 0x51,
     tag_table_moves_map = 0x52,
     tag_table_capture_map = 0x53,
-    tag_table_unboxed_closure_type = 0x54,
+    tag_table_unboxed_closures = 0x54,
     tag_table_upvar_borrow_map = 0x55,
     tag_table_capture_modes = 0x56,
 }
@@ -229,9 +229,11 @@ pub static tag_region_param_def_index: uint = 0x94;
 pub static tag_unboxed_closures: uint = 0x95;
 pub static tag_unboxed_closure: uint = 0x96;
 pub static tag_unboxed_closure_type: uint = 0x97;
+pub static tag_unboxed_closure_kind: uint = 0x98;
 
-pub static tag_struct_fields: uint = 0x98;
-pub static tag_struct_field: uint = 0x99;
-pub static tag_struct_field_id: uint = 0x9a;
+pub static tag_struct_fields: uint = 0x99;
+pub static tag_struct_field: uint = 0x9a;
+pub static tag_struct_field_id: uint = 0x9b;
+
+pub static tag_attribute_is_sugared_doc: uint = 0x9c;
 
-pub static tag_attribute_is_sugared_doc: uint = 0x9b;
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 9b31d3a1b64..1b47227fea4 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -630,6 +630,18 @@ fn encode_visibility(rbml_w: &mut Encoder, visibility: Visibility) {
     rbml_w.end_tag();
 }
 
+fn encode_unboxed_closure_kind(rbml_w: &mut Encoder,
+                               kind: ty::UnboxedClosureKind) {
+    rbml_w.start_tag(tag_unboxed_closure_kind);
+    let ch = match kind {
+        ty::FnUnboxedClosureKind => 'f',
+        ty::FnMutUnboxedClosureKind => 'm',
+        ty::FnOnceUnboxedClosureKind => 'o',
+    };
+    rbml_w.wr_str(ch.to_string().as_slice());
+    rbml_w.end_tag();
+}
+
 fn encode_explicit_self(rbml_w: &mut Encoder,
                         explicit_self: &ty::ExplicitSelfCategory) {
     rbml_w.start_tag(tag_item_trait_method_explicit_self);
@@ -1629,8 +1641,10 @@ fn encode_unboxed_closures<'a>(
                            ecx: &'a EncodeContext,
                            rbml_w: &'a mut Encoder) {
     rbml_w.start_tag(tag_unboxed_closures);
-    for (unboxed_closure_id, unboxed_closure_type) in
-            ecx.tcx.unboxed_closure_types.borrow().iter() {
+    for (unboxed_closure_id, unboxed_closure) in ecx.tcx
+                                                    .unboxed_closures
+                                                    .borrow()
+                                                    .iter() {
         if unboxed_closure_id.krate != LOCAL_CRATE {
             continue
         }
@@ -1638,8 +1652,9 @@ fn encode_unboxed_closures<'a>(
         rbml_w.start_tag(tag_unboxed_closure);
         encode_def_id(rbml_w, *unboxed_closure_id);
         rbml_w.start_tag(tag_unboxed_closure_type);
-        write_closure_type(ecx, rbml_w, unboxed_closure_type);
+        write_closure_type(ecx, rbml_w, &unboxed_closure.closure_type);
         rbml_w.end_tag();
+        encode_unboxed_closure_kind(rbml_w, unboxed_closure.kind);
         rbml_w.end_tag();
     }
     rbml_w.end_tag();
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index fed23185c5d..e6e7d8bf8d1 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -432,7 +432,8 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t {
       }
       'k' => {
           let did = parse_def(st, NominalType, |x,y| conv(x,y));
-          return ty::mk_unboxed_closure(st.tcx, did);
+          let region = parse_region(st, conv);
+          return ty::mk_unboxed_closure(st.tcx, did, region);
       }
       'e' => {
           return ty::mk_err();
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index ba865c7ab04..fc5e267aa90 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -285,8 +285,9 @@ fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) {
             enc_substs(w, cx, substs);
             mywrite!(w, "]");
         }
-        ty::ty_unboxed_closure(def) => {
+        ty::ty_unboxed_closure(def, region) => {
             mywrite!(w, "k{}", (cx.ds)(def));
+            enc_region(w, cx, region);
         }
         ty::ty_err => {
             mywrite!(w, "e");
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index e31af8b58b9..b1ce2ece4af 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -689,10 +689,35 @@ pub fn encode_vtable_param_res(ecx: &e::EncodeContext,
     }).unwrap()
 }
 
+pub fn encode_unboxed_closure_kind(ebml_w: &mut Encoder,
+                                   kind: ty::UnboxedClosureKind) {
+    ebml_w.emit_enum("UnboxedClosureKind", |ebml_w| {
+        match kind {
+            ty::FnUnboxedClosureKind => {
+                ebml_w.emit_enum_variant("FnUnboxedClosureKind", 0, 3, |_| {
+                    Ok(())
+                })
+            }
+            ty::FnMutUnboxedClosureKind => {
+                ebml_w.emit_enum_variant("FnMutUnboxedClosureKind", 1, 3, |_| {
+                    Ok(())
+                })
+            }
+            ty::FnOnceUnboxedClosureKind => {
+                ebml_w.emit_enum_variant("FnOnceUnboxedClosureKind",
+                                         2,
+                                         3,
+                                         |_| {
+                    Ok(())
+                })
+            }
+        }
+    }).unwrap()
+}
 
 pub fn encode_vtable_origin(ecx: &e::EncodeContext,
-                        rbml_w: &mut Encoder,
-                        vtable_origin: &typeck::vtable_origin) {
+                            rbml_w: &mut Encoder,
+                            vtable_origin: &typeck::vtable_origin) {
     rbml_w.emit_enum("vtable_origin", |rbml_w| {
         match *vtable_origin {
           typeck::vtable_static(def_id, ref substs, ref vtable_res) => {
@@ -1210,14 +1235,15 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
         })
     }
 
-    for unboxed_closure_type in tcx.unboxed_closure_types
-                                   .borrow()
-                                   .find(&ast_util::local_def(id))
-                                   .iter() {
-        rbml_w.tag(c::tag_table_unboxed_closure_type, |rbml_w| {
+    for unboxed_closure in tcx.unboxed_closures
+                              .borrow()
+                              .find(&ast_util::local_def(id))
+                              .iter() {
+        rbml_w.tag(c::tag_table_unboxed_closures, |rbml_w| {
             rbml_w.id(id);
             rbml_w.tag(c::tag_table_val, |rbml_w| {
-                rbml_w.emit_closure_type(ecx, *unboxed_closure_type)
+                rbml_w.emit_closure_type(ecx, &unboxed_closure.closure_type);
+                encode_unboxed_closure_kind(rbml_w, unboxed_closure.kind)
             })
         })
     }
@@ -1244,8 +1270,8 @@ trait rbml_decoder_decoder_helpers {
                      -> ty::Polytype;
     fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> subst::Substs;
     fn read_auto_adjustment(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoAdjustment;
-    fn read_unboxed_closure_type(&mut self, xcx: &ExtendedDecodeContext)
-                                 -> ty::ClosureTy;
+    fn read_unboxed_closure(&mut self, xcx: &ExtendedDecodeContext)
+                            -> ty::UnboxedClosure;
     fn convert_def_id(&mut self,
                       xcx: &ExtendedDecodeContext,
                       source: DefIdSource,
@@ -1418,16 +1444,33 @@ impl<'a> rbml_decoder_decoder_helpers for reader::Decoder<'a> {
         }).unwrap()
     }
 
-    fn read_unboxed_closure_type(&mut self, xcx: &ExtendedDecodeContext)
-                                 -> ty::ClosureTy {
-        self.read_opaque(|this, doc| {
+    fn read_unboxed_closure(&mut self, xcx: &ExtendedDecodeContext)
+                            -> ty::UnboxedClosure {
+        let closure_type = self.read_opaque(|this, doc| {
             Ok(tydecode::parse_ty_closure_data(
                 doc.data,
                 xcx.dcx.cdata.cnum,
                 doc.start,
                 xcx.dcx.tcx,
                 |s, a| this.convert_def_id(xcx, s, a)))
-        }).unwrap()
+        }).unwrap();
+        let variants = [
+            "FnUnboxedClosureKind",
+            "FnMutUnboxedClosureKind",
+            "FnOnceUnboxedClosureKind"
+        ];
+        let kind = self.read_enum_variant(variants, |_, i| {
+            Ok(match i {
+                0 => ty::FnUnboxedClosureKind,
+                1 => ty::FnMutUnboxedClosureKind,
+                2 => ty::FnOnceUnboxedClosureKind,
+                _ => fail!("bad enum variant for ty::UnboxedClosureKind"),
+            })
+        }).unwrap();
+        ty::UnboxedClosure {
+            closure_type: closure_type,
+            kind: kind,
+        }
     }
 
     fn convert_def_id(&mut self,
@@ -1566,14 +1609,14 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext,
                         let adj: ty::AutoAdjustment = val_dsr.read_auto_adjustment(xcx);
                         dcx.tcx.adjustments.borrow_mut().insert(id, adj);
                     }
-                    c::tag_table_unboxed_closure_type => {
-                        let unboxed_closure_type =
-                            val_dsr.read_unboxed_closure_type(xcx);
+                    c::tag_table_unboxed_closures => {
+                        let unboxed_closure =
+                            val_dsr.read_unboxed_closure(xcx);
                         dcx.tcx
-                           .unboxed_closure_types
+                           .unboxed_closures
                            .borrow_mut()
                            .insert(ast_util::local_def(id),
-                                   unboxed_closure_type);
+                                   unboxed_closure);
                     }
                     _ => {
                         xcx.dcx.tcx.sess.bug(
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index fd1369439c9..77dd7ed1c42 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -290,8 +290,9 @@ pub fn closure_to_block(closure_id: ast::NodeId,
                     tcx: &ty::ctxt) -> ast::NodeId {
     match tcx.map.get(closure_id) {
         ast_map::NodeExpr(expr) => match expr.node {
-            ast::ExprProc(_decl, block) |
-            ast::ExprFnBlock(_, _decl, block) => { block.id }
+            ast::ExprProc(_, block) |
+            ast::ExprFnBlock(_, _, block) |
+            ast::ExprUnboxedFn(_, _, _, 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 f973b33ef2c..12841fb20d2 100644
--- a/src/librustc/middle/check_loop.rs
+++ b/src/librustc/middle/check_loop.rs
@@ -48,7 +48,7 @@ impl<'a> Visitor<Context> for CheckLoopVisitor<'a> {
             }
             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 b092d45e039..1a2d21baa21 100644
--- a/src/librustc/middle/freevars.rs
+++ b/src/librustc/middle/freevars.rs
@@ -74,7 +74,7 @@ impl<'a> Visitor<int> for CollectFreevarsVisitor<'a> {
                 self.capture_mode_map.insert(expr.id, capture_mode);
                 visit::walk_expr(self, expr, depth + 1)
             }
-            ast::ExprUnboxedFn(capture_clause, _, _) => {
+            ast::ExprUnboxedFn(capture_clause, _, _, _) => {
                 let capture_mode = match capture_clause {
                     ast::CaptureByValue => CaptureByValue,
                     ast::CaptureByRef => CaptureByRef,
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index 83f4fbaed67..17b2c69f453 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -225,7 +225,7 @@ fn with_appropriate_checker(cx: &Context,
             b(check_for_bare)
         }
 
-        ty::ty_unboxed_closure(_) => {}
+        ty::ty_unboxed_closure(..) => {}
 
         ref s => {
             cx.tcx.sess.bug(format!("expect fn type in kind checker, not \
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 1e484178788..baac68904d5 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -967,7 +967,7 @@ impl<'a> Liveness<'a> {
 
           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 6ad8bc0c1e9..ef1e0515156 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -66,7 +66,7 @@ use middle::def;
 use middle::freevars;
 use middle::ty;
 use middle::typeck;
-use util::nodemap::NodeMap;
+use util::nodemap::{DefIdMap, NodeMap};
 use util::ppaux::{ty_to_string, Repr};
 
 use syntax::ast::{MutImmutable, MutMutable};
@@ -273,6 +273,8 @@ pub trait Typer {
     fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow;
     fn capture_mode(&self, closure_expr_id: ast::NodeId)
                     -> freevars::CaptureMode;
+    fn unboxed_closures<'a>(&'a self)
+                        -> &'a RefCell<DefIdMap<ty::UnboxedClosure>>;
 }
 
 impl MutabilityCategory {
@@ -598,13 +600,22 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
                           }))
                       }
                   }
-                  ty::ty_unboxed_closure(_) => {
+                  ty::ty_unboxed_closure(closure_id, _) => {
+                      let unboxed_closures = self.typer
+                                                 .unboxed_closures()
+                                                 .borrow();
+                      let kind = unboxed_closures.get(&closure_id).kind;
+                      let onceness = match kind {
+                          ty::FnUnboxedClosureKind |
+                          ty::FnMutUnboxedClosureKind => ast::Many,
+                          ty::FnOnceUnboxedClosureKind => ast::Once,
+                      };
                       Ok(Rc::new(cmt_ {
                           id: id,
                           span: span,
                           cat: cat_copied_upvar(CopiedUpvar {
                               upvar_id: var_id,
-                              onceness: ast::Many,
+                              onceness: onceness,
                               capturing_proc: fn_node_id,
                           }),
                           mutbl: MutabilityCategory::from_def(&def),
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 1e4cbdbdb6e..13a29842fcb 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -5289,7 +5289,7 @@ impl<'a> Resolver<'a> {
 
             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/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index 9ec0407b5c3..9c68631baa4 100644
--- a/src/librustc/middle/trans/adt.rs
+++ b/src/librustc/middle/trans/adt.rs
@@ -170,7 +170,7 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
 
             return Univariant(mk_struct(cx, ftys.as_slice(), packed), dtor)
         }
-        ty::ty_unboxed_closure(def_id) => {
+        ty::ty_unboxed_closure(def_id, _) => {
             let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id);
             let upvar_types = upvars.iter().map(|u| u.ty).collect::<Vec<_>>();
             return Univariant(mk_struct(cx, upvar_types.as_slice(), false),
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index b7bb383ad3c..16ba3ddbd70 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -45,8 +45,8 @@ use middle::trans::adt;
 use middle::trans::build::*;
 use middle::trans::builder::{Builder, noname};
 use middle::trans::callee;
+use middle::trans::cleanup::{CleanupMethods, ScopeId};
 use middle::trans::cleanup;
-use middle::trans::cleanup::CleanupMethods;
 use middle::trans::common::*;
 use middle::trans::consts;
 use middle::trans::controlflow;
@@ -252,6 +252,31 @@ fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::De
     f
 }
 
+pub fn self_type_for_unboxed_closure(ccx: &CrateContext,
+                                     closure_id: ast::DefId)
+                                     -> ty::t {
+    let unboxed_closure_type = ty::mk_unboxed_closure(ccx.tcx(),
+                                                      closure_id,
+                                                      ty::ReStatic);
+    let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+    let unboxed_closure = unboxed_closures.get(&closure_id);
+    match unboxed_closure.kind {
+        ty::FnUnboxedClosureKind => {
+            ty::mk_imm_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type)
+        }
+        ty::FnMutUnboxedClosureKind => {
+            ty::mk_mut_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type)
+        }
+        ty::FnOnceUnboxedClosureKind => unboxed_closure_type,
+    }
+}
+
+pub fn kind_for_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId)
+                                -> ty::UnboxedClosureKind {
+    let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+    unboxed_closures.get(&closure_id).kind
+}
+
 pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
     let (inputs, output, abi, env) = match ty::get(fn_ty).sty {
         ty::ty_bare_fn(ref f) => {
@@ -260,12 +285,12 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
         ty::ty_closure(ref f) => {
             (f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx)))
         }
-        ty::ty_unboxed_closure(closure_did) => {
-            let unboxed_closure_types = ccx.tcx
-                                           .unboxed_closure_types
-                                           .borrow();
-            let function_type = unboxed_closure_types.get(&closure_did);
-            let llenvironment_type = type_of(ccx, fn_ty).ptr_to();
+        ty::ty_unboxed_closure(closure_did, _) => {
+            let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+            let unboxed_closure = unboxed_closures.get(&closure_did);
+            let function_type = unboxed_closure.closure_type.clone();
+            let self_type = self_type_for_unboxed_closure(ccx, closure_did);
+            let llenvironment_type = type_of_explicit_arg(ccx, self_type);
             (function_type.sig.inputs.clone(),
              function_type.sig.output,
              RustCall,
@@ -691,7 +716,7 @@ pub fn iter_structural_ty<'r,
               }
           })
       }
-      ty::ty_unboxed_closure(def_id) => {
+      ty::ty_unboxed_closure(def_id, _) => {
           let repr = adt::represent_type(cx.ccx(), t);
           let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id);
           for (i, upvar) in upvars.iter().enumerate() {
@@ -1308,7 +1333,7 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
             match e.node {
                 ast::ExprFnBlock(_, _, blk) |
                 ast::ExprProc(_, blk) |
-                ast::ExprUnboxedFn(_, _, blk) => {
+                ast::ExprUnboxedFn(_, _, _, blk) => {
                     let mut explicit = CheckForNestedReturnsVisitor { found: false };
                     let mut implicit = CheckForNestedReturnsVisitor { found: false };
                     visit::walk_expr(&mut explicit, &*e, false);
@@ -1460,6 +1485,8 @@ pub fn create_datums_for_fn_args(fcx: &FunctionContext,
 /// Creates rvalue datums for each of the incoming function arguments and
 /// tuples the arguments. These will later be stored into appropriate lvalue
 /// datums.
+///
+/// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
 fn create_datums_for_fn_args_under_call_abi<
         'a>(
         mut bcx: &'a Block<'a>,
@@ -1708,7 +1735,8 @@ pub fn trans_closure(ccx: &CrateContext,
                      abi: Abi,
                      has_env: bool,
                      is_unboxed_closure: IsUnboxedClosureFlag,
-                     maybe_load_env: <'a> |&'a Block<'a>| -> &'a Block<'a>) {
+                     maybe_load_env: <'a>|&'a Block<'a>, ScopeId|
+                                         -> &'a Block<'a>) {
     ccx.stats.n_closures.set(ccx.stats.n_closures.get() + 1);
 
     let _icx = push_ctxt("trans_closure");
@@ -1773,7 +1801,7 @@ pub fn trans_closure(ccx: &CrateContext,
         }
     };
 
-    bcx = maybe_load_env(bcx);
+    bcx = maybe_load_env(bcx, cleanup::CustomScope(arg_scope));
 
     // Up until here, IR instructions for this function have explicitly not been annotated with
     // source code location, so we don't step into call setup code. From here on, source location
@@ -1854,7 +1882,7 @@ pub fn trans_fn(ccx: &CrateContext,
                   abi,
                   false,
                   NotUnboxedClosure,
-                  |bcx| bcx);
+                  |bcx, _| bcx);
 }
 
 pub fn trans_enum_variant(ccx: &CrateContext,
@@ -2188,11 +2216,11 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
     let (fn_sig, abi, has_env) = match ty::get(fn_ty).sty {
         ty::ty_closure(ref f) => (f.sig.clone(), f.abi, true),
         ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false),
-        ty::ty_unboxed_closure(closure_did) => {
-            let unboxed_closure_types = ccx.tcx
-                                           .unboxed_closure_types
-                                           .borrow();
-            let function_type = unboxed_closure_types.get(&closure_did);
+        ty::ty_unboxed_closure(closure_did, _) => {
+            let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+            let function_type = unboxed_closures.get(&closure_did)
+                                                .closure_type
+                                                .clone();
             (function_type.sig.clone(), RustCall, true)
         }
         _ => fail!("expected closure or function.")
diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs
index 44c52a6739a..17f1b6ca526 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -19,6 +19,7 @@ use middle::lang_items::ClosureExchangeMallocFnLangItem;
 use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::build::*;
+use middle::trans::cleanup::{CleanupMethods, ScopeId};
 use middle::trans::common::*;
 use middle::trans::datum::{Datum, DatumBlock, Expr, Lvalue, rvalue_scratch_datum};
 use middle::trans::debuginfo;
@@ -306,7 +307,9 @@ fn load_environment<'a>(bcx: &'a Block<'a>,
 
 fn load_unboxed_closure_environment<'a>(
                                     bcx: &'a Block<'a>,
-                                    freevars: &Vec<freevars::freevar_entry>)
+                                    arg_scope_id: ScopeId,
+                                    freevars: &Vec<freevars::freevar_entry>,
+                                    closure_id: ast::DefId)
                                     -> &'a Block<'a> {
     let _icx = push_ctxt("closure::load_environment");
 
@@ -314,11 +317,31 @@ fn load_unboxed_closure_environment<'a>(
         return bcx
     }
 
-    let llenv = bcx.fcx.llenv.unwrap();
+    // Special case for small by-value selfs.
+    let self_type = self_type_for_unboxed_closure(bcx.ccx(), closure_id);
+    let kind = kind_for_unboxed_closure(bcx.ccx(), closure_id);
+    let llenv = if kind == ty::FnOnceUnboxedClosureKind &&
+            !arg_is_indirect(bcx.ccx(), self_type) {
+        let datum = rvalue_scratch_datum(bcx,
+                                         self_type,
+                                         "unboxed_closure_env");
+        store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type);
+        assert!(freevars.len() <= 1);
+        datum.val
+    } else {
+        bcx.fcx.llenv.unwrap()
+    };
+
     for (i, freevar) in freevars.iter().enumerate() {
         let upvar_ptr = GEPi(bcx, llenv, [0, i]);
         let def_id = freevar.def.def_id();
         bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr);
+
+        if kind == ty::FnOnceUnboxedClosureKind {
+            bcx.fcx.schedule_drop_mem(arg_scope_id,
+                                      upvar_ptr,
+                                      node_id_type(bcx, def_id.node))
+        }
     }
 
     bcx
@@ -394,7 +417,7 @@ pub fn trans_expr_fn<'a>(
                   ty::ty_fn_abi(fty),
                   true,
                   NotUnboxedClosure,
-                  |bcx| load_environment(bcx, cdata_ty, &freevars, store));
+                  |bcx, _| load_environment(bcx, cdata_ty, &freevars, store));
     fill_fn_pair(bcx, dest_addr, llfn, llbox);
     bcx
 }
@@ -404,7 +427,7 @@ pub fn trans_expr_fn<'a>(
 pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext,
                                                     closure_id: ast::DefId)
                                                     -> Option<ValueRef> {
-    if !ccx.tcx.unboxed_closure_types.borrow().contains_key(&closure_id) {
+    if !ccx.tcx.unboxed_closures.borrow().contains_key(&closure_id) {
         // Not an unboxed closure.
         return None
     }
@@ -418,7 +441,9 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext,
         None => {}
     }
 
-    let function_type = ty::mk_unboxed_closure(&ccx.tcx, closure_id);
+    let function_type = ty::mk_unboxed_closure(&ccx.tcx,
+                                               closure_id,
+                                               ty::ReStatic);
     let symbol = ccx.tcx.map.with_path(closure_id.node, |path| {
         mangle_internal_name_by_path_and_seq(path, "unboxed_closure")
     });
@@ -453,19 +478,10 @@ pub fn trans_unboxed_closure<'a>(
         bcx.ccx(),
         closure_id).unwrap();
 
-    // Untuple the arguments.
-    let unboxed_closure_types = bcx.tcx().unboxed_closure_types.borrow();
-    let /*mut*/ function_type = (*unboxed_closure_types.get(&closure_id)).clone();
-    /*function_type.sig.inputs =
-        match ty::get(*function_type.sig.inputs.get(0)).sty {
-            ty::ty_tup(ref tuple_types) => {
-                tuple_types.iter().map(|x| (*x).clone()).collect()
-            }
-            _ => {
-                bcx.tcx().sess.span_bug(body.span,
-                                        "unboxed closure wasn't a tuple?!")
-            }
-        };*/
+    let unboxed_closures = bcx.tcx().unboxed_closures.borrow();
+    let function_type = unboxed_closures.get(&closure_id)
+                                        .closure_type
+                                        .clone();
     let function_type = ty::mk_closure(bcx.tcx(), function_type);
 
     let freevars: Vec<freevars::freevar_entry> =
@@ -486,7 +502,12 @@ pub fn trans_unboxed_closure<'a>(
                   ty::ty_fn_abi(function_type),
                   true,
                   IsUnboxedClosure,
-                  |bcx| load_unboxed_closure_environment(bcx, freevars_ptr));
+                  |bcx, arg_scope| {
+                      load_unboxed_closure_environment(bcx,
+                                                       arg_scope,
+                                                       freevars_ptr,
+                                                       closure_id)
+                  });
 
     // Don't hoist this to the top of the function. It's perfectly legitimate
     // to have a zero-size unboxed closure (in which case dest will be
@@ -502,13 +523,13 @@ pub fn trans_unboxed_closure<'a>(
     let repr = adt::represent_type(bcx.ccx(), node_id_type(bcx, id));
 
     // Create the closure.
-    for freevar in freevars_ptr.iter() {
+    for (i, freevar) in freevars_ptr.iter().enumerate() {
         let datum = expr::trans_local_var(bcx, freevar.def);
         let upvar_slot_dest = adt::trans_field_ptr(bcx,
                                                    &*repr,
                                                    dest_addr,
                                                    0,
-                                                   0);
+                                                   i);
         bcx = datum.store_to(bcx, upvar_slot_dest);
     }
     adt::trans_set_discr(bcx, &*repr, dest_addr, 0);
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 8b36270ee54..300e7ecf81f 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -32,7 +32,7 @@ use middle::trans::type_of;
 use middle::ty;
 use middle::typeck;
 use util::ppaux::Repr;
-use util::nodemap::NodeMap;
+use util::nodemap::{DefIdMap, NodeMap};
 
 use arena::TypedArena;
 use std::collections::HashMap;
@@ -514,6 +514,11 @@ impl<'a> mc::Typer for Block<'a> {
         self.tcx().region_maps.temporary_scope(rvalue_id)
     }
 
+    fn unboxed_closures<'a>(&'a self)
+                        -> &'a RefCell<DefIdMap<ty::UnboxedClosure>> {
+        &self.tcx().unboxed_closures
+    }
+
     fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow {
         self.tcx().upvar_borrow_map.borrow().get_copy(&upvar_id)
     }
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index ad1c5bf6ef1..96b8acbfb40 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -1152,7 +1152,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
             match expr.node {
                 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,
@@ -3620,7 +3620,7 @@ fn populate_scope_map(cx: &CrateContext,
 
             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,
@@ -3895,7 +3895,7 @@ fn push_debuginfo_type_name(cx: &CrateContext,
                 push_debuginfo_type_name(cx, sig.output, true, output);
             }
         },
-        ty::ty_unboxed_closure(_) => {
+        ty::ty_unboxed_closure(..) => {
             output.push_str("closure");
         }
         ty::ty_err      |
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 7cd2bd631f0..576bdb8b8c0 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -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/trans/meth.rs b/src/librustc/middle/trans/meth.rs
index 3578b25c839..3ae4c552b84 100644
--- a/src/librustc/middle/trans/meth.rs
+++ b/src/librustc/middle/trans/meth.rs
@@ -446,7 +446,7 @@ pub fn trans_trait_callee_from_llval<'a>(bcx: &'a Block<'a>,
 fn get_callee_substitutions_for_unboxed_closure(bcx: &Block,
                                                 def_id: ast::DefId)
                                                 -> subst::Substs {
-    let self_ty = ty::mk_unboxed_closure(bcx.tcx(), def_id);
+    let self_ty = ty::mk_unboxed_closure(bcx.tcx(), def_id, ty::ReStatic);
     subst::Substs::erased(
         VecPerParamSpace::new(Vec::new(),
                               vec![
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index 94c376c09c8..8a445fc4839 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -273,7 +273,7 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
         let name = llvm_type_name(cx, an_enum, did, tps);
         adt::incomplete_type_of(cx, &*repr, name.as_slice())
       }
-      ty::ty_unboxed_closure(did) => {
+      ty::ty_unboxed_closure(did, _) => {
         // Only create the named struct, but don't fill it in. We
         // fill it in *after* placing it into the type cache.
         let repr = adt::represent_type(cx, t);
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 3346b475267..f03c982bb08 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -373,7 +373,7 @@ pub struct ctxt {
 
     /// Records the type of each unboxed closure. The def ID is the ID of the
     /// expression defining the unboxed closure.
-    pub unboxed_closure_types: RefCell<DefIdMap<ClosureTy>>,
+    pub unboxed_closures: RefCell<DefIdMap<UnboxedClosure>>,
 
     pub node_lint_levels: RefCell<HashMap<(ast::NodeId, lint::LintId),
                                           lint::LevelSource>>,
@@ -745,7 +745,7 @@ pub enum sty {
     ty_closure(Box<ClosureTy>),
     ty_trait(Box<TyTrait>),
     ty_struct(DefId, Substs),
-    ty_unboxed_closure(DefId),
+    ty_unboxed_closure(DefId, Region),
     ty_tup(Vec<t>),
 
     ty_param(ParamTy), // type parameter
@@ -1056,6 +1056,21 @@ pub type type_cache = RefCell<DefIdMap<Polytype>>;
 
 pub type node_type_table = RefCell<HashMap<uint,t>>;
 
+/// Records information about each unboxed closure.
+pub struct UnboxedClosure {
+    /// The type of the unboxed closure.
+    pub closure_type: ClosureTy,
+    /// The kind of unboxed closure this is.
+    pub kind: UnboxedClosureKind,
+}
+
+#[deriving(PartialEq, Eq)]
+pub enum UnboxedClosureKind {
+    FnUnboxedClosureKind,
+    FnMutUnboxedClosureKind,
+    FnOnceUnboxedClosureKind,
+}
+
 pub fn mk_ctxt(s: Session,
                dm: resolve::DefMap,
                named_region_map: resolve_lifetime::NamedRegionMap,
@@ -1117,7 +1132,7 @@ pub fn mk_ctxt(s: Session,
         method_map: RefCell::new(FnvHashMap::new()),
         vtable_map: RefCell::new(FnvHashMap::new()),
         dependency_formats: RefCell::new(HashMap::new()),
-        unboxed_closure_types: RefCell::new(DefIdMap::new()),
+        unboxed_closures: RefCell::new(DefIdMap::new()),
         node_lint_levels: RefCell::new(HashMap::new()),
         transmute_restrictions: RefCell::new(Vec::new()),
         stability: RefCell::new(stability),
@@ -1177,7 +1192,7 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
     }
     match &st {
       &ty_nil | &ty_bool | &ty_char | &ty_int(_) | &ty_float(_) | &ty_uint(_) |
-      &ty_str | &ty_unboxed_closure(_) => {}
+      &ty_str => {}
       // You might think that we could just return ty_err for
       // any type containing ty_err as a component, and get
       // rid of the has_ty_err flag -- likewise for ty_bot (with
@@ -1194,6 +1209,7 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
               flags |= has_params as uint;
           }
       }
+      &ty_unboxed_closure(_, ref region) => flags |= rflags(*region),
       &ty_infer(_) => flags |= needs_infer as uint,
       &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
           flags |= sflags(substs);
@@ -1442,8 +1458,9 @@ pub fn mk_struct(cx: &ctxt, struct_id: ast::DefId, substs: Substs) -> t {
     mk_t(cx, ty_struct(struct_id, substs))
 }
 
-pub fn mk_unboxed_closure(cx: &ctxt, closure_id: ast::DefId) -> t {
-    mk_t(cx, ty_unboxed_closure(closure_id))
+pub fn mk_unboxed_closure(cx: &ctxt, closure_id: ast::DefId, region: Region)
+                          -> t {
+    mk_t(cx, ty_unboxed_closure(closure_id, region))
 }
 
 pub fn mk_var(cx: &ctxt, v: TyVid) -> t { mk_infer(cx, TyVar(v)) }
@@ -1476,8 +1493,8 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) {
     }
     match get(ty).sty {
         ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) |
-        ty_str | ty_infer(_) | ty_param(_) | ty_unboxed_closure(_) | ty_err => {
-        }
+        ty_str | ty_infer(_) | ty_param(_) | ty_unboxed_closure(..) |
+        ty_err => {}
         ty_box(ty) | ty_uniq(ty) => maybe_walk_ty(ty, f),
         ty_ptr(ref tm) | ty_rptr(_, ref tm) | ty_vec(ref tm, _) => {
             maybe_walk_ty(tm.ty, f);
@@ -1584,7 +1601,7 @@ pub fn type_is_vec(ty: t) -> bool {
 pub fn type_is_structural(ty: t) -> bool {
     match get(ty).sty {
       ty_struct(..) | ty_tup(_) | ty_enum(..) | ty_closure(_) |
-      ty_vec(_, Some(_)) | ty_unboxed_closure(_) => true,
+      ty_vec(_, Some(_)) | ty_unboxed_closure(..) => true,
       _ => type_is_slice(ty) | type_is_trait(ty)
     }
 }
@@ -2099,10 +2116,13 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
                 apply_lang_items(cx, did, res)
             }
 
-            ty_unboxed_closure(did) => {
+            ty_unboxed_closure(did, r) => {
+                // FIXME(#14449): `borrowed_contents` below assumes `&mut`
+                // unboxed closure.
                 let upvars = unboxed_closure_upvars(cx, did);
                 TypeContents::union(upvars.as_slice(),
-                                    |f| tc_ty(cx, f.ty, cache))
+                                    |f| tc_ty(cx, f.ty, cache)) |
+                    borrowed_contents(r, MutMutable)
             }
 
             ty_tup(ref tys) => {
@@ -2349,7 +2369,7 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool {
                 r
             }
 
-            ty_unboxed_closure(did) => {
+            ty_unboxed_closure(did, _) => {
                 let upvars = unboxed_closure_upvars(cx, did);
                 upvars.iter().any(|f| type_requires(cx, seen, r_ty, f.ty))
             }
@@ -2477,7 +2497,7 @@ pub fn is_type_representable(cx: &ctxt, sp: Span, ty: t) -> Representability {
                 r
             }
 
-            ty_unboxed_closure(did) => {
+            ty_unboxed_closure(did, _) => {
                 let upvars = unboxed_closure_upvars(cx, did);
                 find_nonrepresentable(cx,
                                       sp,
@@ -2718,7 +2738,7 @@ pub fn ty_fn_args(fty: t) -> Vec<t> {
 pub fn ty_closure_store(fty: t) -> TraitStore {
     match get(fty).sty {
         ty_closure(ref f) => f.store,
-        ty_unboxed_closure(_) => {
+        ty_unboxed_closure(..) => {
             // Close enough for the purposes of all the callers of this
             // function (which is soon to be deprecated anyhow).
             UniqTraitStore
@@ -3318,7 +3338,7 @@ pub fn ty_sort_string(cx: &ctxt, t: t) -> String {
         ty_struct(id, _) => {
             format!("struct {}", item_path_str(cx, id))
         }
-        ty_unboxed_closure(_) => "closure".to_string(),
+        ty_unboxed_closure(..) => "closure".to_string(),
         ty_tup(_) => "tuple".to_string(),
         ty_infer(TyVar(_)) => "inferred type".to_string(),
         ty_infer(IntVar(_)) => "integral variable".to_string(),
@@ -3687,7 +3707,7 @@ pub fn ty_to_def_id(ty: t) -> Option<ast::DefId> {
         ty_trait(box TyTrait { def_id: id, .. }) |
         ty_struct(id, _) |
         ty_enum(id, _) |
-        ty_unboxed_closure(id) => Some(id),
+        ty_unboxed_closure(id, _) => Some(id),
         _ => None
     }
 }
@@ -4717,9 +4737,10 @@ pub fn hash_crate_independent(tcx: &ctxt, t: t, svh: &Svh) -> u64 {
             }
             ty_infer(_) => unreachable!(),
             ty_err => byte!(23),
-            ty_unboxed_closure(d) => {
+            ty_unboxed_closure(d, r) => {
                 byte!(24);
                 did(&mut state, d);
+                region(&mut state, r);
             }
         }
     });
@@ -4873,6 +4894,11 @@ impl mc::Typer for ty::ctxt {
                     -> freevars::CaptureMode {
         self.capture_modes.borrow().get_copy(&closure_expr_id)
     }
+
+    fn unboxed_closures<'a>(&'a self)
+                        -> &'a RefCell<DefIdMap<UnboxedClosure>> {
+        &self.unboxed_closures
+    }
 }
 
 /// The category of explicit self.
@@ -4914,6 +4940,7 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec<ty::Region>,
                     UniqTraitStore => {}
                 }
             }
+            ty_unboxed_closure(_, ref region) => accumulator.push(*region),
             ty_nil |
             ty_bot |
             ty_bool |
@@ -4930,7 +4957,6 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec<ty::Region>,
             ty_tup(_) |
             ty_param(_) |
             ty_infer(_) |
-            ty_unboxed_closure(_) |
             ty_err => {}
         }
     })
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index e2b98495906..9f475bfd9d5 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -392,8 +392,8 @@ pub fn super_fold_sty<T:TypeFolder>(this: &mut T,
         ty::ty_struct(did, ref substs) => {
             ty::ty_struct(did, substs.fold_with(this))
         }
-        ty::ty_unboxed_closure(did) => {
-            ty::ty_unboxed_closure(did)
+        ty::ty_unboxed_closure(did, ref region) => {
+            ty::ty_unboxed_closure(did, region.fold_with(this))
         }
         ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_str |
         ty::ty_int(_) | ty::ty_uint(_) | ty::ty_float(_) |
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 5e7426f3ae7..c317f98a25a 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -51,7 +51,8 @@
 
 use middle::const_eval;
 use middle::def;
-use middle::lang_items::FnMutTraitLangItem;
+use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem};
+use middle::lang_items::{FnOnceTraitLangItem};
 use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
 use middle::subst::{VecPerParamSpace};
 use middle::ty;
@@ -544,16 +545,17 @@ fn ast_ty_to_mt<AC:AstConv, RS:RegionScope>(this: &AC,
 
 pub fn trait_ref_for_unboxed_function<AC:AstConv,
                                       RS:RegionScope>(
-                                          this: &AC,
-                                          rscope: &RS,
-                                          unboxed_function: &ast::UnboxedFnTy,
-                                          self_ty: Option<ty::t>)
-    -> ty::TraitRef
-{
-    let fn_mut_trait_did = this.tcx()
-                               .lang_items
-                               .require(FnMutTraitLangItem)
-                               .unwrap();
+                                      this: &AC,
+                                      rscope: &RS,
+                                      unboxed_function: &ast::UnboxedFnTy,
+                                      self_ty: Option<ty::t>)
+                                      -> ty::TraitRef {
+    let lang_item = match unboxed_function.kind {
+        ast::FnUnboxedClosureKind => FnTraitLangItem,
+        ast::FnMutUnboxedClosureKind => FnMutTraitLangItem,
+        ast::FnOnceUnboxedClosureKind => FnOnceTraitLangItem,
+    };
+    let trait_did = this.tcx().lang_items.require(lang_item).unwrap();
     let input_types =
         unboxed_function.decl
                         .inputs
@@ -574,7 +576,7 @@ pub fn trait_ref_for_unboxed_function<AC:AstConv,
     }
 
     ty::TraitRef {
-        def_id: fn_mut_trait_did,
+        def_id: trait_did,
         substs: substs,
     }
 }
@@ -810,7 +812,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
                                             None);
                 ty::mk_closure(tcx, fn_decl)
             }
-            ast::TyUnboxedFn(_) => {
+            ast::TyUnboxedFn(..) => {
                 tcx.sess.span_err(ast_ty.span,
                                   "cannot use unboxed functions here");
                 ty::mk_err()
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 1805c18eaf1..dfcde8ca9b9 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -444,7 +444,7 @@ impl<'a> LookupContext<'a> {
                 },
                 ty_enum(did, _) |
                 ty_struct(did, _) |
-                ty_unboxed_closure(did) => {
+                ty_unboxed_closure(did, _) => {
                     if self.check_traits == CheckTraitsAndInherentMethods {
                         self.push_inherent_impl_candidates_for_type(did);
                     }
@@ -468,7 +468,7 @@ impl<'a> LookupContext<'a> {
                 ty_param(p) => {
                     self.push_inherent_candidates_from_param(self_ty, restrict_to, p);
                 }
-                ty_unboxed_closure(closure_did) => {
+                ty_unboxed_closure(closure_did, _) => {
                     self.push_unboxed_closure_call_candidates_if_applicable(
                         closure_did);
                 }
@@ -531,8 +531,11 @@ impl<'a> LookupContext<'a> {
         let arguments_type = *closure_function_type.sig.inputs.get(0);
         let return_type = closure_function_type.sig.output;
 
+        let closure_region =
+            vcx.infcx.next_region_var(MiscVariable(self.span));
         let unboxed_closure_type = ty::mk_unboxed_closure(self.tcx(),
-                                                          closure_did);
+                                                          closure_did,
+                                                          closure_region);
         self.extension_candidates.push(Candidate {
             rcvr_match_condition:
                 RcvrMatchesIfSubtype(unboxed_closure_type),
@@ -548,39 +551,38 @@ impl<'a> LookupContext<'a> {
     fn push_unboxed_closure_call_candidates_if_applicable(
             &mut self,
             closure_did: DefId) {
-        // FIXME(pcwalton): Try `Fn` and `FnOnce` too.
-        let trait_did = match self.tcx().lang_items.fn_mut_trait() {
-            Some(trait_did) => trait_did,
-            None => return,
-        };
-
-        match self.tcx()
-                  .unboxed_closure_types
-                  .borrow()
-                  .find(&closure_did) {
-            None => {}  // Fall through to try inherited.
-            Some(closure_function_type) => {
-                self.push_unboxed_closure_call_candidate_if_applicable(
-                    trait_did,
-                    closure_did,
-                    closure_function_type);
-                return
+        let trait_dids = [
+            self.tcx().lang_items.fn_trait(),
+            self.tcx().lang_items.fn_mut_trait(),
+            self.tcx().lang_items.fn_once_trait()
+        ];
+        for optional_trait_did in trait_dids.iter() {
+            let trait_did = match *optional_trait_did {
+                Some(trait_did) => trait_did,
+                None => continue,
+            };
+
+            match self.tcx().unboxed_closures.borrow().find(&closure_did) {
+                None => {}  // Fall through to try inherited.
+                Some(closure) => {
+                    self.push_unboxed_closure_call_candidate_if_applicable(
+                        trait_did,
+                        closure_did,
+                        &closure.closure_type);
+                    return
+                }
             }
-        }
 
-        match self.fcx
-                  .inh
-                  .unboxed_closure_types
-                  .borrow()
-                  .find(&closure_did) {
-            Some(closure_function_type) => {
-                self.push_unboxed_closure_call_candidate_if_applicable(
-                    trait_did,
-                    closure_did,
-                    closure_function_type);
-                return
+            match self.fcx.inh.unboxed_closures.borrow().find(&closure_did) {
+                Some(closure) => {
+                    self.push_unboxed_closure_call_candidate_if_applicable(
+                        trait_did,
+                        closure_did,
+                        &closure.closure_type);
+                    return
+                }
+                None => {}
             }
-            None => {}
         }
 
         self.tcx().sess.bug("didn't find unboxed closure type in tcx map or \
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 33acb521780..aa5a3585158 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -168,7 +168,7 @@ pub struct Inherited<'a> {
     method_map: MethodMap,
     vtable_map: vtable_map,
     upvar_borrow_map: RefCell<ty::UpvarBorrowMap>,
-    unboxed_closure_types: RefCell<DefIdMap<ty::ClosureTy>>,
+    unboxed_closures: RefCell<DefIdMap<ty::UnboxedClosure>>,
 }
 
 /// When type-checking an expression, we propagate downward
@@ -275,7 +275,7 @@ impl<'a> Inherited<'a> {
             method_map: RefCell::new(FnvHashMap::new()),
             vtable_map: RefCell::new(FnvHashMap::new()),
             upvar_borrow_map: RefCell::new(HashMap::new()),
-            unboxed_closure_types: RefCell::new(DefIdMap::new()),
+            unboxed_closures: RefCell::new(DefIdMap::new()),
         }
     }
 }
@@ -1271,7 +1271,7 @@ impl<'a> FnCtxt<'a> {
         VtableContext {
             infcx: self.infcx(),
             param_env: &self.inh.param_env,
-            unboxed_closure_types: &self.inh.unboxed_closure_types,
+            unboxed_closures: &self.inh.unboxed_closures,
         }
     }
 }
@@ -2618,6 +2618,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
 
     fn check_unboxed_closure(fcx: &FnCtxt,
                              expr: &ast::Expr,
+                             kind: ast::UnboxedClosureKind,
                              decl: &ast::FnDecl,
                              body: ast::P<ast::Block>) {
         // The `RegionTraitStore` is a lie, but we ignore it so it doesn't
@@ -2635,8 +2636,16 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
             abi::RustCall,
             None);
 
+        let region = match fcx.infcx().anon_regions(expr.span, 1) {
+            Err(_) => {
+                fcx.ccx.tcx.sess.span_bug(expr.span,
+                                          "can't make anon regions here?!")
+            }
+            Ok(regions) => *regions.get(0),
+        };
         let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx,
-                                                  local_def(expr.id));
+                                                  local_def(expr.id),
+                                                  region);
         fcx.write_ty(expr.id, closure_type);
 
         check_fn(fcx.ccx,
@@ -2648,13 +2657,24 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                  fcx.inh);
 
         // Tuple up the arguments and insert the resulting function type into
-        // the `unboxed_closure_types` table.
+        // the `unboxed_closures` table.
         fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)];
 
+        let kind = match kind {
+            ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind,
+            ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind,
+            ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind,
+        };
+
+        let unboxed_closure = ty::UnboxedClosure {
+            closure_type: fn_ty,
+            kind: kind,
+        };
+
         fcx.inh
-           .unboxed_closure_types
+           .unboxed_closures
            .borrow_mut()
-           .insert(local_def(expr.id), fn_ty);
+           .insert(local_def(expr.id), unboxed_closure);
     }
 
     fn check_expr_fn(fcx: &FnCtxt,
@@ -3402,9 +3422,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                       body.clone(),
                       expected);
       }
-      ast::ExprUnboxedFn(_, ref decl, ref body) => {
+      ast::ExprUnboxedFn(_, kind, ref decl, ref body) => {
         check_unboxed_closure(fcx,
                               expr,
+                              kind,
                               &**decl,
                               *body);
       }
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index 47f5c45dde2..0acf6e91831 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -132,7 +132,7 @@ use middle::typeck::infer::resolve_type;
 use middle::typeck::infer;
 use middle::typeck::MethodCall;
 use middle::pat_util;
-use util::nodemap::NodeMap;
+use util::nodemap::{DefIdMap, NodeMap};
 use util::ppaux::{ty_to_string, region_to_string, Repr};
 
 use syntax::ast;
@@ -295,6 +295,11 @@ impl<'fcx> mc::Typer for Rcx<'fcx> {
                     -> freevars::CaptureMode {
         self.tcx().capture_modes.borrow().get_copy(&closure_expr_id)
     }
+
+    fn unboxed_closures<'a>(&'a self)
+                        -> &'a RefCell<DefIdMap<ty::UnboxedClosure>> {
+        &self.fcx.inh.unboxed_closures
+    }
 }
 
 pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) {
@@ -594,7 +599,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
 
         ast::ExprFnBlock(_, _, ref body) |
         ast::ExprProc(_, ref body) |
-        ast::ExprUnboxedFn(_, _, ref body) => {
+        ast::ExprUnboxedFn(_, _, _, ref body) => {
             check_expr_fn_block(rcx, expr, &**body);
         }
 
@@ -660,6 +665,17 @@ fn check_expr_fn_block(rcx: &mut Rcx,
                 }
             });
         }
+        ty::ty_unboxed_closure(_, region) => {
+            freevars::with_freevars(tcx, expr.id, |freevars| {
+                // No free variables means that there is no environment and
+                // hence the closure has static lifetime. Otherwise, the
+                // closure must not outlive the variables it closes over
+                // by-reference.
+                if !freevars.is_empty() {
+                    constrain_free_variables(rcx, region, expr, freevars);
+                }
+            })
+        }
         _ => ()
     }
 
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 565b88b7493..850d5c5a8f0 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -71,7 +71,7 @@ use syntax::visit::Visitor;
 pub struct VtableContext<'a> {
     pub infcx: &'a infer::InferCtxt<'a>,
     pub param_env: &'a ty::ParameterEnvironment,
-    pub unboxed_closure_types: &'a RefCell<DefIdMap<ty::ClosureTy>>,
+    pub unboxed_closures: &'a RefCell<DefIdMap<ty::UnboxedClosure>>,
 }
 
 impl<'a> VtableContext<'a> {
@@ -309,31 +309,42 @@ fn search_for_unboxed_closure_vtable(vcx: &VtableContext,
                                      -> Option<vtable_origin> {
     let tcx = vcx.tcx();
     let closure_def_id = match ty::get(ty).sty {
-        ty::ty_unboxed_closure(closure_def_id) => closure_def_id,
+        ty::ty_unboxed_closure(closure_def_id, _) => closure_def_id,
         _ => return None,
     };
 
     let fn_traits = [
-        tcx.lang_items.fn_trait(),
-        tcx.lang_items.fn_mut_trait(),
-        tcx.lang_items.fn_once_trait()
+        (ty::FnUnboxedClosureKind, tcx.lang_items.fn_trait()),
+        (ty::FnMutUnboxedClosureKind, tcx.lang_items.fn_mut_trait()),
+        (ty::FnOnceUnboxedClosureKind, tcx.lang_items.fn_once_trait()),
     ];
-    for fn_trait in fn_traits.iter() {
-        match *fn_trait {
-            Some(ref fn_trait) if *fn_trait == trait_ref.def_id => {}
+    for tuple in fn_traits.iter() {
+        let kind = match tuple {
+            &(kind, Some(ref fn_trait)) if *fn_trait == trait_ref.def_id => {
+                kind
+            }
             _ => continue,
         };
 
         // Check to see whether the argument and return types match.
-        let unboxed_closure_types = tcx.unboxed_closure_types.borrow();
-        let closure_type = match unboxed_closure_types.find(&closure_def_id) {
-            Some(closure_type) => (*closure_type).clone(),
+        let unboxed_closures = tcx.unboxed_closures.borrow();
+        let closure_type = match unboxed_closures.find(&closure_def_id) {
+            Some(closure) => {
+                if closure.kind != kind {
+                    continue
+                }
+                closure.closure_type.clone()
+            }
             None => {
                 // Try the inherited unboxed closure type map.
-                let unboxed_closure_types = vcx.unboxed_closure_types
-                                               .borrow();
-                match unboxed_closure_types.find(&closure_def_id) {
-                    Some(closure_type) => (*closure_type).clone(),
+                let unboxed_closures = vcx.unboxed_closures.borrow();
+                match unboxed_closures.find(&closure_def_id) {
+                    Some(closure) => {
+                        if closure.kind != kind {
+                            continue
+                        }
+                        closure.closure_type.clone()
+                    }
                     None => {
                         tcx.sess.span_bug(span,
                                           "didn't find unboxed closure type \
@@ -881,11 +892,11 @@ pub fn resolve_impl(tcx: &ty::ctxt,
     debug!("impl_trait_ref={}", impl_trait_ref.repr(tcx));
 
     let infcx = &infer::new_infer_ctxt(tcx);
-    let unboxed_closure_types = RefCell::new(DefIdMap::new());
+    let unboxed_closures = RefCell::new(DefIdMap::new());
     let vcx = VtableContext {
         infcx: infcx,
         param_env: &param_env,
-        unboxed_closure_types: &unboxed_closure_types,
+        unboxed_closures: &unboxed_closures,
     };
 
     // Resolve the vtables for the trait reference on the impl.  This
@@ -934,11 +945,11 @@ pub fn resolve_impl(tcx: &ty::ctxt,
 pub fn trans_resolve_method(tcx: &ty::ctxt, id: ast::NodeId,
                             substs: &subst::Substs) -> vtable_res {
     let generics = ty::lookup_item_type(tcx, ast_util::local_def(id)).generics;
-    let unboxed_closure_types = RefCell::new(DefIdMap::new());
+    let unboxed_closures = RefCell::new(DefIdMap::new());
     let vcx = VtableContext {
         infcx: &infer::new_infer_ctxt(tcx),
         param_env: &ty::construct_parameter_environment(tcx, &ty::Generics::empty(), id),
-        unboxed_closure_types: &unboxed_closure_types,
+        unboxed_closures: &unboxed_closures,
     };
 
     lookup_vtables(&vcx,
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index 6e6ebd181fb..892a62249ac 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -43,7 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &ast::Expr) {
     let mut wbcx = WritebackCx::new(fcx);
     wbcx.visit_expr(e, ());
     wbcx.visit_upvar_borrow_map();
-    wbcx.visit_unboxed_closure_types();
+    wbcx.visit_unboxed_closures();
 }
 
 pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
@@ -62,7 +62,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
         }
     }
     wbcx.visit_upvar_borrow_map();
-    wbcx.visit_unboxed_closure_types();
+    wbcx.visit_unboxed_closures();
 }
 
 pub fn resolve_impl_res(infcx: &infer::InferCtxt,
@@ -134,7 +134,7 @@ impl<'cx> Visitor<()> for WritebackCx<'cx> {
         match e.node {
             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);
@@ -211,23 +211,27 @@ impl<'cx> WritebackCx<'cx> {
         }
     }
 
-    fn visit_unboxed_closure_types(&self) {
+    fn visit_unboxed_closures(&self) {
         if self.fcx.writeback_errors.get() {
             return
         }
 
-        for (def_id, closure_ty) in self.fcx
-                                        .inh
-                                        .unboxed_closure_types
-                                        .borrow()
-                                        .iter() {
-            let closure_ty = self.resolve(closure_ty,
+        for (def_id, unboxed_closure) in self.fcx
+                                             .inh
+                                             .unboxed_closures
+                                             .borrow()
+                                             .iter() {
+            let closure_ty = self.resolve(&unboxed_closure.closure_type,
                                           ResolvingUnboxedClosure(*def_id));
+            let unboxed_closure = ty::UnboxedClosure {
+                closure_type: closure_ty,
+                kind: unboxed_closure.kind,
+            };
             self.fcx
                 .tcx()
-                .unboxed_closure_types
+                .unboxed_closures
                 .borrow_mut()
-                .insert(*def_id, closure_ty);
+                .insert(*def_id, unboxed_closure);
         }
     }
 
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index fdf9125e6e1..76af54ba4d1 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -109,7 +109,7 @@ fn type_is_defined_in_local_crate(tcx: &ty::ctxt, original_type: t) -> bool {
         match get(t).sty {
             ty_enum(def_id, _) |
             ty_struct(def_id, _) |
-            ty_unboxed_closure(def_id) => {
+            ty_unboxed_closure(def_id, _) => {
                 if def_id.krate == ast::LOCAL_CRATE {
                     found_nominal = true;
                 }
@@ -153,7 +153,7 @@ fn get_base_type_def_id(inference_context: &InferCtxt,
             match get(base_type).sty {
                 ty_enum(def_id, _) |
                 ty_struct(def_id, _) |
-                ty_unboxed_closure(def_id) => {
+                ty_unboxed_closure(def_id, _) => {
                     Some(def_id)
                 }
                 ty_rptr(_, ty::mt {ty, ..}) | ty_uniq(ty) => match ty::get(ty).sty {
@@ -687,7 +687,7 @@ impl<'a> CoherenceChecker<'a> {
             match ty::get(self_type.ty).sty {
                 ty::ty_enum(type_def_id, _) |
                 ty::ty_struct(type_def_id, _) |
-                ty::ty_unboxed_closure(type_def_id) => {
+                ty::ty_unboxed_closure(type_def_id, _) => {
                     tcx.destructor_for_type.borrow_mut().insert(type_def_id,
                                                                 method_def_id);
                     tcx.destructors.borrow_mut().insert(method_def_id);
diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs
index 1aae97d3d83..d99d55d4d87 100644
--- a/src/librustc/middle/typeck/infer/combine.rs
+++ b/src/librustc/middle/typeck/infer/combine.rs
@@ -492,9 +492,11 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
             Ok(ty::mk_struct(tcx, a_id, substs))
       }
 
-      (&ty::ty_unboxed_closure(a_id), &ty::ty_unboxed_closure(b_id))
+      (&ty::ty_unboxed_closure(a_id, a_region),
+       &ty::ty_unboxed_closure(b_id, b_region))
       if a_id == b_id => {
-          Ok(ty::mk_unboxed_closure(tcx, a_id))
+          let region = if_ok!(this.regions(a_region, b_region));
+          Ok(ty::mk_unboxed_closure(tcx, a_id, region))
       }
 
       (&ty::ty_box(a_inner), &ty::ty_box(b_inner)) => {
diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs
index be9ae0cc719..54b9ce03534 100644
--- a/src/librustc/middle/typeck/variance.rs
+++ b/src/librustc/middle/typeck/variance.rs
@@ -722,10 +722,15 @@ impl<'a> ConstraintContext<'a> {
         match ty::get(ty).sty {
             ty::ty_nil | ty::ty_bot | ty::ty_bool |
             ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) |
-            ty::ty_float(_) | ty::ty_str | ty::ty_unboxed_closure(..) => {
+            ty::ty_float(_) | ty::ty_str => {
                 /* leaf type -- noop */
             }
 
+            ty::ty_unboxed_closure(_, region) => {
+                let contra = self.contravariant(variance);
+                self.add_constraints_from_region(region, contra);
+            }
+
             ty::ty_rptr(region, ref mt) => {
                 let contra = self.contravariant(variance);
                 self.add_constraints_from_region(region, contra);
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 46039a70fb2..7d9a23e91ba 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -528,7 +528,7 @@ pub enum Expr_ {
     ExprMatch(Gc<Expr>, Vec<Arm>),
     ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>),
     ExprProc(P<FnDecl>, P<Block>),
-    ExprUnboxedFn(CaptureClause, P<FnDecl>, P<Block>),
+    ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>),
     ExprBlock(P<Block>),
 
     ExprAssign(Gc<Expr>, Gc<Expr>),
@@ -900,6 +900,7 @@ pub struct BareFnTy {
 
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
 pub struct UnboxedFnTy {
+    pub kind: UnboxedClosureKind,
     pub decl: P<FnDecl>,
 }
 
@@ -1297,6 +1298,13 @@ pub enum ForeignItem_ {
     ForeignItemStatic(P<Ty>, /* is_mutbl */ bool),
 }
 
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub enum UnboxedClosureKind {
+    FnUnboxedClosureKind,
+    FnMutUnboxedClosureKind,
+    FnOnceUnboxedClosureKind,
+}
+
 /// The data we save and restore about an inlined item or method.  This is not
 /// part of the AST that we parse from a file, but it becomes part of the tree
 /// that we trans.
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 02f1f4646a4..cebe1862528 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -368,6 +368,7 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
         TyUnboxedFn(ref f) => {
             TyUnboxedFn(box(GC) UnboxedFnTy {
                 decl: fld.fold_fn_decl(&*f.decl),
+                kind: f.kind,
             })
         }
         TyTup(ref tys) => TyTup(tys.iter().map(|&ty| fld.fold_ty(ty)).collect()),
@@ -641,6 +642,7 @@ pub fn noop_fold_ty_param_bound<T: Folder>(tpb: &TyParamBound, fld: &mut T)
         UnboxedFnTyParamBound(ref unboxed_function_type) => {
             UnboxedFnTyParamBound(UnboxedFnTy {
                 decl: fld.fold_fn_decl(&*unboxed_function_type.decl),
+                kind: unboxed_function_type.kind,
             })
         }
         OtherRegionTyParamBound(s) => OtherRegionTyParamBound(s)
@@ -1103,8 +1105,9 @@ pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> {
             ExprProc(folder.fold_fn_decl(&**decl),
                      folder.fold_block(body.clone()))
         }
-        ExprUnboxedFn(capture_clause, ref decl, ref body) => {
+        ExprUnboxedFn(capture_clause, kind, ref decl, ref body) => {
             ExprUnboxedFn(capture_clause,
+                          kind,
                           folder.fold_fn_decl(&**decl),
                           folder.fold_block(*body))
         }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index f0920603ad1..f6db577a004 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -30,6 +30,8 @@ use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn};
 use ast::{ExprVec, ExprVstore, ExprVstoreSlice};
 use ast::{ExprVstoreMutSlice, ExprWhile, ExprForLoop, Field, FnDecl};
 use ast::{ExprVstoreUniq, Once, Many};
+use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind};
+use ast::{FnOnceUnboxedClosureKind};
 use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod};
 use ast::{Ident, NormalFn, Inherited, Item, Item_, ItemStatic};
 use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl};
@@ -53,7 +55,8 @@ use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn};
 use ast::{TyTypeof, TyInfer, TypeMethod};
 use ast::{TyNil, TyParam, TyParamBound, TyParen, TyPath, TyPtr, TyRptr};
 use ast::{TyTup, TyU32, TyUnboxedFn, TyUniq, TyVec, UnUniq};
-use ast::{UnboxedFnTy, UnboxedFnTyParamBound, UnnamedField, UnsafeBlock};
+use ast::{UnboxedClosureKind, UnboxedFnTy, UnboxedFnTyParamBound};
+use ast::{UnnamedField, UnsafeBlock};
 use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse};
 use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
 use ast::Visibility;
@@ -1087,6 +1090,34 @@ impl<'a> Parser<'a> {
         })
     }
 
+    /// Parses an optional unboxed closure kind (`&:`, `&mut:`, or `:`).
+    pub fn parse_optional_unboxed_closure_kind(&mut self)
+                                               -> Option<UnboxedClosureKind> {
+        if self.token == token::BINOP(token::AND) &&
+                    self.look_ahead(1, |t| {
+                        token::is_keyword(keywords::Mut, t)
+                    }) &&
+                    self.look_ahead(2, |t| *t == token::COLON) {
+            self.bump();
+            self.bump();
+            self.bump();
+            return Some(FnMutUnboxedClosureKind)
+        }
+
+        if self.token == token::BINOP(token::AND) &&
+                    self.look_ahead(1, |t| *t == token::COLON) {
+            self.bump();
+            self.bump();
+            return Some(FnUnboxedClosureKind)
+        }
+
+        if self.eat(&token::COLON) {
+            return Some(FnOnceUnboxedClosureKind)
+        }
+
+        return None
+    }
+
     /// Parse a TyClosure type
     pub fn parse_ty_closure(&mut self) -> Ty_ {
         /*
@@ -1115,27 +1146,19 @@ impl<'a> Parser<'a> {
             Vec::new()
         };
 
-        let (is_unboxed, inputs) = if self.eat(&token::OROR) {
-            (false, Vec::new())
+        let (optional_unboxed_closure_kind, inputs) = if self.eat(&token::OROR) {
+            (None, Vec::new())
         } else {
             self.expect_or();
 
-            let is_unboxed = self.token == token::BINOP(token::AND) &&
-                self.look_ahead(1, |t| {
-                    token::is_keyword(keywords::Mut, t)
-                }) &&
-                self.look_ahead(2, |t| *t == token::COLON);
-            if is_unboxed {
-                self.bump();
-                self.bump();
-                self.bump();
-            }
+            let optional_unboxed_closure_kind =
+                self.parse_optional_unboxed_closure_kind();
 
             let inputs = self.parse_seq_to_before_or(
                 &token::COMMA,
                 |p| p.parse_arg_general(false));
             self.expect_or();
-            (is_unboxed, inputs)
+            (optional_unboxed_closure_kind, inputs)
         };
 
         let (region, bounds) = {
@@ -1155,18 +1178,22 @@ impl<'a> Parser<'a> {
             variadic: false
         });
 
-        if is_unboxed {
-            TyUnboxedFn(box(GC) UnboxedFnTy {
-                decl: decl,
-            })
-        } else {
-            TyClosure(box(GC) ClosureTy {
-                fn_style: fn_style,
-                onceness: onceness,
-                bounds: bounds,
-                decl: decl,
-                lifetimes: lifetime_defs,
-            }, region)
+        match optional_unboxed_closure_kind {
+            Some(unboxed_closure_kind) => {
+                TyUnboxedFn(box(GC) UnboxedFnTy {
+                    kind: unboxed_closure_kind,
+                    decl: decl,
+                })
+            }
+            None => {
+                TyClosure(box(GC) ClosureTy {
+                    fn_style: fn_style,
+                    onceness: onceness,
+                    bounds: bounds,
+                    decl: decl,
+                    lifetimes: lifetime_defs,
+                }, region)
+            }
         }
     }
 
@@ -2703,7 +2730,8 @@ impl<'a> Parser<'a> {
     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 (decl, optional_unboxed_closure_kind) =
+            self.parse_fn_block_decl();
         let body = self.parse_expr();
         let fakeblock = P(ast::Block {
             view_items: Vec::new(),
@@ -2714,14 +2742,20 @@ impl<'a> Parser<'a> {
             span: body.span,
         });
 
-        if is_unboxed {
-            self.mk_expr(lo,
-                         body.span.hi,
-                         ExprUnboxedFn(capture_clause, decl, fakeblock))
-        } else {
-            self.mk_expr(lo,
-                         body.span.hi,
-                         ExprFnBlock(capture_clause, decl, fakeblock))
+        match optional_unboxed_closure_kind {
+            Some(unboxed_closure_kind) => {
+                self.mk_expr(lo,
+                             body.span.hi,
+                             ExprUnboxedFn(capture_clause,
+                                           unboxed_closure_kind,
+                                           decl,
+                                           fakeblock))
+            }
+            None => {
+                self.mk_expr(lo,
+                             body.span.hi,
+                             ExprFnBlock(capture_clause, decl, fakeblock))
+            }
         }
     }
 
@@ -3553,28 +3587,22 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_unboxed_function_type(&mut self) -> UnboxedFnTy {
-        let inputs = if self.eat(&token::OROR) {
-            Vec::new()
-        } else {
-            self.expect_or();
+        let (optional_unboxed_closure_kind, inputs) =
+            if self.eat(&token::OROR) {
+                (None, Vec::new())
+            } else {
+                self.expect_or();
 
-            if self.token == token::BINOP(token::AND) &&
-                    self.look_ahead(1, |t| {
-                        token::is_keyword(keywords::Mut, t)
-                    }) &&
-                    self.look_ahead(2, |t| *t == token::COLON) {
-                self.bump();
-                self.bump();
-                self.bump();
-            }
+                let optional_unboxed_closure_kind =
+                    self.parse_optional_unboxed_closure_kind();
 
-            let inputs = self.parse_seq_to_before_or(&token::COMMA,
-                                                     |p| {
-                p.parse_arg_general(false)
-            });
-            self.expect_or();
-            inputs
-        };
+                let inputs = self.parse_seq_to_before_or(&token::COMMA,
+                                                         |p| {
+                    p.parse_arg_general(false)
+                });
+                self.expect_or();
+                (optional_unboxed_closure_kind, inputs)
+            };
 
         let (return_style, output) = self.parse_ret_ty();
         UnboxedFnTy {
@@ -3583,7 +3611,11 @@ impl<'a> Parser<'a> {
                 output: output,
                 cf: return_style,
                 variadic: false,
-            })
+            }),
+            kind: match optional_unboxed_closure_kind {
+                Some(kind) => kind,
+                None => FnMutUnboxedClosureKind,
+            },
         }
     }
 
@@ -4026,29 +4058,22 @@ impl<'a> Parser<'a> {
     }
 
     // parse the |arg, arg| header on a lambda
-    fn parse_fn_block_decl(&mut self) -> (P<FnDecl>, bool) {
-        let (is_unboxed, inputs_captures) = {
+    fn parse_fn_block_decl(&mut self)
+                           -> (P<FnDecl>, Option<UnboxedClosureKind>) {
+        let (optional_unboxed_closure_kind, inputs_captures) = {
             if self.eat(&token::OROR) {
-                (false, Vec::new())
+                (None, Vec::new())
             } else {
                 self.expect(&token::BINOP(token::OR));
-                let is_unboxed = self.token == token::BINOP(token::AND) &&
-                    self.look_ahead(1, |t| {
-                        token::is_keyword(keywords::Mut, t)
-                    }) &&
-                    self.look_ahead(2, |t| *t == token::COLON);
-                if is_unboxed {
-                    self.bump();
-                    self.bump();
-                    self.bump();
-                }
+                let optional_unboxed_closure_kind =
+                    self.parse_optional_unboxed_closure_kind();
                 let args = self.parse_seq_to_before_end(
                     &token::BINOP(token::OR),
                     seq_sep_trailing_disallowed(token::COMMA),
                     |p| p.parse_fn_block_arg()
                 );
                 self.bump();
-                (is_unboxed, args)
+                (optional_unboxed_closure_kind, args)
             }
         };
         let output = if self.eat(&token::RARROW) {
@@ -4066,7 +4091,7 @@ impl<'a> Parser<'a> {
             output: output,
             cf: Return,
             variadic: false
-        }), is_unboxed)
+        }), optional_unboxed_closure_kind)
     }
 
     /// Parses the `(arg, arg) -> return_type` header on a procedure.
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index ed285e2aa44..4ee73406f03 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -9,8 +9,10 @@
 // except according to those terms.
 
 use abi;
-use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound};
-use ast::{TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided};
+use ast::{FnMutUnboxedClosureKind, FnOnceUnboxedClosureKind};
+use ast::{FnUnboxedClosureKind, P, OtherRegionTyParamBound};
+use ast::{StaticRegionTyParamBound, TraitTyParamBound, UnboxedClosureKind};
+use ast::{UnboxedFnTyParamBound, Required, Provided};
 use ast;
 use ast_util;
 use owned_slice::OwnedSlice;
@@ -228,7 +230,7 @@ pub fn method_to_string(p: &ast::Method) -> String {
 }
 
 pub fn fn_block_to_string(p: &ast::FnDecl) -> String {
-    $to_string(|s| s.print_fn_block_args(p, false))
+    $to_string(|s| s.print_fn_block_args(p, None))
 }
 
 pub fn path_to_string(p: &ast::Path) -> String {
@@ -594,7 +596,7 @@ impl<'a> State<'a> {
                                       &None,
                                       Some(&generics),
                                       None,
-                                      false));
+                                      None));
             }
             ast::TyClosure(f, ref region) => {
                 let generics = ast::Generics {
@@ -611,7 +613,7 @@ impl<'a> State<'a> {
                                       &f.bounds,
                                       Some(&generics),
                                       None,
-                                      false));
+                                      None));
             }
             ast::TyProc(ref f) => {
                 let generics = ast::Generics {
@@ -628,7 +630,7 @@ impl<'a> State<'a> {
                                       &f.bounds,
                                       Some(&generics),
                                       None,
-                                      false));
+                                      None));
             }
             ast::TyUnboxedFn(f) => {
                 try!(self.print_ty_fn(None,
@@ -641,7 +643,7 @@ impl<'a> State<'a> {
                                       &None,
                                       None,
                                       None,
-                                      true));
+                                      Some(f.kind)));
             }
             ast::TyPath(ref path, ref bounds, _) => {
                 try!(self.print_bounded_path(path, bounds));
@@ -1054,7 +1056,7 @@ impl<'a> State<'a> {
                               &None,
                               Some(&m.generics),
                               Some(m.explicit_self.node),
-                              false));
+                              None));
         word(&mut self.s, ";")
     }
 
@@ -1481,7 +1483,7 @@ impl<'a> State<'a> {
                 // we are inside.
                 //
                 // if !decl.inputs.is_empty() {
-                try!(self.print_fn_block_args(&**decl, false));
+                try!(self.print_fn_block_args(&**decl, None));
                 try!(space(&mut self.s));
                 // }
 
@@ -1505,7 +1507,7 @@ impl<'a> State<'a> {
                 // empty box to satisfy the close.
                 try!(self.ibox(0));
             }
-            ast::ExprUnboxedFn(capture_clause, ref decl, ref body) => {
+            ast::ExprUnboxedFn(capture_clause, kind, ref decl, ref body) => {
                 try!(self.print_capture_clause(capture_clause));
 
                 // in do/for blocks we don't want to show an empty
@@ -1513,7 +1515,7 @@ impl<'a> State<'a> {
                 // we are inside.
                 //
                 // if !decl.inputs.is_empty() {
-                try!(self.print_fn_block_args(&**decl, true));
+                try!(self.print_fn_block_args(&**decl, Some(kind)));
                 try!(space(&mut self.s));
                 // }
 
@@ -2052,13 +2054,17 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_fn_block_args(&mut self,
-                               decl: &ast::FnDecl,
-                               is_unboxed: bool)
-                               -> IoResult<()> {
+    pub fn print_fn_block_args(
+            &mut self,
+            decl: &ast::FnDecl,
+            unboxed_closure_kind: Option<UnboxedClosureKind>)
+            -> IoResult<()> {
         try!(word(&mut self.s, "|"));
-        if is_unboxed {
-            try!(self.word_space("&mut:"));
+        match unboxed_closure_kind {
+            None => {}
+            Some(FnUnboxedClosureKind) => try!(self.word_space("&:")),
+            Some(FnMutUnboxedClosureKind) => try!(self.word_space("&mut:")),
+            Some(FnOnceUnboxedClosureKind) => try!(self.word_space(":")),
         }
         try!(self.print_fn_args(decl, None));
         try!(word(&mut self.s, "|"));
@@ -2148,7 +2154,7 @@ impl<'a> State<'a> {
                                          &None,
                                          None,
                                          None,
-                                         true)
+                                         Some(unboxed_function_type.kind))
                     }
                     OtherRegionTyParamBound(_) => Ok(())
                 })
@@ -2366,7 +2372,8 @@ impl<'a> State<'a> {
                        opt_bounds: &Option<OwnedSlice<ast::TyParamBound>>,
                        generics: Option<&ast::Generics>,
                        opt_explicit_self: Option<ast::ExplicitSelf_>,
-                       is_unboxed: bool)
+                       opt_unboxed_closure_kind:
+                        Option<ast::UnboxedClosureKind>)
                        -> IoResult<()> {
         try!(self.ibox(indent_unit));
 
@@ -2383,7 +2390,7 @@ impl<'a> State<'a> {
             try!(self.print_fn_style(fn_style));
             try!(self.print_opt_abi_and_extern_if_nondefault(opt_abi));
             try!(self.print_onceness(onceness));
-            if !is_unboxed {
+            if opt_unboxed_closure_kind.is_none() {
                 try!(word(&mut self.s, "fn"));
             }
         }
@@ -2399,20 +2406,30 @@ impl<'a> State<'a> {
         match generics { Some(g) => try!(self.print_generics(g)), _ => () }
         try!(zerobreak(&mut self.s));
 
-        if is_unboxed || opt_sigil == Some('&') {
+        if opt_unboxed_closure_kind.is_some() || opt_sigil == Some('&') {
             try!(word(&mut self.s, "|"));
         } else {
             try!(self.popen());
         }
 
-        if is_unboxed {
-            try!(word(&mut self.s, "&mut"));
-            try!(self.word_space(":"));
+        match opt_unboxed_closure_kind {
+            Some(ast::FnUnboxedClosureKind) => {
+                try!(word(&mut self.s, "&"));
+                try!(self.word_space(":"));
+            }
+            Some(ast::FnMutUnboxedClosureKind) => {
+                try!(word(&mut self.s, "&mut"));
+                try!(self.word_space(":"));
+            }
+            Some(ast::FnOnceUnboxedClosureKind) => {
+                try!(self.word_space(":"));
+            }
+            None => {}
         }
 
         try!(self.print_fn_args(decl, opt_explicit_self));
 
-        if is_unboxed || opt_sigil == Some('&') {
+        if opt_unboxed_closure_kind.is_some() || opt_sigil == Some('&') {
             try!(word(&mut self.s, "|"));
         } else {
             if decl.variadic {
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 372cee9ad09..79a870422a6 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -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/compile-fail/borrowck-unboxed-closures.rs b/src/test/compile-fail/borrowck-unboxed-closures.rs
new file mode 100644
index 00000000000..d822bb22e2a
--- /dev/null
+++ b/src/test/compile-fail/borrowck-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.
+
+#![feature(overloaded_calls)]
+
+fn a<F:|&: int, int| -> int>(mut f: F) {
+    let g = &mut f;
+    f(1, 2);    //~ ERROR cannot borrow `f` as immutable
+    //~^ ERROR cannot borrow `f` as immutable
+}
+
+fn b<F:|&mut: int, int| -> int>(f: F) {
+    f(1, 2);    //~ ERROR cannot borrow immutable argument
+}
+
+fn c<F:|: int, int| -> int>(f: F) {
+    f(1, 2);
+    f(1, 2);    //~ ERROR use of moved value
+}
+
+fn main() {}
+
diff --git a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs
new file mode 100644
index 00000000000..1c590db11e3
--- /dev/null
+++ b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs
@@ -0,0 +1,24 @@
+// 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.
+
+#![feature(unboxed_closure_sugar, unboxed_closures, overloaded_calls)]
+
+use std::ops::FnMut;
+
+fn main() {
+    let mut f;
+    {
+        let c = 1;
+        let c_ref = &c;
+        f = |&mut: a: int, b: int| { a + b + *c_ref };
+        //~^ ERROR cannot infer an appropriate lifetime
+    }
+}
+
diff --git a/src/test/compile-fail/unboxed-closures-wrong-trait.rs b/src/test/compile-fail/unboxed-closures-wrong-trait.rs
new file mode 100644
index 00000000000..50d90c6200e
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-wrong-trait.rs
@@ -0,0 +1,22 @@
+// 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.
+
+#![feature(lang_items, overloaded_calls, unboxed_closures)]
+
+fn c<F:|: int, int| -> int>(f: F) -> int {
+    f(5, 6)
+}
+
+fn main() {
+    let z: int = 7;
+    assert_eq!(c(|&: x: int, y| x + y + z), 10);
+    //~^ ERROR failed to find an implementation
+}
+
diff --git a/src/test/run-pass/unboxed-closures-all-traits.rs b/src/test/run-pass/unboxed-closures-all-traits.rs
new file mode 100644
index 00000000000..c362a83e60c
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-all-traits.rs
@@ -0,0 +1,31 @@
+// 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.
+
+#![feature(lang_items, overloaded_calls, unboxed_closures)]
+
+fn a<F:|&: int, int| -> int>(f: F) -> int {
+    f(1, 2)
+}
+
+fn b<F:|&mut: int, int| -> int>(mut f: F) -> int {
+    f(3, 4)
+}
+
+fn c<F:|: int, int| -> int>(f: F) -> int {
+    f(5, 6)
+}
+
+fn main() {
+    let z: int = 7;
+    assert_eq!(a(|&: x: int, y| x + y + z), 10);
+    assert_eq!(b(|&mut: x: int, y| x + y + z), 14);
+    assert_eq!(c(|: x: int, y| x + y + z), 18);
+}
+
diff --git a/src/test/run-pass/unboxed-closures-drop.rs b/src/test/run-pass/unboxed-closures-drop.rs
new file mode 100644
index 00000000000..f20dddcae54
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-drop.rs
@@ -0,0 +1,127 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// A battery of tests to ensure destructors of unboxed closure environments
+// run at the right times.
+
+#![feature(overloaded_calls, unboxed_closures)]
+
+static mut DROP_COUNT: uint = 0;
+
+fn drop_count() -> uint {
+    unsafe {
+        DROP_COUNT
+    }
+}
+
+struct Droppable {
+    x: int,
+}
+
+impl Droppable {
+    fn new() -> Droppable {
+        Droppable {
+            x: 1
+        }
+    }
+}
+
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        unsafe {
+            DROP_COUNT += 1
+        }
+    }
+}
+
+fn a<F:|&: int, int| -> int>(f: F) -> int {
+    f(1, 2)
+}
+
+fn b<F:|&mut: int, int| -> int>(mut f: F) -> int {
+    f(3, 4)
+}
+
+fn c<F:|: int, int| -> int>(f: F) -> int {
+    f(5, 6)
+}
+
+fn test_fn() {
+    {
+        a(|&: a: int, b| { a + b });
+    }
+    assert_eq!(drop_count(), 0);
+
+    {
+        let z = &Droppable::new();
+        a(|&: a: int, b| { z; a + b });
+        assert_eq!(drop_count(), 0);
+    }
+    assert_eq!(drop_count(), 1);
+
+    {
+        let z = &Droppable::new();
+        let zz = &Droppable::new();
+        a(|&: a: int, b| { z; zz; a + b });
+        assert_eq!(drop_count(), 1);
+    }
+    assert_eq!(drop_count(), 3);
+}
+
+fn test_fn_mut() {
+    {
+        b(|&mut: a: int, b| { a + b });
+    }
+    assert_eq!(drop_count(), 3);
+
+    {
+        let z = &Droppable::new();
+        b(|&mut: a: int, b| { z; a + b });
+        assert_eq!(drop_count(), 3);
+    }
+    assert_eq!(drop_count(), 4);
+
+    {
+        let z = &Droppable::new();
+        let zz = &Droppable::new();
+        b(|&mut: a: int, b| { z; zz; a + b });
+        assert_eq!(drop_count(), 4);
+    }
+    assert_eq!(drop_count(), 6);
+}
+
+fn test_fn_once() {
+    {
+        c(|: a: int, b| { a + b });
+    }
+    assert_eq!(drop_count(), 6);
+
+    {
+        let z = Droppable::new();
+        c(|: a: int, b| { z; a + b });
+        assert_eq!(drop_count(), 7);
+    }
+    assert_eq!(drop_count(), 7);
+
+    {
+        let z = Droppable::new();
+        let zz = Droppable::new();
+        c(|: a: int, b| { z; zz; a + b });
+        assert_eq!(drop_count(), 9);
+    }
+    assert_eq!(drop_count(), 9);
+}
+
+fn main() {
+    test_fn();
+    test_fn_mut();
+    test_fn_once();
+}
+
diff --git a/src/test/run-pass/unboxed-closures-single-word-env.rs b/src/test/run-pass/unboxed-closures-single-word-env.rs
new file mode 100644
index 00000000000..754b1f70644
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-single-word-env.rs
@@ -0,0 +1,34 @@
+// 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.
+
+// Ensures that single-word environments work right in unboxed closures.
+// These take a different path in codegen.
+
+#![feature(overloaded_calls, unboxed_closures)]
+
+fn a<F:|&: int, int| -> int>(f: F) -> int {
+    f(1, 2)
+}
+
+fn b<F:|&mut: int, int| -> int>(mut f: F) -> int {
+    f(3, 4)
+}
+
+fn c<F:|: int, int| -> int>(f: F) -> int {
+    f(5, 6)
+}
+
+fn main() {
+    let z = 10;
+    assert_eq!(a(|&: x: int, y| x + y + z), 13);
+    assert_eq!(b(|&mut: x: int, y| x + y + z), 17);
+    assert_eq!(c(|: x: int, y| x + y + z), 21);
+}
+