about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/borrowck/mod.rs9
-rw-r--r--src/librustc/middle/cfg/construct.rs3
-rw-r--r--src/librustc/middle/check_loop.rs5
-rw-r--r--src/librustc/middle/expr_use_visitor.rs3
-rw-r--r--src/librustc/middle/liveness.rs11
-rw-r--r--src/librustc/middle/mem_categorization.rs7
-rw-r--r--src/librustc/middle/resolve.rs13
-rw-r--r--src/librustc/middle/traits/fulfill.rs4
-rw-r--r--src/librustc/middle/ty.rs3
-rw-r--r--src/librustc/middle/typeck/astconv.rs2
-rw-r--r--src/librustc/middle/typeck/check/closure.rs346
-rw-r--r--src/librustc/middle/typeck/check/mod.rs209
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs3
-rw-r--r--src/librustc/middle/typeck/check/writeback.rs5
-rw-r--r--src/librustc_back/svh.rs6
-rw-r--r--src/librustc_trans/save/mod.rs2
-rw-r--r--src/librustc_trans/trans/base.rs5
-rw-r--r--src/librustc_trans/trans/debuginfo.rs8
-rw-r--r--src/librustc_trans/trans/expr.rs26
-rw-r--r--src/libsyntax/ast.rs3
-rw-r--r--src/libsyntax/ast_map/blocks.rs6
-rw-r--r--src/libsyntax/ext/build.rs4
-rw-r--r--src/libsyntax/ext/expand.rs7
-rw-r--r--src/libsyntax/feature_gate.rs2
-rw-r--r--src/libsyntax/fold.rs11
-rw-r--r--src/libsyntax/parse/parser.rs26
-rw-r--r--src/libsyntax/print/pprust.rs47
-rw-r--r--src/libsyntax/visit.rs9
-rw-r--r--src/test/compile-fail/regions-escape-unboxed-closure.rs19
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs29
-rw-r--r--src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs24
-rw-r--r--src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs20
-rw-r--r--src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs24
-rw-r--r--src/test/run-pass/unboxed-closures-infer-kind.rs38
34 files changed, 595 insertions, 344 deletions
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 1ab9baa1ac4..d421b3ebdf3 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -288,9 +288,12 @@ pub fn closure_to_block(closure_id: ast::NodeId,
     match tcx.map.get(closure_id) {
         ast_map::NodeExpr(expr) => match expr.node {
             ast::ExprProc(_, ref block) |
-            ast::ExprFnBlock(_, _, ref block) |
-            ast::ExprUnboxedFn(_, _, _, ref block) => { block.id }
-            _ => panic!("encountered non-closure id: {}", closure_id)
+            ast::ExprClosure(_, _, _, ref block) => {
+                block.id
+            }
+            _ => {
+                panic!("encountered non-closure id: {}", closure_id)
+            }
         },
         _ => panic!("encountered non-expr id: {}", closure_id)
     }
diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs
index e84e70e2cd9..04c8a4fe594 100644
--- a/src/librustc/middle/cfg/construct.rs
+++ b/src/librustc/middle/cfg/construct.rs
@@ -496,9 +496,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
             }
 
             ast::ExprMac(..) |
-            ast::ExprFnBlock(..) |
+            ast::ExprClosure(..) |
             ast::ExprProc(..) |
-            ast::ExprUnboxedFn(..) |
             ast::ExprLit(..) |
             ast::ExprPath(..) => {
                 self.straightline(expr, pred, None::<ast::Expr>.iter())
diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs
index 01ec9bbc13d..36742df9850 100644
--- a/src/librustc/middle/check_loop.rs
+++ b/src/librustc/middle/check_loop.rs
@@ -48,9 +48,8 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> {
                 self.visit_expr(&**e);
                 self.with_context(Loop, |v| v.visit_block(&**b));
             }
-            ast::ExprFnBlock(_, _, ref b) |
-            ast::ExprProc(_, ref b) |
-            ast::ExprUnboxedFn(_, _, _, ref b) => {
+            ast::ExprClosure(_, _, _, ref b) |
+            ast::ExprProc(_, ref b) => {
                 self.with_context(Closure, |v| v.visit_block(&**b));
             }
             ast::ExprBreak(_) => self.require_loop("break", e.span),
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index f4c22c57163..08605f76e02 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -496,8 +496,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                 self.consume_expr(&**count);
             }
 
-            ast::ExprFnBlock(..) |
-            ast::ExprUnboxedFn(..) |
+            ast::ExprClosure(..) |
             ast::ExprProc(..) => {
                 self.walk_captures(expr)
             }
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 82edfdf146e..7d13d2e5f94 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -458,7 +458,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
         }
         visit::walk_expr(ir, expr);
       }
-      ast::ExprFnBlock(..) | ast::ExprProc(..) | ast::ExprUnboxedFn(..) => {
+      ast::ExprClosure(..) | ast::ExprProc(..) => {
         // Interesting control flow (for loops can contain labeled
         // breaks or continues)
         ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
@@ -975,10 +975,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
               self.propagate_through_expr(&**e, succ)
           }
 
-          ast::ExprFnBlock(_, _, ref blk) |
-          ast::ExprProc(_, ref blk) |
-          ast::ExprUnboxedFn(_, _, _, ref blk) => {
-              debug!("{} is an ExprFnBlock, ExprProc, or ExprUnboxedFn",
+          ast::ExprClosure(_, _, _, ref blk) |
+          ast::ExprProc(_, ref blk) => {
+              debug!("{} is an ExprClosure or ExprProc",
                      expr_to_string(expr));
 
               /*
@@ -1495,7 +1494,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
       ast::ExprBreak(..) | ast::ExprAgain(..) | ast::ExprLit(_) |
       ast::ExprBlock(..) | ast::ExprMac(..) | ast::ExprAddrOf(..) |
       ast::ExprStruct(..) | ast::ExprRepeat(..) | ast::ExprParen(..) |
-      ast::ExprFnBlock(..) | ast::ExprProc(..) | ast::ExprUnboxedFn(..) |
+      ast::ExprClosure(..) | ast::ExprProc(..) |
       ast::ExprPath(..) | ast::ExprBox(..) | ast::ExprSlice(..) => {
         visit::walk_expr(this, expr);
       }
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 93c2e8f0d99..3166f6b1495 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -520,8 +520,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
 
           ast::ExprAddrOf(..) | ast::ExprCall(..) |
           ast::ExprAssign(..) | ast::ExprAssignOp(..) |
-          ast::ExprFnBlock(..) | ast::ExprProc(..) |
-          ast::ExprUnboxedFn(..) | ast::ExprRet(..) |
+          ast::ExprClosure(..) | ast::ExprProc(..) |
+          ast::ExprRet(..) |
           ast::ExprUnary(..) | ast::ExprSlice(..) |
           ast::ExprMethodCall(..) | ast::ExprCast(..) |
           ast::ExprVec(..) | ast::ExprTup(..) | ast::ExprIf(..) |
@@ -693,9 +693,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                 };
 
                 match fn_expr.node {
-                    ast::ExprFnBlock(_, _, ref body) |
                     ast::ExprProc(_, ref body) |
-                    ast::ExprUnboxedFn(_, _, _, ref body) => body.id,
+                    ast::ExprClosure(_, _, _, ref body) => body.id,
                     _ => unreachable!()
                 }
             };
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index f369f00a14e..901f602ff30 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -50,8 +50,8 @@ use util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap};
 
 use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate, CrateNum};
 use syntax::ast::{DeclItem, DefId, Expr, ExprAgain, ExprBreak, ExprField};
-use syntax::ast::{ExprFnBlock, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall};
-use syntax::ast::{ExprPath, ExprProc, ExprStruct, ExprUnboxedFn, FnDecl};
+use syntax::ast::{ExprClosure, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall};
+use syntax::ast::{ExprPath, ExprProc, ExprStruct, FnDecl};
 use syntax::ast::{ForeignItem, ForeignItemFn, ForeignItemStatic, Generics};
 use syntax::ast::{Ident, ImplItem, Item, ItemEnum, ItemFn, ItemForeignMod};
 use syntax::ast::{ItemImpl, ItemMac, ItemMod, ItemStatic, ItemStruct};
@@ -5848,24 +5848,19 @@ impl<'a> Resolver<'a> {
                 visit::walk_expr(self, expr);
             }
 
-            ExprFnBlock(capture_clause, ref fn_decl, ref block) => {
+            ExprClosure(capture_clause, _, ref fn_decl, ref block) => {
                 self.capture_mode_map.insert(expr.id, capture_clause);
                 self.resolve_function(ClosureRibKind(expr.id, ast::DUMMY_NODE_ID),
                                       Some(&**fn_decl), NoTypeParameters,
                                       &**block);
             }
+
             ExprProc(ref fn_decl, ref block) => {
                 self.capture_mode_map.insert(expr.id, ast::CaptureByValue);
                 self.resolve_function(ClosureRibKind(expr.id, block.id),
                                       Some(&**fn_decl), NoTypeParameters,
                                       &**block);
             }
-            ExprUnboxedFn(capture_clause, _, ref fn_decl, ref block) => {
-                self.capture_mode_map.insert(expr.id, capture_clause);
-                self.resolve_function(ClosureRibKind(expr.id, block.id),
-                                      Some(&**fn_decl), NoTypeParameters,
-                                      &**block);
-            }
 
             ExprStruct(ref path, _, _) => {
                 // Resolve the path to the structure it goes to. We don't
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 5b8edacb28d..62382ac386f 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -109,6 +109,10 @@ impl<'tcx> FulfillmentContext<'tcx> {
         self.select(&mut selcx, false)
     }
 
+    pub fn pending_trait_obligations(&self) -> &[Obligation<'tcx>] {
+        self.trait_obligations[]
+    }
+
     fn select<'a>(&mut self,
                   selcx: &mut SelectionContext<'a, 'tcx>,
                   only_new_obligations: bool)
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 15c292a6b20..6552a5b4351 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -3919,9 +3919,8 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
         ast::ExprTup(..) |
         ast::ExprIf(..) |
         ast::ExprMatch(..) |
-        ast::ExprFnBlock(..) |
+        ast::ExprClosure(..) |
         ast::ExprProc(..) |
-        ast::ExprUnboxedFn(..) |
         ast::ExprBlock(..) |
         ast::ExprRepeat(..) |
         ast::ExprVec(..) => {
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 637c1f58157..5cd0a7add44 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -995,7 +995,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
             }
             ast::TyInfer => {
                 // TyInfer also appears as the type of arguments or return
-                // values in a ExprFnBlock, ExprProc, or ExprUnboxedFn, or as
+                // values in a ExprClosure or ExprProc, or as
                 // the type of local variables. Both of these cases are
                 // handled specially and will not descend into this routine.
                 this.ty_infer(ast_ty.span)
diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs
new file mode 100644
index 00000000000..51636f00c39
--- /dev/null
+++ b/src/librustc/middle/typeck/check/closure.rs
@@ -0,0 +1,346 @@
+// 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.
+
+/*!
+ * Code for type-checking closure expressions.
+ */
+
+use super::check_fn;
+use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation};
+use super::FnCtxt;
+
+use middle::subst;
+use middle::ty::{mod, Ty};
+use middle::typeck::astconv;
+use middle::typeck::infer;
+use middle::typeck::rscope::RegionScope;
+use syntax::abi;
+use syntax::ast;
+use syntax::ast_util;
+use util::ppaux::Repr;
+
+pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                   expr: &ast::Expr,
+                                   opt_kind: Option<ast::UnboxedClosureKind>,
+                                   decl: &ast::FnDecl,
+                                   body: &ast::Block,
+                                   expected: Expectation<'tcx>) {
+    debug!("check_expr_closure(expr={},expected={})",
+           expr.repr(fcx.tcx()),
+           expected.repr(fcx.tcx()));
+
+    match opt_kind {
+        None => {
+            // If users didn't specify what sort of closure they want,
+            // examine the expected type. For now, if we see explicit
+            // evidence than an unboxed closure is desired, we'll use
+            // that, otherwise we'll fall back to boxed closures.
+            match deduce_unboxed_closure_expectations_from_expectation(fcx, expected) {
+                None => { // doesn't look like an unboxed closure
+                    let region = astconv::opt_ast_region_to_region(fcx,
+                                                                   fcx.infcx(),
+                                                                   expr.span,
+                                                                   &None);
+                    check_boxed_closure(fcx,
+                                        expr,
+                                        ty::RegionTraitStore(region, ast::MutMutable),
+                                        decl,
+                                        body,
+                                        expected);
+                }
+                Some((sig, kind)) => {
+                    check_unboxed_closure(fcx, expr, kind, decl, body, Some(sig));
+                }
+            }
+        }
+
+        Some(kind) => {
+            let kind = match kind {
+                ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind,
+                ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind,
+                ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind,
+            };
+
+            let expected_sig =
+                deduce_unboxed_closure_expectations_from_expectation(fcx, expected)
+                .map(|t| t.0);
+
+            check_unboxed_closure(fcx, expr, kind, decl, body, expected_sig);
+        }
+    }
+}
+
+fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                  expr: &ast::Expr,
+                                  kind: ty::UnboxedClosureKind,
+                                  decl: &ast::FnDecl,
+                                  body: &ast::Block,
+                                  expected_sig: Option<ty::FnSig<'tcx>>) {
+    let expr_def_id = ast_util::local_def(expr.id);
+
+    debug!("check_unboxed_closure kind={} expected_sig={}",
+           kind,
+           expected_sig.repr(fcx.tcx()));
+
+    let mut fn_ty = astconv::ty_of_closure(
+        fcx,
+        ast::NormalFn,
+        ast::Many,
+
+        // The `RegionTraitStore` and region_existential_bounds
+        // are lies, but we ignore them so it doesn't matter.
+        //
+        // FIXME(pcwalton): Refactor this API.
+        ty::region_existential_bound(ty::ReStatic),
+        ty::RegionTraitStore(ty::ReStatic, ast::MutImmutable),
+
+        decl,
+        abi::RustCall,
+        expected_sig);
+
+    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[0],
+    };
+
+    let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx,
+                                              expr_def_id,
+                                              region,
+                                              fcx.inh.param_env.free_substs.clone());
+
+    fcx.write_ty(expr.id, closure_type);
+
+    check_fn(fcx.ccx,
+             ast::NormalFn,
+             expr.id,
+             &fn_ty.sig,
+             decl,
+             expr.id,
+             &*body,
+             fcx.inh);
+
+    // Tuple up the arguments and insert the resulting function type into
+    // the `unboxed_closures` table.
+    fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)];
+
+    debug!("unboxed_closure for {} --> sig={} kind={}",
+           expr_def_id.repr(fcx.tcx()),
+           fn_ty.sig.repr(fcx.tcx()),
+           kind);
+
+    let unboxed_closure = ty::UnboxedClosure {
+        closure_type: fn_ty,
+        kind: kind,
+    };
+
+    fcx.inh
+        .unboxed_closures
+        .borrow_mut()
+        .insert(expr_def_id, unboxed_closure);
+}
+
+fn deduce_unboxed_closure_expectations_from_expectation<'a,'tcx>(
+    fcx: &FnCtxt<'a,'tcx>,
+    expected: Expectation<'tcx>)
+    -> Option<(ty::FnSig<'tcx>,ty::UnboxedClosureKind)>
+{
+    match expected.resolve(fcx) {
+        NoExpectation => None,
+        ExpectCastableToType(t) | ExpectHasType(t) => {
+            deduce_unboxed_closure_expectations_from_expected_type(fcx, t)
+        }
+    }
+}
+
+fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>(
+    fcx: &FnCtxt<'a,'tcx>,
+    expected_ty: Ty<'tcx>)
+    -> Option<(ty::FnSig<'tcx>,ty::UnboxedClosureKind)>
+{
+    match expected_ty.sty {
+        ty::ty_trait(ref object_type) => {
+            deduce_unboxed_closure_expectations_from_trait_ref(fcx, &object_type.principal)
+        }
+        ty::ty_infer(ty::TyVar(vid)) => {
+            deduce_unboxed_closure_expectations_from_obligations(fcx, vid)
+        }
+        _ => {
+            None
+        }
+    }
+}
+
+fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
+    fcx: &FnCtxt<'a,'tcx>,
+    trait_ref: &ty::TraitRef<'tcx>)
+    -> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)>
+{
+    let tcx = fcx.tcx();
+
+    debug!("deduce_unboxed_closure_expectations_from_object_type({})",
+           trait_ref.repr(tcx));
+
+    let def_id_kinds = [
+        (tcx.lang_items.fn_trait(), ty::FnUnboxedClosureKind),
+        (tcx.lang_items.fn_mut_trait(), ty::FnMutUnboxedClosureKind),
+        (tcx.lang_items.fn_once_trait(), ty::FnOnceUnboxedClosureKind),
+    ];
+
+    for &(def_id, kind) in def_id_kinds.iter() {
+        if Some(trait_ref.def_id) == def_id {
+            debug!("found object type {}", kind);
+
+            let arg_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 0);
+            let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(arg_param_ty);
+            debug!("arg_param_ty {}", arg_param_ty.repr(tcx));
+
+            let input_tys = match arg_param_ty.sty {
+                ty::ty_tup(ref tys) => { (*tys).clone() }
+                _ => { continue; }
+            };
+            debug!("input_tys {}", input_tys.repr(tcx));
+
+            let ret_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 1);
+            let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(ret_param_ty);
+            debug!("ret_param_ty {}", ret_param_ty.repr(tcx));
+
+            let fn_sig = ty::FnSig {
+                inputs: input_tys,
+                output: ty::FnConverging(ret_param_ty),
+                variadic: false
+            };
+            debug!("fn_sig {}", fn_sig.repr(tcx));
+
+            return Some((fn_sig, kind));
+        }
+    }
+
+    None
+}
+
+fn deduce_unboxed_closure_expectations_from_obligations<'a,'tcx>(
+    fcx: &FnCtxt<'a,'tcx>,
+    expected_vid: ty::TyVid)
+    -> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)>
+{
+    // Here `expected_ty` is known to be a type inference variable.
+    for obligation in fcx.inh.fulfillment_cx.borrow().pending_trait_obligations().iter() {
+        let obligation_self_ty = fcx.infcx().shallow_resolve(obligation.self_ty());
+        match obligation_self_ty.sty {
+            ty::ty_infer(ty::TyVar(v)) if expected_vid == v => { }
+            _ => { continue; }
+        }
+
+        match deduce_unboxed_closure_expectations_from_trait_ref(fcx, &*obligation.trait_ref) {
+            Some(e) => { return Some(e); }
+            None => { }
+        }
+    }
+
+    None
+}
+
+
+pub fn check_boxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                    expr: &ast::Expr,
+                                    store: ty::TraitStore,
+                                    decl: &ast::FnDecl,
+                                    body: &ast::Block,
+                                    expected: Expectation<'tcx>) {
+    let tcx = fcx.ccx.tcx;
+
+    // Find the expected input/output types (if any). Substitute
+    // fresh bound regions for any bound regions we find in the
+    // expected types so as to avoid capture.
+    let expected_sty = expected.map_to_option(fcx, |x| Some((*x).clone()));
+    let (expected_sig,
+         expected_onceness,
+         expected_bounds) = {
+        match expected_sty {
+            Some(ty::ty_closure(ref cenv)) => {
+                let (sig, _) =
+                    ty::replace_late_bound_regions(
+                        tcx,
+                        &cenv.sig,
+                        |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn));
+                let onceness = match (&store, &cenv.store) {
+                    // As the closure type and onceness go, only three
+                    // combinations are legit:
+                    //      once closure
+                    //      many closure
+                    //      once proc
+                    // If the actual and expected closure type disagree with
+                    // each other, set expected onceness to be always Once or
+                    // Many according to the actual type. Otherwise, it will
+                    // yield either an illegal "many proc" or a less known
+                    // "once closure" in the error message.
+                    (&ty::UniqTraitStore, &ty::UniqTraitStore) |
+                    (&ty::RegionTraitStore(..), &ty::RegionTraitStore(..)) =>
+                        cenv.onceness,
+                    (&ty::UniqTraitStore, _) => ast::Once,
+                    (&ty::RegionTraitStore(..), _) => ast::Many,
+                };
+                (Some(sig), onceness, cenv.bounds)
+            }
+            _ => {
+                // Not an error! Means we're inferring the closure type
+                let (bounds, onceness) = match expr.node {
+                    ast::ExprProc(..) => {
+                        let mut bounds = ty::region_existential_bound(ty::ReStatic);
+                        bounds.builtin_bounds.insert(ty::BoundSend); // FIXME
+                        (bounds, ast::Once)
+                    }
+                    _ => {
+                        let region = fcx.infcx().next_region_var(
+                            infer::AddrOfRegion(expr.span));
+                        (ty::region_existential_bound(region), ast::Many)
+                    }
+                };
+                (None, onceness, bounds)
+            }
+        }
+    };
+
+    // construct the function type
+    let fn_ty = astconv::ty_of_closure(fcx,
+                                       ast::NormalFn,
+                                       expected_onceness,
+                                       expected_bounds,
+                                       store,
+                                       decl,
+                                       abi::Rust,
+                                       expected_sig);
+    let fty_sig = fn_ty.sig.clone();
+    let fty = ty::mk_closure(tcx, fn_ty);
+    debug!("check_expr_fn fty={}", fcx.infcx().ty_to_string(fty));
+
+    fcx.write_ty(expr.id, fty);
+
+    // If the closure is a stack closure and hasn't had some non-standard
+    // style inferred for it, then check it under its parent's style.
+    // Otherwise, use its own
+    let (inherited_style, inherited_style_id) = match store {
+        ty::RegionTraitStore(..) => (fcx.ps.borrow().fn_style,
+                                     fcx.ps.borrow().def),
+        ty::UniqTraitStore => (ast::NormalFn, expr.id)
+    };
+
+    check_fn(fcx.ccx,
+             inherited_style,
+             inherited_style_id,
+             &fty_sig,
+             &*decl,
+             expr.id,
+             &*body,
+             fcx.inh);
+}
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 553d80852c2..1430bf87f4e 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -78,7 +78,7 @@ type parameter).
 
 pub use self::LvaluePreference::*;
 pub use self::DerefArgs::*;
-use self::Expectation::*;
+pub use self::Expectation::*;
 use self::IsBinopAssignment::*;
 use self::TupleArgumentsFlag::*;
 
@@ -97,7 +97,7 @@ use middle::ty::{FnSig, VariantInfo};
 use middle::ty::{Polytype};
 use middle::ty::{Disr, ParamTy, ParameterEnvironment};
 use middle::ty::{mod, Ty};
-use middle::ty::{replace_late_bound_regions, liberate_late_bound_regions};
+use middle::ty::liberate_late_bound_regions;
 use middle::ty_fold::TypeFolder;
 use middle::typeck::astconv::AstConv;
 use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty};
@@ -147,6 +147,7 @@ pub mod regionck;
 pub mod demand;
 pub mod method;
 pub mod wf;
+mod closure;
 
 /// Fields that are part of a `FnCtxt` which are inherited by
 /// closures defined within the function.  For example:
@@ -2833,9 +2834,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         };
         for (i, arg) in args.iter().take(t).enumerate() {
             let is_block = match arg.node {
-                ast::ExprFnBlock(..) |
-                ast::ExprProc(..) |
-                ast::ExprUnboxedFn(..) => true,
+                ast::ExprClosure(..) | ast::ExprProc(..) => true,
                 _ => false
             };
 
@@ -3530,174 +3529,6 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         })
     }
 
-    fn check_unboxed_closure(fcx: &FnCtxt,
-                             expr: &ast::Expr,
-                             kind: ast::UnboxedClosureKind,
-                             decl: &ast::FnDecl,
-                             body: &ast::Block) {
-        let mut fn_ty = astconv::ty_of_closure(
-            fcx,
-            ast::NormalFn,
-            ast::Many,
-
-            // The `RegionTraitStore` and region_existential_bounds
-            // are lies, but we ignore them so it doesn't matter.
-            //
-            // FIXME(pcwalton): Refactor this API.
-            ty::region_existential_bound(ty::ReStatic),
-            ty::RegionTraitStore(ty::ReStatic, ast::MutImmutable),
-
-            decl,
-            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[0],
-        };
-        let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx,
-                                                  local_def(expr.id),
-                                                  region,
-                                                  fcx.inh.param_env.free_substs.clone());
-        fcx.write_ty(expr.id, closure_type);
-
-        check_fn(fcx.ccx,
-                 ast::NormalFn,
-                 expr.id,
-                 &fn_ty.sig,
-                 decl,
-                 expr.id,
-                 &*body,
-                 fcx.inh);
-
-        // Tuple up the arguments and insert the resulting function type into
-        // 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,
-        };
-
-        debug!("unboxed_closure for {} --> sig={} kind={}",
-               local_def(expr.id).repr(fcx.tcx()),
-               fn_ty.sig.repr(fcx.tcx()),
-               kind);
-
-        let unboxed_closure = ty::UnboxedClosure {
-            closure_type: fn_ty,
-            kind: kind,
-        };
-
-        fcx.inh
-           .unboxed_closures
-           .borrow_mut()
-           .insert(local_def(expr.id), unboxed_closure);
-    }
-
-    fn check_expr_fn<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                               expr: &ast::Expr,
-                               store: ty::TraitStore,
-                               decl: &ast::FnDecl,
-                               body: &ast::Block,
-                               expected: Expectation<'tcx>) {
-        let tcx = fcx.ccx.tcx;
-
-        debug!("check_expr_fn(expr={}, expected={})",
-               expr.repr(tcx),
-               expected.repr(tcx));
-
-        // Find the expected input/output types (if any). Substitute
-        // fresh bound regions for any bound regions we find in the
-        // expected types so as to avoid capture.
-        let expected_sty = expected.map_to_option(fcx, |x| Some((*x).clone()));
-        let (expected_sig,
-             expected_onceness,
-             expected_bounds) = {
-            match expected_sty {
-                Some(ty::ty_closure(ref cenv)) => {
-                    let (sig, _) =
-                        replace_late_bound_regions(
-                            tcx,
-                            &cenv.sig,
-                            |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn));
-                    let onceness = match (&store, &cenv.store) {
-                        // As the closure type and onceness go, only three
-                        // combinations are legit:
-                        //      once closure
-                        //      many closure
-                        //      once proc
-                        // If the actual and expected closure type disagree with
-                        // each other, set expected onceness to be always Once or
-                        // Many according to the actual type. Otherwise, it will
-                        // yield either an illegal "many proc" or a less known
-                        // "once closure" in the error message.
-                        (&ty::UniqTraitStore, &ty::UniqTraitStore) |
-                        (&ty::RegionTraitStore(..), &ty::RegionTraitStore(..)) =>
-                            cenv.onceness,
-                        (&ty::UniqTraitStore, _) => ast::Once,
-                        (&ty::RegionTraitStore(..), _) => ast::Many,
-                    };
-                    (Some(sig), onceness, cenv.bounds)
-                }
-                _ => {
-                    // Not an error! Means we're inferring the closure type
-                    let (bounds, onceness) = match expr.node {
-                        ast::ExprProc(..) => {
-                            let mut bounds = ty::region_existential_bound(ty::ReStatic);
-                            bounds.builtin_bounds.insert(ty::BoundSend); // FIXME
-                            (bounds, ast::Once)
-                        }
-                        _ => {
-                            let region = fcx.infcx().next_region_var(
-                                infer::AddrOfRegion(expr.span));
-                            (ty::region_existential_bound(region), ast::Many)
-                        }
-                    };
-                    (None, onceness, bounds)
-                }
-            }
-        };
-
-        // construct the function type
-        let fn_ty = astconv::ty_of_closure(fcx,
-                                           ast::NormalFn,
-                                           expected_onceness,
-                                           expected_bounds,
-                                           store,
-                                           decl,
-                                           abi::Rust,
-                                           expected_sig);
-        let fty_sig = fn_ty.sig.clone();
-        let fty = ty::mk_closure(tcx, fn_ty);
-        debug!("check_expr_fn fty={}", fcx.infcx().ty_to_string(fty));
-
-        fcx.write_ty(expr.id, fty);
-
-        // If the closure is a stack closure and hasn't had some non-standard
-        // style inferred for it, then check it under its parent's style.
-        // Otherwise, use its own
-        let (inherited_style, inherited_style_id) = match store {
-            ty::RegionTraitStore(..) => (fcx.ps.borrow().fn_style,
-                                         fcx.ps.borrow().def),
-            ty::UniqTraitStore => (ast::NormalFn, expr.id)
-        };
-
-        check_fn(fcx.ccx,
-                 inherited_style,
-                 inherited_style_id,
-                 &fty_sig,
-                 &*decl,
-                 expr.id,
-                 &*body,
-                 fcx.inh);
-    }
-
-
     // Check field access expressions
     fn check_field(fcx: &FnCtxt,
                    expr: &ast::Expr,
@@ -4326,32 +4157,16 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
       ast::ExprMatch(ref discrim, ref arms, _) => {
         _match::check_match(fcx, expr, &**discrim, arms.as_slice());
       }
-      ast::ExprFnBlock(_, ref decl, ref body) => {
-        let region = astconv::opt_ast_region_to_region(fcx,
-                                                       fcx.infcx(),
-                                                       expr.span,
-                                                       &None);
-        check_expr_fn(fcx,
-                      expr,
-                      ty::RegionTraitStore(region, ast::MutMutable),
-                      &**decl,
-                      &**body,
-                      expected);
-      }
-      ast::ExprUnboxedFn(_, kind, ref decl, ref body) => {
-        check_unboxed_closure(fcx,
-                              expr,
-                              kind,
-                              &**decl,
-                              &**body);
+      ast::ExprClosure(_, opt_kind, ref decl, ref body) => {
+          closure::check_expr_closure(fcx, expr, opt_kind, &**decl, &**body, expected);
       }
       ast::ExprProc(ref decl, ref body) => {
-        check_expr_fn(fcx,
-                      expr,
-                      ty::UniqTraitStore,
-                      &**decl,
-                      &**body,
-                      expected);
+          closure::check_boxed_closure(fcx,
+                                       expr,
+                                       ty::UniqTraitStore,
+                                       &**decl,
+                                       &**body,
+                                       expected);
       }
       ast::ExprBlock(ref b) => {
         check_block_with_expected(fcx, &**b, expected);
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index 44c11318038..0a5af8baeac 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -742,9 +742,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
             visit::walk_expr(rcx, expr);
         }
 
-        ast::ExprFnBlock(_, _, ref body) |
         ast::ExprProc(_, ref body) |
-        ast::ExprUnboxedFn(_, _, _, ref body) => {
+        ast::ExprClosure(_, _, _, ref body) => {
             check_expr_fn_block(rcx, expr, &**body);
         }
 
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index 76d9ed15e51..23af30b44d9 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -122,9 +122,8 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> {
                                     MethodCall::expr(e.id));
 
         match e.node {
-            ast::ExprFnBlock(_, ref decl, _) |
-            ast::ExprProc(ref decl, _) |
-            ast::ExprUnboxedFn(_, _, ref decl, _) => {
+            ast::ExprClosure(_, _, ref decl, _) |
+            ast::ExprProc(ref decl, _) => {
                 for input in decl.inputs.iter() {
                     let _ = self.visit_node_id(ResolvingExpr(e.span),
                                                input.id);
diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs
index 2cca63bd65d..cda8a1b1b5f 100644
--- a/src/librustc_back/svh.rs
+++ b/src/librustc_back/svh.rs
@@ -241,8 +241,7 @@ mod svh_visitor {
         SawExprIf,
         SawExprWhile,
         SawExprMatch,
-        SawExprFnBlock,
-        SawExprUnboxedFn,
+        SawExprClosure,
         SawExprProc,
         SawExprBlock,
         SawExprAssign,
@@ -274,8 +273,7 @@ mod svh_visitor {
             ExprWhile(..)            => SawExprWhile,
             ExprLoop(_, id)          => SawExprLoop(id.map(content)),
             ExprMatch(..)            => SawExprMatch,
-            ExprFnBlock(..)          => SawExprFnBlock,
-            ExprUnboxedFn(..)        => SawExprUnboxedFn,
+            ExprClosure(..)          => SawExprClosure,
             ExprProc(..)             => SawExprProc,
             ExprBlock(..)            => SawExprBlock,
             ExprAssign(..)           => SawExprAssign,
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 2591051e9b9..67ed95f83fd 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -1345,7 +1345,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
                                             "Expected struct type, but not ty_struct"),
                 }
             },
-            ast::ExprFnBlock(_, ref decl, ref body) => {
+            ast::ExprClosure(_, _, ref decl, ref body) => {
                 if generated_code(body.span) {
                     return
                 }
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index d8cc475c1df..c30ad781cc9 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -1385,9 +1385,8 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
         }
         Some(ast_map::NodeExpr(e)) => {
             match e.node {
-                ast::ExprFnBlock(_, _, ref blk) |
-                ast::ExprProc(_, ref blk) |
-                ast::ExprUnboxedFn(_, _, _, ref blk) => {
+                ast::ExprClosure(_, _, _, ref blk) |
+                ast::ExprProc(_, ref blk) => {
                     let mut explicit = CheckForNestedReturnsVisitor::explicit();
                     let mut implicit = CheckForNestedReturnsVisitor::implicit();
                     visit::walk_expr(&mut explicit, e);
diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index 7b3f619f41f..075b6b0dd6e 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -1232,9 +1232,8 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         }
         ast_map::NodeExpr(ref expr) => {
             match expr.node {
-                ast::ExprFnBlock(_, ref fn_decl, ref top_level_block) |
                 ast::ExprProc(ref fn_decl, ref top_level_block) |
-                ast::ExprUnboxedFn(_, _, ref fn_decl, ref top_level_block) => {
+                ast::ExprClosure(_, _, ref fn_decl, ref top_level_block) => {
                     let name = format!("fn{}", token::gensym("fn"));
                     let name = token::str_to_ident(name.as_slice());
                     (name, &**fn_decl,
@@ -1310,7 +1309,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                                       file_metadata,
                                                       &mut function_name);
 
-    // There is no ast_map::Path for ast::ExprFnBlock-type functions. For now,
+    // There is no ast_map::Path for ast::ExprClosure-type functions. For now,
     // just don't put them into a namespace. In the future this could be improved
     // somehow (storing a path in the ast_map, or construct a path using the
     // enclosing function).
@@ -3578,9 +3577,8 @@ 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::ExprClosure(_, _, ref decl, ref block) => {
                 with_new_scope(cx,
                                block.span,
                                scope_stack,
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 44c37793b4b..670e893cc0e 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -77,6 +77,7 @@ use trans::machine::{llsize_of, llsize_of_alloc};
 use trans::type_::Type;
 
 use syntax::ast;
+use syntax::ast_util;
 use syntax::codemap;
 use syntax::print::pprust::{expr_to_string};
 use syntax::ptr::P;
@@ -1070,16 +1071,23 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         ast::ExprVec(..) | ast::ExprRepeat(..) => {
             tvec::trans_fixed_vstore(bcx, expr, dest)
         }
-        ast::ExprFnBlock(_, ref decl, ref body) |
+        ast::ExprClosure(_, _, ref decl, ref body) |
         ast::ExprProc(ref decl, ref body) => {
-            let expr_ty = expr_ty(bcx, expr);
-            let store = ty::ty_closure_store(expr_ty);
-            debug!("translating block function {} with type {}",
-                   expr_to_string(expr), expr_ty.repr(tcx));
-            closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest)
-        }
-        ast::ExprUnboxedFn(_, _, ref decl, ref body) => {
-            closure::trans_unboxed_closure(bcx, &**decl, &**body, expr.id, dest)
+            // Check the side-table to see whether this is an unboxed
+            // closure or an older, legacy style closure. Store this
+            // into a variable to ensure the the RefCell-lock is
+            // released before we recurse.
+            let is_unboxed_closure =
+                bcx.tcx().unboxed_closures.borrow().contains_key(&ast_util::local_def(expr.id));
+            if is_unboxed_closure {
+                closure::trans_unboxed_closure(bcx, &**decl, &**body, expr.id, dest)
+            } else {
+                let expr_ty = expr_ty(bcx, expr);
+                let store = ty::ty_closure_store(expr_ty);
+                debug!("translating block function {} with type {}",
+                       expr_to_string(expr), expr_ty.repr(tcx));
+                closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest)
+            }
         }
         ast::ExprCall(ref f, ref args) => {
             if bcx.tcx().is_method_call(expr.id) {
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index e3058c0a248..5d4fd2704a2 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -667,9 +667,8 @@ pub enum Expr_ {
     // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
     ExprLoop(P<Block>, Option<Ident>),
     ExprMatch(P<Expr>, Vec<Arm>, MatchSource),
-    ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>),
+    ExprClosure(CaptureClause, Option<UnboxedClosureKind>, P<FnDecl>, P<Block>),
     ExprProc(P<FnDecl>, P<Block>),
-    ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>),
     ExprBlock(P<Block>),
 
     ExprAssign(P<Expr>, P<Expr>),
diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs
index a35ee3ab1d0..8db12fbd835 100644
--- a/src/libsyntax/ast_map/blocks.rs
+++ b/src/libsyntax/ast_map/blocks.rs
@@ -37,7 +37,7 @@ use visit;
 ///
 /// More specifically, it is one of either:
 ///   - A function item,
-///   - A closure expr (i.e. an ExprFnBlock or ExprProc), or
+///   - A closure expr (i.e. an ExprClosure or ExprProc), or
 ///   - The default implementation for a trait method.
 ///
 /// To construct one, use the `Code::from_node` function.
@@ -71,7 +71,7 @@ impl MaybeFnLike for ast::TraitItem {
 impl MaybeFnLike for ast::Expr {
     fn is_fn_like(&self) -> bool {
         match self.node {
-            ast::ExprFnBlock(..) | ast::ExprProc(..) => true,
+            ast::ExprClosure(..) | ast::ExprProc(..) => true,
             _ => false,
         }
     }
@@ -215,7 +215,7 @@ impl<'a> FnLikeNode<'a> {
                 }
             }
             ast_map::NodeExpr(e) => match e.node {
-                ast::ExprFnBlock(_, ref decl, ref block) =>
+                ast::ExprClosure(_, _, ref decl, ref block) =>
                     closure(ClosureParts::new(&**decl, &**block, e.id, e.span)),
                 ast::ExprProc(ref decl, ref block) =>
                     closure(ClosureParts::new(&**decl, &**block, e.id, e.span)),
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index ffc42b67033..b18a0c8411c 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -864,14 +864,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
 
     fn lambda_fn_decl(&self, span: Span,
                       fn_decl: P<ast::FnDecl>, blk: P<ast::Block>) -> P<ast::Expr> {
-        self.expr(span, ast::ExprFnBlock(ast::CaptureByRef, fn_decl, blk))
+        self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
     }
     fn lambda(&self, span: Span, ids: Vec<ast::Ident>, blk: P<ast::Block>) -> P<ast::Expr> {
         let fn_decl = self.fn_decl(
             ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(),
             self.ty_infer(span));
 
-        self.expr(span, ast::ExprFnBlock(ast::CaptureByRef, fn_decl, blk))
+        self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
     }
     fn lambda0(&self, span: Span, blk: P<ast::Block>) -> P<ast::Expr> {
         self.lambda(span, Vec::new(), blk)
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 081456bebe1..04132679a03 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -207,10 +207,11 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             fld.cx.expr(span, ast::ExprForLoop(pat, head, body, opt_ident))
         }
 
-        ast::ExprFnBlock(capture_clause, fn_decl, block) => {
+        ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => {
             let (rewritten_fn_decl, rewritten_block)
                 = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
-            let new_node = ast::ExprFnBlock(capture_clause,
+            let new_node = ast::ExprClosure(capture_clause,
+                                            opt_kind,
                                             rewritten_fn_decl,
                                             rewritten_block);
             P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)})
@@ -1555,7 +1556,7 @@ mod test {
             0)
     }
 
-    // closure arg hygiene (ExprFnBlock)
+    // closure arg hygiene (ExprClosure)
     // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);}
     #[test] fn closure_arg_hygiene(){
         run_renaming_test(
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 460a94a8d5a..9635f0175f0 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -309,7 +309,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
 
     fn visit_expr(&mut self, e: &ast::Expr) {
         match e.node {
-            ast::ExprUnboxedFn(..) => {
+            ast::ExprClosure(_, Some(_), _, _) => {
                 self.gate_feature("unboxed_closures",
                                   e.span,
                                   "unboxed closures are a work-in-progress \
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 2e6ee49f0ff..1bdf9ea73df 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1326,18 +1326,13 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
                         arms.move_map(|x| folder.fold_arm(x)),
                         source)
             }
-            ExprFnBlock(capture_clause, decl, body) => {
-                ExprFnBlock(capture_clause,
-                            folder.fold_fn_decl(decl),
-                            folder.fold_block(body))
-            }
             ExprProc(decl, body) => {
                 ExprProc(folder.fold_fn_decl(decl),
                          folder.fold_block(body))
             }
-            ExprUnboxedFn(capture_clause, kind, decl, body) => {
-                ExprUnboxedFn(capture_clause,
-                            kind,
+            ExprClosure(capture_clause, opt_kind, decl, body) => {
+                ExprClosure(capture_clause,
+                            opt_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 a6fe3902395..ab0543d64b7 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -25,10 +25,10 @@ use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, Explicit
 use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
 use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox};
 use ast::{ExprBreak, ExprCall, ExprCast};
-use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex, ExprSlice};
+use ast::{ExprField, ExprTupField, ExprClosure, ExprIf, ExprIfLet, ExprIndex, ExprSlice};
 use ast::{ExprLit, ExprLoop, ExprMac};
 use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc};
-use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn};
+use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary};
 use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl};
 use ast::{Once, Many};
 use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind};
@@ -2999,7 +2999,8 @@ impl<'a> Parser<'a> {
 
     // `|args| expr`
     pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause)
-                             -> P<Expr> {
+                             -> P<Expr>
+    {
         let lo = self.span.lo;
         let (decl, optional_unboxed_closure_kind) =
             self.parse_fn_block_decl();
@@ -3013,21 +3014,10 @@ impl<'a> Parser<'a> {
             rules: DefaultBlock,
         });
 
-        match optional_unboxed_closure_kind {
-            Some(unboxed_closure_kind) => {
-                self.mk_expr(lo,
-                             fakeblock.span.hi,
-                             ExprUnboxedFn(capture_clause,
-                                           unboxed_closure_kind,
-                                           decl,
-                                           fakeblock))
-            }
-            None => {
-                self.mk_expr(lo,
-                             fakeblock.span.hi,
-                             ExprFnBlock(capture_clause, decl, fakeblock))
-            }
-        }
+        self.mk_expr(
+            lo,
+            fakeblock.span.hi,
+            ExprClosure(capture_clause, optional_unboxed_closure_kind, decl, fakeblock))
     }
 
     pub fn parse_else_expr(&mut self) -> P<Expr> {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 8e7804aaa71..5652a9a9d3a 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -444,9 +444,8 @@ pub fn visibility_qualified(vis: ast::Visibility, s: &str) -> String {
 fn needs_parentheses(expr: &ast::Expr) -> bool {
     match expr.node {
         ast::ExprAssign(..) | ast::ExprBinary(..) |
-        ast::ExprFnBlock(..) | ast::ExprProc(..) |
-        ast::ExprUnboxedFn(..) | ast::ExprAssignOp(..) |
-        ast::ExprCast(..) => true,
+        ast::ExprClosure(..) | ast::ExprProc(..) |
+        ast::ExprAssignOp(..) | ast::ExprCast(..) => true,
         _ => false,
     }
 }
@@ -1662,49 +1661,11 @@ impl<'a> State<'a> {
                 }
                 try!(self.bclose_(expr.span, indent_unit));
             }
-            ast::ExprFnBlock(capture_clause, ref decl, ref body) => {
+            ast::ExprClosure(capture_clause, opt_kind, ref decl, ref body) => {
                 try!(self.print_capture_clause(capture_clause));
 
-                // in do/for blocks we don't want to show an empty
-                // argument list, but at this point we don't know which
-                // we are inside.
-                //
-                // if !decl.inputs.is_empty() {
-                try!(self.print_fn_block_args(&**decl, None));
+                try!(self.print_fn_block_args(&**decl, opt_kind));
                 try!(space(&mut self.s));
-                // }
-
-                if !body.stmts.is_empty() || !body.expr.is_some() {
-                    try!(self.print_block_unclosed(&**body));
-                } else {
-                    // we extract the block, so as not to create another set of boxes
-                    match body.expr.as_ref().unwrap().node {
-                        ast::ExprBlock(ref blk) => {
-                            try!(self.print_block_unclosed(&**blk));
-                        }
-                        _ => {
-                            // this is a bare expression
-                            try!(self.print_expr(&**body.expr.as_ref().unwrap()));
-                            try!(self.end()); // need to close a box
-                        }
-                    }
-                }
-                // a box will be closed by print_expr, but we didn't want an overall
-                // wrapper so we closed the corresponding opening. so create an
-                // empty box to satisfy the close.
-                try!(self.ibox(0));
-            }
-            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
-                // argument list, but at this point we don't know which
-                // we are inside.
-                //
-                // if !decl.inputs.is_empty() {
-                try!(self.print_fn_block_args(&**decl, Some(kind)));
-                try!(space(&mut self.s));
-                // }
 
                 if !body.stmts.is_empty() || !body.expr.is_some() {
                     try!(self.print_block_unclosed(&**body));
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 41a7ce7d78e..a0bdd739113 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -815,14 +815,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
                 visitor.visit_arm(arm)
             }
         }
-        ExprFnBlock(_, ref function_declaration, ref body) => {
-            visitor.visit_fn(FkFnBlock,
-                             &**function_declaration,
-                             &**body,
-                             expression.span,
-                             expression.id)
-        }
-        ExprUnboxedFn(_, _, ref function_declaration, ref body) => {
+        ExprClosure(_, _, ref function_declaration, ref body) => {
             visitor.visit_fn(FkFnBlock,
                              &**function_declaration,
                              &**body,
diff --git a/src/test/compile-fail/regions-escape-unboxed-closure.rs b/src/test/compile-fail/regions-escape-unboxed-closure.rs
new file mode 100644
index 00000000000..70f0d61b5ee
--- /dev/null
+++ b/src/test/compile-fail/regions-escape-unboxed-closure.rs
@@ -0,0 +1,19 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(unboxed_closures)]
+
+fn with_int(f: &mut FnMut(&int)) {
+}
+
+fn main() {
+    let mut x: Option<&int> = None;
+    with_int(&mut |&mut: y| x = Some(y));   //~ ERROR cannot infer
+}
diff --git a/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs
new file mode 100644
index 00000000000..72109b22957
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.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.
+
+// That a closure whose expected argument types include two distinct
+// bound regions.
+
+#![feature(unboxed_closures)]
+
+use std::cell::Cell;
+
+fn doit<T,F>(val: T, f: &F)
+    where F : Fn(&Cell<&T>, &T)
+{
+    let x = Cell::new(&val);
+    f.call((&x,&val))
+}
+
+pub fn main() {
+    doit(0i, &|&: x, y| {
+        x.set(y); //~ ERROR cannot infer
+    });
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs
new file mode 100644
index 00000000000..465c324122a
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.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.
+
+// Test that we are able to infer that the type of `x` is `int` based
+// on the expected type from the object.
+
+#![feature(unboxed_closures)]
+
+fn doit<T,F>(val: T, f: &F)
+    where F : Fn(T)
+{
+    f.call((val,))
+}
+
+pub fn main() {
+    doit(0i, &|&: x /*: int*/ | { x.to_int(); });
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs
new file mode 100644
index 00000000000..440292d202e
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs
@@ -0,0 +1,20 @@
+// 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.
+
+// Test that we are able to infer that the type of `x` is `int` based
+// on the expected type from the object.
+
+#![feature(unboxed_closures)]
+
+fn doit<T>(val: T, f: &Fn(T)) { f.call((val,)) }
+
+pub fn main() {
+    doit(0i, &|&: x /*: int*/ | { x.to_int(); });
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs
new file mode 100644
index 00000000000..b279eb5fbba
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.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.
+
+// Test that we are able to infer that the type of `x` is `int` based
+// on the expected type from the object.
+
+#![feature(unboxed_closures)]
+
+fn doit<T,F>(val: T, f: &F)
+    where F : Fn(&T)
+{
+    f.call((&val,))
+}
+
+pub fn main() {
+    doit(0i, &|&: x /*: int*/ | { x.to_int(); });
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-kind.rs b/src/test/run-pass/unboxed-closures-infer-kind.rs
new file mode 100644
index 00000000000..0cb719ecd7f
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-kind.rs
@@ -0,0 +1,38 @@
+// 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.
+
+// Test that we can infer the "kind" of an unboxed closure based on
+// the expected type.
+
+#![feature(unboxed_closures)]
+
+// Test by-ref capture of environment in unboxed closure types
+
+fn call_fn<F: Fn()>(f: F) {
+    f()
+}
+
+fn call_fn_mut<F: FnMut()>(mut f: F) {
+    f()
+}
+
+fn call_fn_once<F: FnOnce()>(f: F) {
+    f()
+}
+
+fn main() {
+    let mut x = 0u;
+    let y = 2u;
+
+    call_fn(|| assert_eq!(x, 0));
+    call_fn_mut(|| x += y);
+    call_fn_once(|| x += y);
+    assert_eq!(x, y * 2);
+}