about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/metadata/common.rs14
-rw-r--r--src/librustc/metadata/encoder.rs37
-rw-r--r--src/librustc/middle/astencode.rs97
-rw-r--r--src/librustc/middle/expr_use_visitor.rs6
-rw-r--r--src/librustc/middle/mem_categorization.rs12
-rw-r--r--src/librustc/middle/traits/select.rs17
-rw-r--r--src/librustc/middle/ty.rs85
-rw-r--r--src/librustc/util/ppaux.rs6
-rw-r--r--src/librustc_trans/trans/base.rs2
-rw-r--r--src/librustc_trans/trans/closure.rs2
-rw-r--r--src/librustc_trans/trans/common.rs12
-rw-r--r--src/librustc_typeck/check/_match.rs31
-rw-r--r--src/librustc_typeck/check/callee.rs204
-rw-r--r--src/librustc_typeck/check/closure.rs45
-rw-r--r--src/librustc_typeck/check/method/confirm.rs12
-rw-r--r--src/librustc_typeck/check/method/mod.rs14
-rw-r--r--src/librustc_typeck/check/method/probe.rs68
-rw-r--r--src/librustc_typeck/check/method/suggest.rs14
-rw-r--r--src/librustc_typeck/check/mod.rs249
-rw-r--r--src/librustc_typeck/check/upvar.rs213
-rw-r--r--src/librustc_typeck/check/vtable.rs14
-rw-r--r--src/librustc_typeck/check/writeback.rs15
-rw-r--r--src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs33
-rw-r--r--src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs39
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs18
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs32
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs18
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs18
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs21
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs21
-rw-r--r--src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs48
-rw-r--r--src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs29
-rw-r--r--src/test/run-pass/unboxed-closures-infer-fnmut-move.rs25
-rw-r--r--src/test/run-pass/unboxed-closures-infer-fnmut.rs24
-rw-r--r--src/test/run-pass/unboxed-closures-infer-fnonce-move.rs37
-rw-r--r--src/test/run-pass/unboxed-closures-infer-fnonce.rs37
-rw-r--r--src/test/run-pass/unboxed-closures-infer-recursive-fn.rs47
37 files changed, 1201 insertions, 415 deletions
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 242ab630a20..bf2f5bd22c4 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -139,10 +139,11 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f
     tag_table_adjustments = 0x51,
     tag_table_moves_map = 0x52,
     tag_table_capture_map = 0x53,
-    tag_table_closures = 0x54,
-    tag_table_upvar_capture_map = 0x55,
-    tag_table_capture_modes = 0x56,
-    tag_table_object_cast_map = 0x57,
+    tag_table_closure_tys = 0x54,
+    tag_table_closure_kinds = 0x55,
+    tag_table_upvar_capture_map = 0x56,
+    tag_table_capture_modes = 0x57,
+    tag_table_object_cast_map = 0x58,
 }
 
 static first_astencode_tag: uint = tag_ast as uint;
@@ -225,10 +226,7 @@ pub struct LinkMeta {
     pub crate_hash: Svh,
 }
 
-pub const tag_closures: uint = 0x95;
-pub const tag_closure: uint = 0x96;
-pub const tag_closure_type: uint = 0x97;
-pub const tag_closure_kind: uint = 0x98;
+// GAP 0x94...0x98
 
 pub const tag_struct_fields: uint = 0x99;
 pub const tag_struct_field: uint = 0x9a;
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 6767f77de84..e09d29b98b0 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -618,17 +618,6 @@ fn encode_visibility(rbml_w: &mut Encoder, visibility: ast::Visibility) {
     rbml_w.end_tag();
 }
 
-fn encode_closure_kind(rbml_w: &mut Encoder, kind: ty::ClosureKind) {
-    rbml_w.start_tag(tag_closure_kind);
-    let ch = match kind {
-        ty::FnClosureKind => 'f',
-        ty::FnMutClosureKind => 'm',
-        ty::FnOnceClosureKind => 'o',
-    };
-    rbml_w.wr_str(&ch.to_string()[]);
-    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);
@@ -1843,24 +1832,6 @@ fn encode_macro_defs(rbml_w: &mut Encoder,
     rbml_w.end_tag();
 }
 
-fn encode_closures<'a>(ecx: &'a EncodeContext, rbml_w: &'a mut Encoder) {
-    rbml_w.start_tag(tag_closures);
-    for (closure_id, closure) in ecx.tcx.closures.borrow().iter() {
-        if closure_id.krate != ast::LOCAL_CRATE {
-            continue
-        }
-
-        rbml_w.start_tag(tag_closure);
-        encode_def_id(rbml_w, *closure_id);
-        rbml_w.start_tag(tag_closure_type);
-        write_closure_type(ecx, rbml_w, &closure.closure_type);
-        rbml_w.end_tag();
-        encode_closure_kind(rbml_w, closure.kind);
-        rbml_w.end_tag();
-    }
-    rbml_w.end_tag();
-}
-
 fn encode_struct_field_attrs(rbml_w: &mut Encoder, krate: &ast::Crate) {
     struct StructFieldVisitor<'a, 'b:'a> {
         rbml_w: &'a mut Encoder<'b>,
@@ -2069,7 +2040,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
         native_lib_bytes: u64,
         plugin_registrar_fn_bytes: u64,
         macro_defs_bytes: u64,
-        closure_bytes: u64,
         impl_bytes: u64,
         misc_bytes: u64,
         item_bytes: u64,
@@ -2084,7 +2054,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
         native_lib_bytes: 0,
         plugin_registrar_fn_bytes: 0,
         macro_defs_bytes: 0,
-        closure_bytes: 0,
         impl_bytes: 0,
         misc_bytes: 0,
         item_bytes: 0,
@@ -2154,11 +2123,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
     encode_macro_defs(&mut rbml_w, krate);
     stats.macro_defs_bytes = rbml_w.writer.tell().unwrap() - i;
 
-    // Encode the types of all closures in this crate.
-    i = rbml_w.writer.tell().unwrap();
-    encode_closures(&ecx, &mut rbml_w);
-    stats.closure_bytes = rbml_w.writer.tell().unwrap() - i;
-
     // Encode the def IDs of impls, for coherence checking.
     i = rbml_w.writer.tell().unwrap();
     encode_impls(&ecx, krate, &mut rbml_w);
@@ -2199,7 +2163,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
         println!("          native bytes: {}", stats.native_lib_bytes);
         println!("plugin registrar bytes: {}", stats.plugin_registrar_fn_bytes);
         println!("       macro def bytes: {}", stats.macro_defs_bytes);
-        println!("         closure bytes: {}", stats.closure_bytes);
         println!("            impl bytes: {}", stats.impl_bytes);
         println!("            misc bytes: {}", stats.misc_bytes);
         println!("            item bytes: {}", stats.item_bytes);
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 902c029fef4..37643247341 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -647,30 +647,7 @@ impl<'tcx> tr for MethodOrigin<'tcx> {
 }
 
 pub fn encode_closure_kind(ebml_w: &mut Encoder, kind: ty::ClosureKind) {
-    use serialize::Encoder;
-
-    ebml_w.emit_enum("ClosureKind", |ebml_w| {
-        match kind {
-            ty::FnClosureKind => {
-                ebml_w.emit_enum_variant("FnClosureKind", 0, 3, |_| {
-                    Ok(())
-                })
-            }
-            ty::FnMutClosureKind => {
-                ebml_w.emit_enum_variant("FnMutClosureKind", 1, 3, |_| {
-                    Ok(())
-                })
-            }
-            ty::FnOnceClosureKind => {
-                ebml_w.emit_enum_variant("FnOnceClosureKind",
-                                         2,
-                                         3,
-                                         |_| {
-                    Ok(())
-                })
-            }
-        }
-    }).unwrap()
+    kind.encode(ebml_w).unwrap();
 }
 
 pub trait vtable_decoder_helpers<'tcx> {
@@ -1310,12 +1287,20 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
         })
     }
 
-    for closure in tcx.closures.borrow().get(&ast_util::local_def(id)).iter() {
-        rbml_w.tag(c::tag_table_closures, |rbml_w| {
+    for &closure_type in tcx.closure_tys.borrow().get(&ast_util::local_def(id)).iter() {
+        rbml_w.tag(c::tag_table_closure_tys, |rbml_w| {
+            rbml_w.id(id);
+            rbml_w.tag(c::tag_table_val, |rbml_w| {
+                rbml_w.emit_closure_type(ecx, closure_type);
+            })
+        })
+    }
+
+    for &&closure_kind in tcx.closure_kinds.borrow().get(&ast_util::local_def(id)).iter() {
+        rbml_w.tag(c::tag_table_closure_kinds, |rbml_w| {
             rbml_w.id(id);
             rbml_w.tag(c::tag_table_val, |rbml_w| {
-                rbml_w.emit_closure_type(ecx, &closure.closure_type);
-                encode_closure_kind(rbml_w, closure.kind)
+                encode_closure_kind(rbml_w, closure_kind)
             })
         })
     }
@@ -1354,8 +1339,10 @@ trait rbml_decoder_decoder_helpers<'tcx> {
                            -> subst::Substs<'tcx>;
     fn read_auto_adjustment<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
                                     -> ty::AutoAdjustment<'tcx>;
-    fn read_closure<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-                            -> ty::Closure<'tcx>;
+    fn read_closure_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
+                                 -> ty::ClosureKind;
+    fn read_closure_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
+                               -> ty::ClosureTy<'tcx>;
     fn read_auto_deref_ref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
                                    -> ty::AutoDerefRef<'tcx>;
     fn read_autoref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
@@ -1782,35 +1769,23 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
         }).unwrap()
     }
 
-    fn read_closure<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
-                            -> ty::Closure<'tcx> {
-        let closure_type = self.read_opaque(|this, doc| {
+    fn read_closure_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>)
+                                 -> ty::ClosureKind
+    {
+        Decodable::decode(self).ok().unwrap()
+    }
+
+    fn read_closure_ty<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
+                               -> ty::ClosureTy<'tcx>
+    {
+        self.read_opaque(|this, doc| {
             Ok(tydecode::parse_ty_closure_data(
                 doc.data,
                 dcx.cdata.cnum,
                 doc.start,
                 dcx.tcx,
                 |s, a| this.convert_def_id(dcx, s, a)))
-        }).unwrap();
-        let variants = &[
-            "FnClosureKind",
-            "FnMutClosureKind",
-            "FnOnceClosureKind"
-        ];
-        let kind = self.read_enum("ClosureKind", |this| {
-            this.read_enum_variant(variants, |_, i| {
-                Ok(match i {
-                    0 => ty::FnClosureKind,
-                    1 => ty::FnMutClosureKind,
-                    2 => ty::FnOnceClosureKind,
-                    _ => panic!("bad enum variant for ty::ClosureKind"),
-                })
-            })
-        }).unwrap();
-        ty::Closure {
-            closure_type: closure_type,
-            kind: kind,
-        }
+        }).unwrap()
     }
 
     /// Converts a def-id that appears in a type.  The correct
@@ -1937,11 +1912,17 @@ fn decode_side_tables(dcx: &DecodeContext,
                         let adj: ty::AutoAdjustment = val_dsr.read_auto_adjustment(dcx);
                         dcx.tcx.adjustments.borrow_mut().insert(id, adj);
                     }
-                    c::tag_table_closures => {
-                        let closure =
-                            val_dsr.read_closure(dcx);
-                        dcx.tcx.closures.borrow_mut().insert(ast_util::local_def(id),
-                                                             closure);
+                    c::tag_table_closure_tys => {
+                        let closure_ty =
+                            val_dsr.read_closure_ty(dcx);
+                        dcx.tcx.closure_tys.borrow_mut().insert(ast_util::local_def(id),
+                                                                closure_ty);
+                    }
+                    c::tag_table_closure_kinds => {
+                        let closure_kind =
+                            val_dsr.read_closure_kind(dcx);
+                        dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id),
+                                                                  closure_kind);
                     }
                     _ => {
                         dcx.tcx.sess.bug(
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 188a56135ec..4a0bed57433 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -260,12 +260,10 @@ impl OverloadedCallType {
     fn from_closure(tcx: &ty::ctxt, closure_did: ast::DefId)
                     -> OverloadedCallType {
         let trait_did =
-            tcx.closures
+            tcx.closure_kinds
                .borrow()
                .get(&closure_did)
-               .expect("OverloadedCallType::from_closure: didn't \
-                        find closure id")
-               .kind
+               .expect("OverloadedCallType::from_closure: didn't find closure id")
                .trait_did(tcx);
         OverloadedCallType::from_trait_id(tcx, trait_did)
     }
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 0d86dbeadf4..1330ad86bfc 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -594,8 +594,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
               let ty = try!(self.node_ty(fn_node_id));
               match ty.sty {
                   ty::ty_closure(closure_id, _, _) => {
-                      let kind = self.typer.closure_kind(closure_id);
-                      self.cat_upvar(id, span, var_id, fn_node_id, kind)
+                      match self.typer.closure_kind(closure_id) {
+                          Some(kind) => {
+                              self.cat_upvar(id, span, var_id, fn_node_id, kind)
+                          }
+                          None => {
+                              self.tcx().sess.span_bug(
+                                  span,
+                                  &*format!("No closure kind for {:?}", closure_id));
+                          }
+                      }
                   }
                   _ => {
                       self.tcx().sess.span_bug(
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index 5780b5b70f4..106c07baaa7 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -1024,12 +1024,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                kind,
                obligation.repr(self.tcx()));
 
-        let closure_kind = self.closure_typer.closure_kind(closure_def_id);
-
-        debug!("closure_kind = {:?}", closure_kind);
-
-        if closure_kind == kind {
-            candidates.vec.push(ClosureCandidate(closure_def_id, substs.clone()));
+        match self.closure_typer.closure_kind(closure_def_id) {
+            Some(closure_kind) => {
+                debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind);
+                if closure_kind == kind {
+                    candidates.vec.push(ClosureCandidate(closure_def_id, substs.clone()));
+                }
+            }
+            None => {
+                debug!("assemble_unboxed_candidates: closure_kind not yet known");
+                candidates.ambiguous = true;
+            }
         }
 
         Ok(())
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 8e88e4338cd..fb1d9fcada1 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -790,7 +790,11 @@ pub struct ctxt<'tcx> {
 
     /// Records the type of each closure. The def ID is the ID of the
     /// expression defining the closure.
-    pub closures: RefCell<DefIdMap<Closure<'tcx>>>,
+    pub closure_kinds: RefCell<DefIdMap<ClosureKind>>,
+
+    /// Records the type of each closure. The def ID is the ID of the
+    /// expression defining the closure.
+    pub closure_tys: RefCell<DefIdMap<ClosureTy<'tcx>>>,
 
     pub node_lint_levels: RefCell<FnvHashMap<(ast::NodeId, lint::LintId),
                                               lint::LevelSource>>,
@@ -2251,16 +2255,7 @@ pub struct ItemSubsts<'tcx> {
     pub substs: Substs<'tcx>,
 }
 
-/// Records information about each closure.
-#[derive(Clone)]
-pub struct Closure<'tcx> {
-    /// The type of the closure.
-    pub closure_type: ClosureTy<'tcx>,
-    /// The kind of closure this is.
-    pub kind: ClosureKind,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
 pub enum ClosureKind {
     FnClosureKind,
     FnMutClosureKind,
@@ -2288,14 +2283,22 @@ impl ClosureKind {
 pub trait ClosureTyper<'tcx> {
     fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx>;
 
-    fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind;
+    /// Is this a `Fn`, `FnMut` or `FnOnce` closure? During typeck,
+    /// returns `None` if the kind of this closure has not yet been
+    /// inferred.
+    fn closure_kind(&self,
+                    def_id: ast::DefId)
+                    -> Option<ty::ClosureKind>;
 
+    /// Returns the argument/return types of this closure.
     fn closure_type(&self,
                     def_id: ast::DefId,
                     substs: &subst::Substs<'tcx>)
                     -> ty::ClosureTy<'tcx>;
 
-    // Returns `None` if the upvar types cannot yet be definitively determined.
+    /// Returns the set of all upvars and their transformed
+    /// types. During typeck, maybe return `None` if the upvar types
+    /// have not yet been inferred.
     fn closure_upvars(&self,
                       def_id: ast::DefId,
                       substs: &Substs<'tcx>)
@@ -2391,7 +2394,8 @@ pub fn mk_ctxt<'tcx>(s: Session,
         extern_const_variants: RefCell::new(DefIdMap()),
         method_map: RefCell::new(FnvHashMap()),
         dependency_formats: RefCell::new(FnvHashMap()),
-        closures: RefCell::new(DefIdMap()),
+        closure_kinds: RefCell::new(DefIdMap()),
+        closure_tys: RefCell::new(DefIdMap()),
         node_lint_levels: RefCell::new(FnvHashMap()),
         transmute_restrictions: RefCell::new(Vec::new()),
         stability: RefCell::new(stability),
@@ -2438,7 +2442,7 @@ impl<'tcx> ctxt<'tcx> {
     }
 
     pub fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
-        self.closures.borrow()[def_id].kind
+        self.closure_kinds.borrow()[def_id]
     }
 
     pub fn closure_type(&self,
@@ -2446,7 +2450,7 @@ impl<'tcx> ctxt<'tcx> {
                         substs: &subst::Substs<'tcx>)
                         -> ty::ClosureTy<'tcx>
     {
-        self.closures.borrow()[def_id].closure_type.subst(self, substs)
+        self.closure_tys.borrow()[def_id].subst(self, substs)
     }
 }
 
@@ -5635,32 +5639,26 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>,
                             closure_expr_id: closure_id.node
                         };
 
-                        let captured_freevar_ty = match typer.upvar_capture(upvar_id) {
-                            Some(UpvarCapture::ByValue) => {
-                                freevar_ty
-                            }
-
-                            Some(UpvarCapture::ByRef(borrow)) => {
-                                mk_rptr(tcx,
-                                        tcx.mk_region(borrow.region),
-                                        ty::mt {
-                                            ty: freevar_ty,
-                                            mutbl: borrow.kind.to_mutbl_lossy(),
-                                        })
-                            }
+                        typer.upvar_capture(upvar_id).map(|capture| {
+                            let freevar_ref_ty = match capture {
+                                UpvarCapture::ByValue => {
+                                    freevar_ty
+                                }
+                                UpvarCapture::ByRef(borrow) => {
+                                    mk_rptr(tcx,
+                                            tcx.mk_region(borrow.region),
+                                            ty::mt {
+                                                ty: freevar_ty,
+                                                mutbl: borrow.kind.to_mutbl_lossy(),
+                                            })
+                                }
+                            };
 
-                            None => {
-                                // FIXME(#16640) we should really return None here;
-                                // but that requires better inference integration,
-                                // for now gin up something.
-                                freevar_ty
+                            ClosureUpvar {
+                                def: freevar.def,
+                                span: freevar.span,
+                                ty: freevar_ref_ty,
                             }
-                        };
-
-                        Some(ClosureUpvar {
-                            def: freevar.def,
-                            span: freevar.span,
-                            ty: captured_freevar_ty,
                         })
                     })
                     .collect()
@@ -6473,8 +6471,11 @@ impl<'a,'tcx> ClosureTyper<'tcx> for ty::ParameterEnvironment<'a,'tcx> {
         self
     }
 
-    fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
-        self.tcx.closure_kind(def_id)
+    fn closure_kind(&self,
+                    def_id: ast::DefId)
+                    -> Option<ty::ClosureKind>
+    {
+        Some(self.tcx.closure_kind(def_id))
     }
 
     fn closure_type(&self,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 6c723a58380..15cf37dc2f2 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -405,9 +405,9 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
         }
         ty_str => "str".to_string(),
         ty_closure(ref did, _, substs) => {
-            let closures = cx.closures.borrow();
-            closures.get(did).map(|cl| {
-                closure_to_string(cx, &cl.closure_type.subst(cx, substs))
+            let closure_tys = cx.closure_tys.borrow();
+            closure_tys.get(did).map(|closure_type| {
+                closure_to_string(cx, &closure_type.subst(cx, substs))
             }).unwrap_or_else(|| {
                 if did.krate == ast::LOCAL_CRATE {
                     let span = cx.map.span(did.node);
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 1b212aca330..9ec3db0f602 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -273,7 +273,7 @@ pub fn self_type_for_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 }
 
 pub fn kind_for_closure(ccx: &CrateContext, closure_id: ast::DefId) -> ty::ClosureKind {
-    ccx.tcx().closures.borrow()[closure_id].kind
+    ccx.tcx().closure_kinds.borrow()[closure_id]
 }
 
 pub fn decl_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index a15ede095a7..8473ce1b797 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -125,7 +125,7 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc
                                                       closure_id: ast::DefId,
                                                       substs: &Substs<'tcx>)
                                                       -> Option<Datum<'tcx, Rvalue>> {
-    if !ccx.tcx().closures.borrow().contains_key(&closure_id) {
+    if !ccx.tcx().closure_kinds.borrow().contains_key(&closure_id) {
         // Not a closure.
         return None
     }
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index a0dbc9c40a6..5782b3987cb 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -693,7 +693,10 @@ impl<'blk, 'tcx> ty::ClosureTyper<'tcx> for BlockS<'blk, 'tcx> {
         &self.fcx.param_env
     }
 
-    fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
+    fn closure_kind(&self,
+                    def_id: ast::DefId)
+                    -> Option<ty::ClosureKind>
+    {
         let typer = NormalizingClosureTyper::new(self.tcx());
         typer.closure_kind(def_id)
     }
@@ -1065,8 +1068,11 @@ impl<'a,'tcx> ty::ClosureTyper<'tcx> for NormalizingClosureTyper<'a,'tcx> {
         &self.param_env
     }
 
-    fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
-        self.param_env.tcx.closure_kind(def_id)
+    fn closure_kind(&self,
+                    def_id: ast::DefId)
+                    -> Option<ty::ClosureKind>
+    {
+        self.param_env.closure_kind(def_id)
     }
 
     fn closure_type(&self,
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index cb4c880717b..c4b7ffb8729 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -30,7 +30,7 @@ use syntax::print::pprust;
 use syntax::ptr::P;
 
 pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
-                           pat: &ast::Pat,
+                           pat: &'tcx ast::Pat,
                            expected: Ty<'tcx>)
 {
     let fcx = pcx.fcx;
@@ -157,9 +157,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
         }
         ast::PatIdent(_, ref path, _) => {
             let path = ast_util::ident_to_path(path.span, path.node);
-            check_pat_enum(pcx, pat, &path, &Some(vec![]), expected);
+            check_pat_enum(pcx, pat, &path, Some(&[]), expected);
         }
         ast::PatEnum(ref path, ref subpats) => {
+            let subpats = subpats.as_ref().map(|v| &v[]);
             check_pat_enum(pcx, pat, path, subpats, expected);
         }
         ast::PatStruct(ref path, ref fields, etc) => {
@@ -335,9 +336,9 @@ pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
 }
 
 pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                             expr: &ast::Expr,
-                             discrim: &ast::Expr,
-                             arms: &[ast::Arm],
+                             expr: &'tcx ast::Expr,
+                             discrim: &'tcx ast::Expr,
+                             arms: &'tcx [ast::Arm],
                              expected: Expectation<'tcx>,
                              match_src: ast::MatchSource) {
     let tcx = fcx.ccx.tcx;
@@ -424,8 +425,8 @@ pub struct pat_ctxt<'a, 'tcx: 'a> {
     pub map: PatIdMap,
 }
 
-pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
-                                  path: &ast::Path, fields: &[Spanned<ast::FieldPat>],
+pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
+                                  path: &ast::Path, fields: &'tcx [Spanned<ast::FieldPat>],
                                   etc: bool, expected: Ty<'tcx>) {
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
@@ -483,10 +484,12 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
                             variant_def_id, etc);
 }
 
-pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
-                                path: &ast::Path, subpats: &Option<Vec<P<ast::Pat>>>,
-                                expected: Ty<'tcx>) {
-
+pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
+                                pat: &ast::Pat,
+                                path: &ast::Path,
+                                subpats: Option<&'tcx [P<ast::Pat>]>,
+                                expected: Ty<'tcx>)
+{
     // Typecheck the path.
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
@@ -536,7 +539,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
                 "`{}` does not name a non-struct variant or a tuple struct", name);
             fcx.write_error(pat.id);
 
-            if let Some(ref subpats) = *subpats {
+            if let Some(subpats) = subpats {
                 for pat in subpats.iter() {
                     check_pat(pcx, &**pat, tcx.types.err);
                 }
@@ -545,7 +548,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
         }
     };
 
-    if let Some(ref subpats) = *subpats {
+    if let Some(subpats) = subpats {
         if subpats.len() == arg_tys.len() {
             for (subpat, arg_ty) in subpats.iter().zip(arg_tys.iter()) {
                 check_pat(pcx, &**subpat, *arg_ty);
@@ -579,7 +582,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
 /// `etc` is true if the pattern said '...' and false otherwise.
 pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                                          span: Span,
-                                         fields: &[Spanned<ast::FieldPat>],
+                                         fields: &'tcx [Spanned<ast::FieldPat>],
                                          struct_fields: &[ty::field<'tcx>],
                                          struct_id: ast::DefId,
                                          etc: bool) {
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index e37c1ab7f0c..93c6445606e 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -13,6 +13,8 @@ use super::AutorefArgs;
 use super::check_argument_types;
 use super::check_expr;
 use super::check_method_argument_types;
+use super::demand;
+use super::DeferredCallResolution;
 use super::err_args;
 use super::Expectation;
 use super::expected_types_for_fn_args;
@@ -24,13 +26,14 @@ use super::TupleArgumentsFlag;
 use super::UnresolvedTypeAction;
 use super::write_call;
 
+use CrateCtxt;
 use middle::infer;
-use middle::ty::{self, Ty};
+use middle::ty::{self, Ty, ClosureTyper};
 use syntax::ast;
 use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ptr::P;
-use CrateCtxt;
+use util::ppaux::Repr;
 
 /// Check that it is legal to call methods of the trait corresponding
 /// to `trait_id` (this only cares about the trait, not the specific
@@ -66,9 +69,9 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id:
 }
 
 pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                            call_expr: &ast::Expr,
-                            callee_expr: &ast::Expr,
-                            arg_exprs: &[P<ast::Expr>],
+                            call_expr: &'tcx ast::Expr,
+                            callee_expr: &'tcx ast::Expr,
+                            arg_exprs: &'tcx [P<ast::Expr>],
                             expected: Expectation<'tcx>)
 {
     check_expr(fcx, callee_expr);
@@ -96,24 +99,35 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs, expected);
         }
 
+        Some(CallStep::DeferredClosure(fn_sig)) => {
+            confirm_deferred_closure_call(fcx, call_expr, arg_exprs, expected, fn_sig);
+        }
+
         Some(CallStep::Overloaded(method_callee)) => {
-            confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee, expected);
+            confirm_overloaded_call(fcx, call_expr, callee_expr,
+                                    arg_exprs, expected, method_callee);
         }
     }
 }
 
 enum CallStep<'tcx> {
     Builtin,
+    DeferredClosure(ty::FnSig<'tcx>),
     Overloaded(ty::MethodCallee<'tcx>)
 }
 
 fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                      call_expr: &ast::Expr,
-                                      callee_expr: &ast::Expr,
+                                      call_expr: &'tcx ast::Expr,
+                                      callee_expr: &'tcx ast::Expr,
                                       adjusted_ty: Ty<'tcx>,
                                       autoderefref: ty::AutoDerefRef<'tcx>)
                                       -> Option<CallStep<'tcx>>
 {
+    debug!("try_overloaded_call_step(call_expr={}, adjusted_ty={}, autoderefref={})",
+           call_expr.repr(fcx.tcx()),
+           adjusted_ty.repr(fcx.tcx()),
+           autoderefref.repr(fcx.tcx()));
+
     // If the callee is a bare function or a closure, then we're all set.
     match structurally_resolved_type(fcx, callee_expr.span, adjusted_ty).sty {
         ty::ty_bare_fn(..) => {
@@ -123,9 +137,45 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             return Some(CallStep::Builtin);
         }
 
+        ty::ty_closure(def_id, _, substs) => {
+            assert_eq!(def_id.krate, ast::LOCAL_CRATE);
+
+            // Check whether this is a call to a closure where we
+            // haven't yet decided on whether the closure is fn vs
+            // fnmut vs fnonce. If so, we have to defer further processing.
+            if fcx.closure_kind(def_id).is_none() {
+                let closure_ty =
+                    fcx.closure_type(def_id, substs);
+                let fn_sig =
+                    fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
+                                                                          infer::FnCall,
+                                                                          &closure_ty.sig).0;
+                fcx.record_deferred_call_resolution(
+                    def_id,
+                    box CallResolution {call_expr: call_expr,
+                                        callee_expr: callee_expr,
+                                        adjusted_ty: adjusted_ty,
+                                        autoderefref: autoderefref,
+                                        fn_sig: fn_sig.clone(),
+                                        closure_def_id: def_id});
+                return Some(CallStep::DeferredClosure(fn_sig));
+            }
+        }
+
         _ => {}
     }
 
+    try_overloaded_call_traits(fcx, call_expr, callee_expr, adjusted_ty, autoderefref)
+        .map(|method_callee| CallStep::Overloaded(method_callee))
+}
+
+fn try_overloaded_call_traits<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                       call_expr: &ast::Expr,
+                                       callee_expr: &ast::Expr,
+                                       adjusted_ty: Ty<'tcx>,
+                                       autoderefref: ty::AutoDerefRef<'tcx>)
+                                       -> Option<ty::MethodCallee<'tcx>>
+{
     // Try the options that are least restrictive on the caller first.
     for &(opt_trait_def_id, method_name) in [
         (fcx.tcx().lang_items.fn_trait(), token::intern("call")),
@@ -147,7 +197,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                None) {
             None => continue,
             Some(method_callee) => {
-                return Some(CallStep::Overloaded(method_callee));
+                return Some(method_callee);
             }
         }
     }
@@ -158,7 +208,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                  call_expr: &ast::Expr,
                                  callee_ty: Ty<'tcx>,
-                                 arg_exprs: &[P<ast::Expr>],
+                                 arg_exprs: &'tcx [P<ast::Expr>],
                                  expected: Expectation<'tcx>)
 {
     let error_fn_sig;
@@ -215,22 +265,132 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
     write_call(fcx, call_expr, fn_sig.output);
 }
 
+fn confirm_deferred_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                          call_expr: &ast::Expr,
+                                          arg_exprs: &'tcx [P<ast::Expr>],
+                                          expected: Expectation<'tcx>,
+                                          fn_sig: ty::FnSig<'tcx>)
+{
+    // `fn_sig` is the *signature* of the cosure being called. We
+    // don't know the full details yet (`Fn` vs `FnMut` etc), but we
+    // do know the types expected for each argument and the return
+    // type.
+
+    let expected_arg_tys =
+        expected_types_for_fn_args(fcx,
+                                   call_expr.span,
+                                   expected,
+                                   fn_sig.output.clone(),
+                                   &*fn_sig.inputs);
+
+    check_argument_types(fcx,
+                         call_expr.span,
+                         &*fn_sig.inputs,
+                         &*expected_arg_tys,
+                         arg_exprs,
+                         AutorefArgs::No,
+                         fn_sig.variadic,
+                         TupleArgumentsFlag::TupleArguments);
+
+    write_call(fcx, call_expr, fn_sig.output);
+}
+
 fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                     call_expr: &ast::Expr,
-                                    arg_exprs: &[P<ast::Expr>],
-                                    method_callee: ty::MethodCallee<'tcx>,
-                                    expected: Expectation<'tcx>)
+                                    callee_expr: &'tcx ast::Expr,
+                                    arg_exprs: &'tcx [P<ast::Expr>],
+                                    expected: Expectation<'tcx>,
+                                    method_callee: ty::MethodCallee<'tcx>)
 {
-    let output_type = check_method_argument_types(fcx,
-                                                  call_expr.span,
-                                                  method_callee.ty,
-                                                  call_expr,
-                                                  arg_exprs,
-                                                  AutorefArgs::No,
-                                                  TupleArgumentsFlag::TupleArguments,
-                                                  expected);
+    let output_type =
+        check_method_argument_types(fcx,
+                                    call_expr.span,
+                                    method_callee.ty,
+                                    callee_expr,
+                                    arg_exprs,
+                                    AutorefArgs::No,
+                                    TupleArgumentsFlag::TupleArguments,
+                                    expected);
+    write_call(fcx, call_expr, output_type);
+
+    write_overloaded_call_method_map(fcx, call_expr, method_callee);
+}
+
+fn write_overloaded_call_method_map<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                             call_expr: &ast::Expr,
+                                             method_callee: ty::MethodCallee<'tcx>) {
     let method_call = ty::MethodCall::expr(call_expr.id);
     fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
-    write_call(fcx, call_expr, output_type);
 }
 
+struct CallResolution<'tcx> {
+    call_expr: &'tcx ast::Expr,
+    callee_expr: &'tcx ast::Expr,
+    adjusted_ty: Ty<'tcx>,
+    autoderefref: ty::AutoDerefRef<'tcx>,
+    fn_sig: ty::FnSig<'tcx>,
+    closure_def_id: ast::DefId,
+}
+
+impl<'tcx> Repr<'tcx> for CallResolution<'tcx> {
+    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
+        format!("CallResolution(call_expr={}, callee_expr={}, adjusted_ty={}, \
+                autoderefref={}, fn_sig={}, closure_def_id={})",
+                self.call_expr.repr(tcx),
+                self.callee_expr.repr(tcx),
+                self.adjusted_ty.repr(tcx),
+                self.autoderefref.repr(tcx),
+                self.fn_sig.repr(tcx),
+                self.closure_def_id.repr(tcx))
+    }
+}
+
+impl<'tcx> DeferredCallResolution<'tcx> for CallResolution<'tcx> {
+    fn resolve<'a>(&mut self, fcx: &FnCtxt<'a,'tcx>) {
+        debug!("DeferredCallResolution::resolve() {}",
+               self.repr(fcx.tcx()));
+
+        // we should not be invoked until the closure kind has been
+        // determined by upvar inference
+        assert!(fcx.closure_kind(self.closure_def_id).is_some());
+
+        // We may now know enough to figure out fn vs fnmut etc.
+        match try_overloaded_call_traits(fcx, self.call_expr, self.callee_expr,
+                                         self.adjusted_ty, self.autoderefref.clone()) {
+            Some(method_callee) => {
+                // One problem is that when we get here, we are going
+                // to have a newly instantiated function signature
+                // from the call trait. This has to be reconciled with
+                // the older function signature we had before. In
+                // principle we *should* be able to fn_sigs(), but we
+                // can't because of the annoying need for a TypeTrace.
+                // (This always bites me, should find a way to
+                // refactor it.)
+                let method_sig =
+                    ty::assert_no_late_bound_regions(fcx.tcx(),
+                                                     ty::ty_fn_sig(method_callee.ty));
+
+                debug!("attempt_resolution: method_callee={}",
+                       method_callee.repr(fcx.tcx()));
+
+                for (&method_arg_ty, &self_arg_ty) in
+                    method_sig.inputs[1..].iter().zip(self.fn_sig.inputs.iter())
+                {
+                    demand::eqtype(fcx, self.call_expr.span, self_arg_ty, method_arg_ty);
+                }
+
+                demand::eqtype(fcx,
+                               self.call_expr.span,
+                               method_sig.output.unwrap(),
+                               self.fn_sig.output.unwrap());
+
+                write_overloaded_call_method_map(fcx, self.call_expr, method_callee);
+            }
+            None => {
+                fcx.tcx().sess.span_bug(
+                    self.call_expr.span,
+                    "failed to find an overloaded call trait for closure call");
+            }
+        }
+    }
+}
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index bdae34e7878..808dbd4b319 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -26,8 +26,8 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                    expr: &ast::Expr,
                                    _capture: ast::CaptureClause,
                                    opt_kind: Option<ast::ClosureKind>,
-                                   decl: &ast::FnDecl,
-                                   body: &ast::Block,
+                                   decl: &'tcx ast::FnDecl,
+                                   body: &'tcx ast::Block,
                                    expected: Expectation<'tcx>) {
     debug!("check_expr_closure(expr={},expected={})",
            expr.repr(fcx.tcx()),
@@ -42,20 +42,14 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
             // 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 error, requesting an annotation.
+            // that. Otherwise, we leave it unspecified, to be filled
+            // in by upvar inference.
             match expected_sig_and_kind {
                 None => { // don't have information about the kind, request explicit annotation
-                    // NB We still need to typeck the body, so assume `FnMut` kind just for that
-                    let kind = ty::FnMutClosureKind;
-
-                    check_closure(fcx, expr, kind, decl, body, None);
-
-                    span_err!(fcx.ccx.tcx.sess, expr.span, E0187,
-                        "can't infer the \"kind\" of the closure; explicitly annotate it; e.g. \
-                        `|&:| {{}}`");
+                    check_closure(fcx, expr, None, decl, body, None);
                 },
                 Some((sig, kind)) => {
-                    check_closure(fcx, expr, kind, decl, body, Some(sig));
+                    check_closure(fcx, expr, Some(kind), decl, body, Some(sig));
                 }
             }
         }
@@ -68,21 +62,21 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
             };
 
             let expected_sig = expected_sig_and_kind.map(|t| t.0);
-            check_closure(fcx, expr, kind, decl, body, expected_sig);
+            check_closure(fcx, expr, Some(kind), decl, body, expected_sig);
         }
     }
 }
 
 fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                           expr: &ast::Expr,
-                          kind: ty::ClosureKind,
-                          decl: &ast::FnDecl,
-                          body: &ast::Block,
+                          opt_kind: Option<ty::ClosureKind>,
+                          decl: &'tcx ast::FnDecl,
+                          body: &'tcx ast::Block,
                           expected_sig: Option<ty::FnSig<'tcx>>) {
     let expr_def_id = ast_util::local_def(expr.id);
 
-    debug!("check_closure kind={:?} expected_sig={}",
-           kind,
+    debug!("check_closure opt_kind={:?} expected_sig={}",
+           opt_kind,
            expected_sig.repr(fcx.tcx()));
 
     let mut fn_ty = astconv::ty_of_closure(
@@ -124,17 +118,16 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
     // the `closures` table.
     fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)];
 
-    debug!("closure for {} --> sig={} kind={:?}",
+    debug!("closure for {} --> sig={} opt_kind={:?}",
            expr_def_id.repr(fcx.tcx()),
            fn_ty.sig.repr(fcx.tcx()),
-           kind);
-
-    let closure = ty::Closure {
-        closure_type: fn_ty,
-        kind: kind,
-    };
+           opt_kind);
 
-    fcx.inh.closures.borrow_mut().insert(expr_def_id, closure);
+    fcx.inh.closure_tys.borrow_mut().insert(expr_def_id, fn_ty);
+    match opt_kind {
+        Some(kind) => { fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind); }
+        None => { }
+    }
 }
 
 fn deduce_expectations_from_expected_type<'a,'tcx>(
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 56a32186c9e..c326116cbd5 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -31,8 +31,8 @@ use util::ppaux::Repr;
 struct ConfirmContext<'a, 'tcx:'a> {
     fcx: &'a FnCtxt<'a, 'tcx>,
     span: Span,
-    self_expr: &'a ast::Expr,
-    call_expr: &'a ast::Expr,
+    self_expr: &'tcx ast::Expr,
+    call_expr: &'tcx ast::Expr,
 }
 
 struct InstantiatedMethodSig<'tcx> {
@@ -51,8 +51,8 @@ struct InstantiatedMethodSig<'tcx> {
 
 pub fn confirm<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                          span: Span,
-                         self_expr: &ast::Expr,
-                         call_expr: &ast::Expr,
+                         self_expr: &'tcx ast::Expr,
+                         call_expr: &'tcx ast::Expr,
                          unadjusted_self_ty: Ty<'tcx>,
                          pick: probe::Pick<'tcx>,
                          supplied_method_types: Vec<Ty<'tcx>>)
@@ -70,8 +70,8 @@ pub fn confirm<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 impl<'a,'tcx> ConfirmContext<'a,'tcx> {
     fn new(fcx: &'a FnCtxt<'a, 'tcx>,
            span: Span,
-           self_expr: &'a ast::Expr,
-           call_expr: &'a ast::Expr)
+           self_expr: &'tcx ast::Expr,
+           call_expr: &'tcx ast::Expr)
            -> ConfirmContext<'a, 'tcx>
     {
         ConfirmContext { fcx: fcx, span: span, self_expr: self_expr, call_expr: call_expr }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index d92cc1dfc1e..88455b3385a 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -44,6 +44,9 @@ pub enum MethodError {
 
     // Multiple methods might apply.
     Ambiguity(Vec<CandidateSource>),
+
+    // Using a `Fn`/`FnMut`/etc method on a raw closure type before we have inferred its kind.
+    ClosureAmbiguity(/* DefId of fn trait */ ast::DefId),
 }
 
 // A pared down enum describing just the places from which a method
@@ -65,9 +68,10 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                         -> bool
 {
     match probe::probe(fcx, span, method_name, self_ty, call_expr_id) {
-        Ok(_) => true,
-        Err(NoMatch(_, _)) => false,
-        Err(Ambiguity(_)) => true,
+        Ok(..) => true,
+        Err(NoMatch(..)) => false,
+        Err(Ambiguity(..)) => true,
+        Err(ClosureAmbiguity(..)) => true,
     }
 }
 
@@ -90,8 +94,8 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                         method_name: ast::Name,
                         self_ty: Ty<'tcx>,
                         supplied_method_types: Vec<Ty<'tcx>>,
-                        call_expr: &ast::Expr,
-                        self_expr: &ast::Expr)
+                        call_expr: &'tcx ast::Expr,
+                        self_expr: &'tcx ast::Expr)
                         -> Result<MethodCallee<'tcx>, MethodError>
 {
     debug!("lookup(method_name={}, self_ty={}, call_expr={}, self_expr={})",
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 2e366f44507..e9ea0921bc9 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use super::{MethodError,Ambiguity,NoMatch};
+use super::{MethodError};
 use super::MethodIndex;
 use super::{CandidateSource,ImplSource,TraitSource};
 use super::suggest;
@@ -129,7 +129,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // take place in the `fcx.infcx().probe` below.
     let steps = match create_steps(fcx, span, self_ty) {
         Some(steps) => steps,
-        None => return Err(NoMatch(Vec::new(), Vec::new())),
+        None => return Err(MethodError::NoMatch(Vec::new(), Vec::new())),
     };
 
     // Create a list of simplified self types, if we can.
@@ -158,7 +158,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         let (steps, opt_simplified_steps) = dummy.take().unwrap();
         let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps, opt_simplified_steps);
         probe_cx.assemble_inherent_candidates();
-        probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id);
+        try!(probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id));
         probe_cx.pick()
     })
 }
@@ -444,29 +444,34 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
 
     fn assemble_extension_candidates_for_traits_in_scope(&mut self,
                                                          expr_id: ast::NodeId)
+                                                         -> Result<(),MethodError>
     {
         let mut duplicates = HashSet::new();
         let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id);
         for applicable_traits in opt_applicable_traits.into_iter() {
             for &trait_did in applicable_traits.iter() {
                 if duplicates.insert(trait_did) {
-                    self.assemble_extension_candidates_for_trait(trait_did);
+                    try!(self.assemble_extension_candidates_for_trait(trait_did));
                 }
             }
         }
+        Ok(())
     }
 
-    fn assemble_extension_candidates_for_all_traits(&mut self) {
+    fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(),MethodError> {
         let mut duplicates = HashSet::new();
         for trait_info in suggest::all_traits(self.fcx.ccx) {
             if duplicates.insert(trait_info.def_id) {
-                self.assemble_extension_candidates_for_trait(trait_info.def_id)
+                try!(self.assemble_extension_candidates_for_trait(trait_info.def_id));
             }
         }
+        Ok(())
     }
 
     fn assemble_extension_candidates_for_trait(&mut self,
-                                               trait_def_id: ast::DefId) {
+                                               trait_def_id: ast::DefId)
+                                               -> Result<(),MethodError>
+    {
         debug!("assemble_extension_candidates_for_trait(trait_def_id={})",
                trait_def_id.repr(self.tcx()));
 
@@ -478,26 +483,27 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                        .position(|item| item.name() == self.method_name);
         let matching_index = match matching_index {
             Some(i) => i,
-            None => { return; }
+            None => { return Ok(()); }
         };
         let method = match (&*trait_items)[matching_index].as_opt_method() {
             Some(m) => m,
-            None => { return; }
+            None => { return Ok(()); }
         };
 
         // Check whether `trait_def_id` defines a method with suitable name:
         if !self.has_applicable_self(&*method) {
             debug!("method has inapplicable self");
-            return self.record_static_candidate(TraitSource(trait_def_id));
+            self.record_static_candidate(TraitSource(trait_def_id));
+            return Ok(());
         }
 
         self.assemble_extension_candidates_for_trait_impls(trait_def_id,
                                                            method.clone(),
                                                            matching_index);
 
-        self.assemble_closure_candidates(trait_def_id,
-                                         method.clone(),
-                                         matching_index);
+        try!(self.assemble_closure_candidates(trait_def_id,
+                                              method.clone(),
+                                              matching_index));
 
         self.assemble_projection_candidates(trait_def_id,
                                             method.clone(),
@@ -506,6 +512,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         self.assemble_where_clause_candidates(trait_def_id,
                                               method,
                                               matching_index);
+
+        Ok(())
     }
 
     fn assemble_extension_candidates_for_trait_impls(&mut self,
@@ -576,6 +584,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                                    trait_def_id: ast::DefId,
                                    method_ty: Rc<ty::Method<'tcx>>,
                                    method_index: uint)
+                                   -> Result<(),MethodError>
     {
         // Check if this is one of the Fn,FnMut,FnOnce traits.
         let tcx = self.tcx();
@@ -586,7 +595,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() {
             ty::FnOnceClosureKind
         } else {
-            return;
+            return Ok(());
         };
 
         // Check if there is an unboxed-closure self-type in the list of receivers.
@@ -598,19 +607,16 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                 _ => continue,
             };
 
-            let closures = self.fcx.inh.closures.borrow();
-            let closure_data = match closures.get(&closure_def_id) {
-                Some(data) => data,
+            let closure_kinds = self.fcx.inh.closure_kinds.borrow();
+            let closure_kind = match closure_kinds.get(&closure_def_id) {
+                Some(&k) => k,
                 None => {
-                    self.tcx().sess.span_bug(
-                        self.span,
-                        &format!("No entry for closure: {}",
-                                closure_def_id.repr(self.tcx()))[]);
+                    return Err(MethodError::ClosureAmbiguity(trait_def_id));
                 }
             };
 
             // this closure doesn't implement the right kind of `Fn` trait
-            if closure_data.kind != kind {
+            if closure_kind != kind {
                 continue;
             }
 
@@ -630,6 +636,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                 kind: ClosureCandidate(trait_def_id, method_index)
             });
         }
+
+        Ok(())
     }
 
     fn assemble_projection_candidates(&mut self,
@@ -735,11 +743,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         let span = self.span;
         let tcx = self.tcx();
 
-        self.assemble_extension_candidates_for_all_traits();
+        try!(self.assemble_extension_candidates_for_all_traits());
 
         let out_of_scope_traits = match self.pick_core() {
             Some(Ok(p)) => vec![p.method_ty.container.id()],
-            Some(Err(Ambiguity(v))) => v.into_iter().map(|source| {
+            Some(Err(MethodError::Ambiguity(v))) => v.into_iter().map(|source| {
                 match source {
                     TraitSource(id) => id,
                     ImplSource(impl_id) => {
@@ -752,14 +760,18 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                     }
                 }
             }).collect(),
-            Some(Err(NoMatch(_, others))) => {
+            Some(Err(MethodError::NoMatch(_, others))) => {
                 assert!(others.is_empty());
                 vec![]
             }
+            Some(Err(MethodError::ClosureAmbiguity(..))) => {
+                // this error only occurs when assembling candidates
+                tcx.sess.span_bug(span, "encountered ClosureAmbiguity from pick_core");
+            }
             None => vec![],
         };
-;
-        Err(NoMatch(static_candidates, out_of_scope_traits))
+
+        Err(MethodError::NoMatch(static_candidates, out_of_scope_traits))
     }
 
     fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
@@ -895,7 +907,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
 
         if applicable_candidates.len() > 1 {
             let sources = probes.iter().map(|p| p.to_source()).collect();
-            return Some(Err(Ambiguity(sources)));
+            return Some(Err(MethodError::Ambiguity(sources)));
         }
 
         applicable_candidates.pop().map(|probe| {
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 3cf9a1a9456..bd5060c940e 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -22,6 +22,7 @@ use util::ppaux::UserString;
 
 use syntax::{ast, ast_util};
 use syntax::codemap::Span;
+use syntax::print::pprust;
 
 use std::cell;
 use std::cmp::Ordering;
@@ -32,6 +33,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                               span: Span,
                               rcvr_ty: Ty<'tcx>,
                               method_name: ast::Name,
+                              callee_expr: &ast::Expr,
                               error: MethodError)
 {
     match error {
@@ -84,6 +86,18 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
             report_candidates(fcx, span, method_name, sources);
         }
+
+        MethodError::ClosureAmbiguity(trait_def_id) => {
+            fcx.sess().span_err(
+                span,
+                &*format!("the `{}` method from the `{}` trait cannot be explicitly \
+                           invoked on this closure as we have not yet inferred what \
+                           kind of closure it is; use overloaded call notation instead \
+                           (e.g., `{}()`)",
+                          method_name.user_string(fcx.tcx()),
+                          ty::item_path_str(fcx.tcx(), trait_def_id),
+                          pprust::expr_to_string(callee_expr)));
+        }
     }
 
     fn report_candidates(fcx: &FnCtxt,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 9dcde1c2a0a..c193e1ef483 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -110,6 +110,7 @@ use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
 use util::lev_distance::lev_distance;
 
 use std::cell::{Cell, Ref, RefCell};
+use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::mem::replace;
 use std::rc::Rc;
 use std::iter::repeat;
@@ -160,7 +161,8 @@ pub struct Inherited<'a, 'tcx: 'a> {
     adjustments: RefCell<NodeMap<ty::AutoAdjustment<'tcx>>>,
     method_map: MethodMap<'tcx>,
     upvar_capture_map: RefCell<ty::UpvarCaptureMap>,
-    closures: RefCell<DefIdMap<ty::Closure<'tcx>>>,
+    closure_tys: RefCell<DefIdMap<ty::ClosureTy<'tcx>>>,
+    closure_kinds: RefCell<DefIdMap<ty::ClosureKind>>,
     object_cast_map: ObjectCastMap<'tcx>,
 
     // A mapping from each fn's id to its signature, with all bound
@@ -170,8 +172,23 @@ pub struct Inherited<'a, 'tcx: 'a> {
 
     // Tracks trait obligations incurred during this function body.
     fulfillment_cx: RefCell<traits::FulfillmentContext<'tcx>>,
+
+    // When we process a call like `c()` where `c` is a closure type,
+    // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
+    // `FnOnce` closure. In that case, we defer full resolution of the
+    // call until upvar inference can kick in and make the
+    // decision. We keep these deferred resolutions grouped by the
+    // def-id of the closure, so that once we decide, we can easily go
+    // back and process them.
+    deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolutionHandler<'tcx>>>>,
+}
+
+trait DeferredCallResolution<'tcx> {
+    fn resolve<'a>(&mut self, fcx: &FnCtxt<'a,'tcx>);
 }
 
+type DeferredCallResolutionHandler<'tcx> = Box<DeferredCallResolution<'tcx>+'tcx>;
+
 /// When type-checking an expression, we propagate downward
 /// whatever type hint we are able in the form of an `Expectation`.
 #[derive(Copy)]
@@ -339,8 +356,11 @@ impl<'a, 'tcx> ty::ClosureTyper<'tcx> for FnCtxt<'a, 'tcx> {
         &self.inh.param_env
     }
 
-    fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind {
-        self.inh.closures.borrow()[def_id].kind
+    fn closure_kind(&self,
+                    def_id: ast::DefId)
+                    -> Option<ty::ClosureKind>
+    {
+        self.inh.closure_kinds.borrow().get(&def_id).cloned()
     }
 
     fn closure_type(&self,
@@ -348,7 +368,7 @@ impl<'a, 'tcx> ty::ClosureTyper<'tcx> for FnCtxt<'a, 'tcx> {
                     substs: &subst::Substs<'tcx>)
                     -> ty::ClosureTy<'tcx>
     {
-        self.inh.closures.borrow()[def_id].closure_type.subst(self.tcx(), substs)
+        self.inh.closure_tys.borrow()[def_id].subst(self.tcx(), substs)
     }
 
     fn closure_upvars(&self,
@@ -374,9 +394,11 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
             method_map: RefCell::new(FnvHashMap()),
             object_cast_map: RefCell::new(NodeMap()),
             upvar_capture_map: RefCell::new(FnvHashMap()),
-            closures: RefCell::new(DefIdMap()),
+            closure_tys: RefCell::new(DefIdMap()),
+            closure_kinds: RefCell::new(DefIdMap()),
             fn_sig_map: RefCell::new(NodeMap()),
             fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
+            deferred_call_resolutions: RefCell::new(DefIdMap()),
         }
     }
 
@@ -425,13 +447,13 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>)
 
 struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }
 
-impl<'a, 'tcx, 'v> Visitor<'v> for CheckItemTypesVisitor<'a, 'tcx> {
-    fn visit_item(&mut self, i: &ast::Item) {
+impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
+    fn visit_item(&mut self, i: &'tcx ast::Item) {
         check_item(self.ccx, i);
         visit::walk_item(self, i);
     }
 
-    fn visit_ty(&mut self, t: &ast::Ty) {
+    fn visit_ty(&mut self, t: &'tcx ast::Ty) {
         match t.node {
             ast::TyFixedLengthVec(_, ref expr) => {
                 check_const_in_type(self.ccx, &**expr, self.ccx.tcx.types.uint);
@@ -459,8 +481,8 @@ pub fn check_item_types(ccx: &CrateCtxt) {
 }
 
 fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                           decl: &ast::FnDecl,
-                           body: &ast::Block,
+                           decl: &'tcx ast::FnDecl,
+                           body: &'tcx ast::Block,
                            id: ast::NodeId,
                            raw_fty: Ty<'tcx>,
                            param_env: ty::ParameterEnvironment<'a, 'tcx>)
@@ -480,8 +502,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig,
                                decl, id, body, &inh);
 
-            vtable::select_all_fcx_obligations_or_error(&fcx);
+            vtable::select_all_fcx_obligations_and_apply_defaults(&fcx);
             upvar::closure_analyze_fn(&fcx, id, decl, body);
+            vtable::select_all_fcx_obligations_or_error(&fcx);
             regionck::regionck_fn(&fcx, id, decl, body);
             writeback::resolve_type_vars_in_fn(&fcx, decl, body);
         }
@@ -512,9 +535,9 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
+impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
     // Add explicitly-declared locals.
-    fn visit_local(&mut self, local: &ast::Local) {
+    fn visit_local(&mut self, local: &'tcx ast::Local) {
         let o_ty = match local.ty {
             Some(ref ty) => Some(self.fcx.to_ty(&**ty)),
             None => None
@@ -528,7 +551,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
     }
 
     // Add pattern bindings.
-    fn visit_pat(&mut self, p: &ast::Pat) {
+    fn visit_pat(&mut self, p: &'tcx ast::Pat) {
         if let ast::PatIdent(_, ref path1, _) = p.node {
             if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map, p) {
                 let var_ty = self.assign(p.span, p.id, None);
@@ -546,7 +569,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
         visit::walk_pat(self, p);
     }
 
-    fn visit_block(&mut self, b: &ast::Block) {
+    fn visit_block(&mut self, b: &'tcx ast::Block) {
         // non-obvious: the `blk` variable maps to region lb, so
         // we have to keep this up-to-date.  This
         // is... unfortunate.  It'd be nice to not need this.
@@ -555,7 +578,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
 
     // Since an expr occurs as part of the type fixed size arrays we
     // need to record the type for that node
-    fn visit_ty(&mut self, t: &ast::Ty) {
+    fn visit_ty(&mut self, t: &'tcx ast::Ty) {
         match t.node {
             ast::TyFixedLengthVec(ref ty, ref count_expr) => {
                 self.visit_ty(&**ty);
@@ -566,8 +589,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
     }
 
     // Don't descend into fns and items
-    fn visit_fn(&mut self, _: visit::FnKind<'v>, _: &'v ast::FnDecl,
-                _: &'v ast::Block, _: Span, _: ast::NodeId) { }
+    fn visit_fn(&mut self, _: visit::FnKind<'tcx>, _: &'tcx ast::FnDecl,
+                _: &'tcx ast::Block, _: Span, _: ast::NodeId) { }
     fn visit_item(&mut self, _: &ast::Item) { }
 
 }
@@ -582,9 +605,9 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
                       unsafety: ast::Unsafety,
                       unsafety_id: ast::NodeId,
                       fn_sig: &ty::FnSig<'tcx>,
-                      decl: &ast::FnDecl,
+                      decl: &'tcx ast::FnDecl,
                       fn_id: ast::NodeId,
-                      body: &ast::Block,
+                      body: &'tcx ast::Block,
                       inherited: &'a Inherited<'a, 'tcx>)
                       -> FnCtxt<'a, 'tcx>
 {
@@ -677,7 +700,7 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
     }
 }
 
-pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
+pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
     debug!("check_item(it.id={}, it.ident={})",
            it.id,
            ty::item_path_str(ccx.tcx, local_def(it.id)));
@@ -829,7 +852,7 @@ fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 /// * `method`: the method definition
 fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                item_generics: &ty::Generics<'tcx>,
-                               method: &ast::Method) {
+                               method: &'tcx ast::Method) {
     debug!("check_method_body(item_generics={}, method.id={})",
             item_generics.repr(ccx.tcx),
             method.id);
@@ -1133,10 +1156,10 @@ fn check_cast_inner<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     }
 }
 
-fn check_cast(fcx: &FnCtxt,
-              cast_expr: &ast::Expr,
-              e: &ast::Expr,
-              t: &ast::Ty) {
+fn check_cast<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                       cast_expr: &ast::Expr,
+                       e: &'tcx ast::Expr,
+                       t: &ast::Ty) {
     let id = cast_expr.id;
     let span = cast_expr.span;
 
@@ -1279,6 +1302,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if ty::type_has_ty_infer(t) || ty::type_is_error(t) { Err(()) } else { Ok(t) }
     }
 
+    fn record_deferred_call_resolution(&self,
+                                       closure_def_id: ast::DefId,
+                                       r: DeferredCallResolutionHandler<'tcx>) {
+        let mut deferred_call_resolutions = self.inh.deferred_call_resolutions.borrow_mut();
+        let mut vec = match deferred_call_resolutions.entry(closure_def_id) {
+            Occupied(entry) => entry.into_mut(),
+            Vacant(entry) => entry.insert(Vec::new()),
+        };
+        vec.push(r);
+    }
+
+    fn remove_deferred_call_resolutions(&self,
+                                        closure_def_id: ast::DefId)
+                                        -> Vec<DeferredCallResolutionHandler<'tcx>>
+    {
+        let mut deferred_call_resolutions = self.inh.deferred_call_resolutions.borrow_mut();
+        deferred_call_resolutions.remove(&closure_def_id).unwrap_or(Vec::new())
+    }
+
     pub fn tag(&self) -> String {
         format!("{:?}", self as *const FnCtxt)
     }
@@ -2068,7 +2110,7 @@ fn autoderef_for_index<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
 fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                             method_call: MethodCall,
                             expr: &ast::Expr,
-                            base_expr: &ast::Expr,
+                            base_expr: &'tcx ast::Expr,
                             adjusted_ty: Ty<'tcx>,
                             adjustment: ty::AutoDerefRef<'tcx>,
                             lvalue_pref: LvaluePreference,
@@ -2138,8 +2180,8 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                          sp: Span,
                                          method_fn_ty: Ty<'tcx>,
-                                         callee_expr: &ast::Expr,
-                                         args_no_rcvr: &[P<ast::Expr>],
+                                         callee_expr: &'tcx ast::Expr,
+                                         args_no_rcvr: &'tcx [P<ast::Expr>],
                                          autoref_args: AutorefArgs,
                                          tuple_arguments: TupleArgumentsFlag,
                                          expected: Expectation<'tcx>)
@@ -2194,7 +2236,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   sp: Span,
                                   fn_inputs: &[Ty<'tcx>],
                                   expected_arg_tys: &[Ty<'tcx>],
-                                  args: &[P<ast::Expr>],
+                                  args: &'tcx [P<ast::Expr>],
                                   autoref_args: AutorefArgs,
                                   variadic: bool,
                                   tuple_arguments: TupleArgumentsFlag) {
@@ -2462,7 +2504,7 @@ pub fn valid_range_bounds(ccx: &CrateCtxt,
 }
 
 pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                     expr: &ast::Expr,
+                                     expr: &'tcx ast::Expr,
                                      expected: Ty<'tcx>) {
     check_expr_with_unifier(
         fcx, expr, ExpectHasType(expected), NoPreference,
@@ -2470,14 +2512,14 @@ pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 }
 
 fn check_expr_coercable_to_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                          expr: &ast::Expr,
+                                          expr: &'tcx ast::Expr,
                                           expected: Ty<'tcx>) {
     check_expr_with_unifier(
         fcx, expr, ExpectHasType(expected), NoPreference,
         || demand::coerce(fcx, expr.span, expected, expr));
 }
 
-fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr,
+fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx ast::Expr,
                                   expected: Ty<'tcx>) {
     check_expr_with_unifier(
         fcx, expr, ExpectHasType(expected), NoPreference,
@@ -2485,7 +2527,7 @@ fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr,
 }
 
 fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                         expr: &ast::Expr,
+                                         expr: &'tcx ast::Expr,
                                          expected: Expectation<'tcx>) {
     check_expr_with_unifier(
         fcx, expr, expected, NoPreference,
@@ -2493,19 +2535,19 @@ fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 }
 
 fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                         expr: &ast::Expr,
+                                                         expr: &'tcx ast::Expr,
                                                          expected: Expectation<'tcx>,
                                                          lvalue_pref: LvaluePreference)
 {
     check_expr_with_unifier(fcx, expr, expected, lvalue_pref, || ())
 }
 
-fn check_expr(fcx: &FnCtxt, expr: &ast::Expr)  {
+fn check_expr<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx ast::Expr)  {
     check_expr_with_unifier(fcx, expr, NoExpectation, NoPreference, || ())
 }
 
-fn check_expr_with_lvalue_pref(fcx: &FnCtxt, expr: &ast::Expr,
-                               lvalue_pref: LvaluePreference)  {
+fn check_expr_with_lvalue_pref<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx ast::Expr,
+                                        lvalue_pref: LvaluePreference)  {
     check_expr_with_unifier(fcx, expr, NoExpectation, lvalue_pref, || ())
 }
 
@@ -2613,7 +2655,7 @@ fn expected_types_for_fn_args<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 /// that there are actually multiple representations for `ty_err`, so avoid
 /// that when err needs to be handled differently.
 fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
-                                        expr: &ast::Expr,
+                                        expr: &'tcx ast::Expr,
                                         expected: Expectation<'tcx>,
                                         lvalue_pref: LvaluePreference,
                                         unifier: F) where
@@ -2624,9 +2666,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
 
     // Checks a method call.
     fn check_method_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                   expr: &ast::Expr,
+                                   expr: &'tcx ast::Expr,
                                    method_name: ast::SpannedIdent,
-                                   args: &[P<ast::Expr>],
+                                   args: &'tcx [P<ast::Expr>],
                                    tps: &[P<ast::Ty>],
                                    expected: Expectation<'tcx>,
                                    lvalue_pref: LvaluePreference) {
@@ -2653,7 +2695,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                 method_ty
             }
             Err(error) => {
-                method::report_error(fcx, method_name.span, expr_t, method_name.node.name, error);
+                method::report_error(fcx, method_name.span, expr_t,
+                                     method_name.node.name, rcvr, error);
                 fcx.write_error(expr.id);
                 fcx.tcx().types.err
             }
@@ -2675,9 +2718,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     // A generic function for checking the then and else in an if
     // or if-else.
     fn check_then_else<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                 cond_expr: &ast::Expr,
-                                 then_blk: &ast::Block,
-                                 opt_else_expr: Option<&ast::Expr>,
+                                 cond_expr: &'tcx ast::Expr,
+                                 then_blk: &'tcx ast::Block,
+                                 opt_else_expr: Option<&'tcx ast::Expr>,
                                  id: ast::NodeId,
                                  sp: Span,
                                  expected: Expectation<'tcx>) {
@@ -2717,12 +2760,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     fn lookup_op_method<'a, 'tcx, F>(fcx: &'a FnCtxt<'a, 'tcx>,
-                                     op_ex: &ast::Expr,
+                                     op_ex: &'tcx ast::Expr,
                                      lhs_ty: Ty<'tcx>,
                                      opname: ast::Name,
                                      trait_did: Option<ast::DefId>,
                                      lhs: &'a ast::Expr,
-                                     rhs: Option<&P<ast::Expr>>,
+                                     rhs: Option<&'tcx P<ast::Expr>>,
                                      unbound_method: F,
                                      autoref_args: AutorefArgs) -> Ty<'tcx> where
         F: FnOnce(),
@@ -2803,12 +2846,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     // could be either an expr_binop or an expr_assign_binop
-    fn check_binop(fcx: &FnCtxt,
-                   expr: &ast::Expr,
-                   op: ast::BinOp,
-                   lhs: &ast::Expr,
-                   rhs: &P<ast::Expr>,
-                   is_binop_assignment: IsBinopAssignment) {
+    fn check_binop<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                            expr: &'tcx ast::Expr,
+                            op: ast::BinOp,
+                            lhs: &'tcx ast::Expr,
+                            rhs: &'tcx P<ast::Expr>,
+                            is_binop_assignment: IsBinopAssignment) {
         let tcx = fcx.ccx.tcx;
 
         let lvalue_pref = match is_binop_assignment {
@@ -2923,11 +2966,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     fn check_user_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                  ex: &ast::Expr,
-                                  lhs_expr: &ast::Expr,
+                                  ex: &'tcx ast::Expr,
+                                  lhs_expr: &'tcx ast::Expr,
                                   lhs_resolved_t: Ty<'tcx>,
                                   op: ast::BinOp,
-                                  rhs: &P<ast::Expr>) -> Ty<'tcx> {
+                                  rhs: &'tcx P<ast::Expr>) -> Ty<'tcx> {
         let tcx = fcx.ccx.tcx;
         let lang = &tcx.lang_items;
         let (name, trait_did) = match op.node {
@@ -2966,8 +3009,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                  op_str: &str,
                                  mname: &str,
                                  trait_did: Option<ast::DefId>,
-                                 ex: &ast::Expr,
-                                 rhs_expr: &ast::Expr,
+                                 ex: &'tcx ast::Expr,
+                                 rhs_expr: &'tcx ast::Expr,
                                  rhs_t: Ty<'tcx>,
                                  op: ast::UnOp) -> Ty<'tcx> {
        lookup_op_method(fcx, ex, rhs_t, token::intern(mname),
@@ -2980,11 +3023,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     // Check field access expressions
-    fn check_field(fcx: &FnCtxt,
-                   expr: &ast::Expr,
-                   lvalue_pref: LvaluePreference,
-                   base: &ast::Expr,
-                   field: &ast::SpannedIdent) {
+    fn check_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                            expr: &'tcx ast::Expr,
+                            lvalue_pref: LvaluePreference,
+                            base: &'tcx ast::Expr,
+                            field: &ast::SpannedIdent) {
         let tcx = fcx.ccx.tcx;
         check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
         let expr_t = structurally_resolved_type(fcx, expr.span,
@@ -3077,11 +3120,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     // Check tuple index expressions
-    fn check_tup_field(fcx: &FnCtxt,
-                       expr: &ast::Expr,
-                       lvalue_pref: LvaluePreference,
-                       base: &ast::Expr,
-                       idx: codemap::Spanned<uint>) {
+    fn check_tup_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                expr: &'tcx ast::Expr,
+                                lvalue_pref: LvaluePreference,
+                                base: &'tcx ast::Expr,
+                                idx: codemap::Spanned<uint>) {
         let tcx = fcx.ccx.tcx;
         check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
         let expr_t = structurally_resolved_type(fcx, expr.span,
@@ -3149,7 +3192,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                                 node_id: ast::NodeId,
                                                 substitutions: &'tcx subst::Substs<'tcx>,
                                                 field_types: &[ty::field_ty],
-                                                ast_fields: &[ast::Field],
+                                                ast_fields: &'tcx [ast::Field],
                                                 check_completeness: bool,
                                                 enum_id_opt: Option<ast::DefId>)  {
         let tcx = fcx.ccx.tcx;
@@ -3252,12 +3295,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         }
     }
 
-    fn check_struct_constructor(fcx: &FnCtxt,
-                                id: ast::NodeId,
-                                span: codemap::Span,
-                                class_id: ast::DefId,
-                                fields: &[ast::Field],
-                                base_expr: Option<&ast::Expr>) {
+    fn check_struct_constructor<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                         id: ast::NodeId,
+                                         span: codemap::Span,
+                                         class_id: ast::DefId,
+                                         fields: &'tcx [ast::Field],
+                                         base_expr: Option<&'tcx ast::Expr>) {
         let tcx = fcx.ccx.tcx;
 
         // Generate the struct type.
@@ -3294,12 +3337,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         fcx.write_ty(id, struct_type);
     }
 
-    fn check_struct_enum_variant(fcx: &FnCtxt,
-                                 id: ast::NodeId,
-                                 span: codemap::Span,
-                                 enum_id: ast::DefId,
-                                 variant_id: ast::DefId,
-                                 fields: &[ast::Field]) {
+    fn check_struct_enum_variant<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                          id: ast::NodeId,
+                                          span: codemap::Span,
+                                          enum_id: ast::DefId,
+                                          variant_id: ast::DefId,
+                                          fields: &'tcx [ast::Field]) {
         let tcx = fcx.ccx.tcx;
 
         // Look up the number of type parameters and the raw type, and
@@ -3324,10 +3367,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         fcx.write_ty(id, enum_type);
     }
 
-    fn check_struct_fields_on_error(fcx: &FnCtxt,
-                                    id: ast::NodeId,
-                                    fields: &[ast::Field],
-                                    base_expr: &Option<P<ast::Expr>>) {
+    fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                             id: ast::NodeId,
+                                             fields: &'tcx [ast::Field],
+                                             base_expr: &'tcx Option<P<ast::Expr>>) {
         // Make sure to still write the types
         // otherwise we might ICE
         fcx.write_error(id);
@@ -4126,15 +4169,15 @@ impl<'tcx> Repr<'tcx> for Expectation<'tcx> {
     }
 }
 
-pub fn check_decl_initializer(fcx: &FnCtxt,
-                              nid: ast::NodeId,
-                              init: &ast::Expr)
+pub fn check_decl_initializer<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                       nid: ast::NodeId,
+                                       init: &'tcx ast::Expr)
 {
     let local_ty = fcx.local_ty(init.span, nid);
     check_expr_coercable_to_type(fcx, init, local_ty)
 }
 
-pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local)  {
+pub fn check_decl_local<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, local: &'tcx ast::Local)  {
     let tcx = fcx.ccx.tcx;
 
     let t = fcx.local_ty(local.span, local.id);
@@ -4159,7 +4202,7 @@ pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local)  {
     }
 }
 
-pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt)  {
+pub fn check_stmt<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, stmt: &'tcx ast::Stmt)  {
     let node_id;
     let mut saw_bot = false;
     let mut saw_err = false;
@@ -4204,7 +4247,7 @@ pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt)  {
     }
 }
 
-pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block)  {
+pub fn check_block_no_value<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, blk: &'tcx ast::Block)  {
     check_block_with_expected(fcx, blk, ExpectHasType(ty::mk_nil(fcx.tcx())));
     let blkty = fcx.node_ty(blk.id);
     if ty::type_is_error(blkty) {
@@ -4216,7 +4259,7 @@ pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block)  {
 }
 
 fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                       blk: &ast::Block,
+                                       blk: &'tcx ast::Block,
                                        expected: Expectation<'tcx>) {
     let prev = {
         let mut fcx_ps = fcx.ps.borrow_mut();
@@ -4299,17 +4342,17 @@ fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 /// length expression in a fixed-length vector, but someday it might be
 /// extended to type-level numeric literals.
 fn check_const_in_type<'a,'tcx>(ccx: &'a CrateCtxt<'a,'tcx>,
-                                expr: &ast::Expr,
+                                expr: &'tcx ast::Expr,
                                 expected_type: Ty<'tcx>) {
     let inh = static_inherited_fields(ccx);
     let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(expected_type), expr.id);
     check_const_with_ty(&fcx, expr.span, expr, expected_type);
 }
 
-fn check_const(ccx: &CrateCtxt,
-               sp: Span,
-               e: &ast::Expr,
-               id: ast::NodeId) {
+fn check_const<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
+                        sp: Span,
+                        e: &'tcx ast::Expr,
+                        id: ast::NodeId) {
     let inh = static_inherited_fields(ccx);
     let rty = ty::node_id_to_type(ccx.tcx, id);
     let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id);
@@ -4319,7 +4362,7 @@ fn check_const(ccx: &CrateCtxt,
 
 fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                  _: Span,
-                                 e: &ast::Expr,
+                                 e: &'tcx ast::Expr,
                                  declty: Ty<'tcx>) {
     // Gather locals in statics (because of block expressions).
     // This is technically unnecessary because locals in static items are forbidden,
@@ -4420,10 +4463,10 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {
     }
 }
 
-pub fn check_enum_variants(ccx: &CrateCtxt,
-                           sp: Span,
-                           vs: &[P<ast::Variant>],
-                           id: ast::NodeId) {
+pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
+                                    sp: Span,
+                                    vs: &'tcx [P<ast::Variant>],
+                                    id: ast::NodeId) {
 
     fn disr_in_range(ccx: &CrateCtxt,
                      ty: attr::IntType,
@@ -4453,7 +4496,7 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
     }
 
     fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                          vs: &[P<ast::Variant>],
+                          vs: &'tcx [P<ast::Variant>],
                           id: ast::NodeId,
                           hint: attr::ReprAttr)
                           -> Vec<Rc<ty::VariantInfo<'tcx>>> {
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 449220b1c85..f7babadd41f 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -46,7 +46,9 @@ use middle::expr_use_visitor as euv;
 use middle::mem_categorization as mc;
 use middle::ty::{self};
 use middle::infer::{InferCtxt, UpvarRegion};
+use std::collections::HashSet;
 use syntax::ast;
+use syntax::ast_util;
 use syntax::codemap::Span;
 use syntax::visit::{self, Visitor};
 use util::ppaux::Repr;
@@ -56,13 +58,18 @@ use util::ppaux::Repr;
 
 pub fn closure_analyze_fn(fcx: &FnCtxt,
                           _id: ast::NodeId,
-                          decl: &ast::FnDecl,
-                          body: &ast::Block) {
+                          _decl: &ast::FnDecl,
+                          body: &ast::Block)
+{
     let mut seed = SeedBorrowKind::new(fcx);
     seed.visit_block(body);
+    let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
 
-    let mut adjust = AdjustBorrowKind::new(fcx);
-    adjust.analyze_fn(decl, body);
+    let mut adjust = AdjustBorrowKind::new(fcx, &closures_with_inferred_kinds);
+    adjust.visit_block(body);
+
+    // it's our job to process these.
+    assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty());
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -70,6 +77,7 @@ pub fn closure_analyze_fn(fcx: &FnCtxt,
 
 struct SeedBorrowKind<'a,'tcx:'a> {
     fcx: &'a FnCtxt<'a,'tcx>,
+    closures_with_inferred_kinds: HashSet<ast::NodeId>,
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
@@ -105,7 +113,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
 
 impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
     fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> {
-        SeedBorrowKind { fcx: fcx }
+        SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() }
     }
 
     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
@@ -121,6 +129,14 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
                      capture_clause: ast::CaptureClause,
                      _body: &ast::Block)
     {
+        let closure_def_id = ast_util::local_def(expr.id);
+        if !self.fcx.inh.closure_kinds.borrow().contains_key(&closure_def_id) {
+            self.closures_with_inferred_kinds.insert(expr.id);
+            self.fcx.inh.closure_kinds.borrow_mut().insert(closure_def_id, ty::FnClosureKind);
+            debug!("check_closure: adding closure_id={} to closures_with_inferred_kinds",
+                   closure_def_id.repr(self.tcx()));
+        }
+
         ty::with_freevars(self.tcx(), expr.id, |freevars| {
             for freevar in freevars.iter() {
                 let var_node_id = freevar.def.local_node_id();
@@ -151,29 +167,78 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
 // ADJUST BORROW KIND
 
 struct AdjustBorrowKind<'a,'tcx:'a> {
-    fcx: &'a FnCtxt<'a,'tcx>
+    fcx: &'a FnCtxt<'a,'tcx>,
+    closures_with_inferred_kinds: &'a HashSet<ast::NodeId>,
 }
 
 impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
-    fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
-        AdjustBorrowKind { fcx: fcx }
+    fn new(fcx: &'a FnCtxt<'a,'tcx>,
+           closures_with_inferred_kinds: &'a HashSet<ast::NodeId>)
+           -> AdjustBorrowKind<'a,'tcx> {
+        AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds }
     }
 
     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
         self.fcx.tcx()
     }
 
-    fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
+    fn analyze_closure(&mut self, id: ast::NodeId, decl: &ast::FnDecl, body: &ast::Block) {
         /*!
          * Analysis starting point.
          */
 
         self.visit_block(body);
 
-        debug!("analyzing fn body with id {}", body.id);
+        debug!("analyzing closure `{}` with fn body id `{}`", id, body.id);
 
         let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
         euv.walk_fn(decl, body);
+
+        // If we had not yet settled on a closure kind for this closure,
+        // then we should have by now. Process and remove any deferred resolutions.
+        //
+        // Interesting fact: all calls to this closure must come
+        // *after* its definition.  Initially, I thought that some
+        // kind of fixed-point iteration would be required, due to the
+        // possibility of twisted examples like this one:
+        //
+        // ```rust
+        // let mut closure0 = None;
+        // let vec = vec!(1, 2, 3);
+        //
+        // loop {
+        //     {
+        //         let closure1 = || {
+        //             match closure0.take() {
+        //                 Some(c) => {
+        //                     return c(); // (*) call to `closure0` before it is defined
+        //                 }
+        //                 None => { }
+        //             }
+        //         };
+        //         closure1();
+        //     }
+        //
+        //     closure0 = || vec;
+        // }
+        // ```
+        //
+        // However, this turns out to be wrong. Examples like this
+        // fail to compile because the type of the variable `c` above
+        // is an inference variable.  And in fact since closure types
+        // cannot be written, there is no way to make this example
+        // work without a boxed closure. This implies that we can't
+        // have two closures that recursively call one another without
+        // some form of boxing (and hence explicit writing of a
+        // closure kind) involved. Huzzah. -nmatsakis
+        let closure_def_id = ast_util::local_def(id);
+        if self.closures_with_inferred_kinds.contains(&id) {
+            let mut deferred_call_resolutions =
+                self.fcx.remove_deferred_call_resolutions(closure_def_id);
+            for deferred_call_resolution in deferred_call_resolutions.iter_mut() {
+                deferred_call_resolution.resolve(self.fcx);
+            }
+        }
     }
 
     fn adjust_upvar_borrow_kind_for_consume(&self,
@@ -198,13 +263,29 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
         match guarantor.cat {
             mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
             mc::cat_deref(_, _, mc::Implicit(..)) => {
-                if let mc::NoteUpvarRef(upvar_id) = cmt.note {
-                    debug!("adjust_upvar_borrow_kind_for_consume: \
-                            setting upvar_id={:?} to by value",
-                           upvar_id);
+                match cmt.note {
+                    mc::NoteUpvarRef(upvar_id) => {
+                        debug!("adjust_upvar_borrow_kind_for_consume: \
+                                setting upvar_id={:?} to by value",
+                               upvar_id);
 
-                    let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
-                    upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
+                        // to move out of an upvar, this must be a FnOnce closure
+                        self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
+
+                        let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
+                        upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
+                    }
+                    mc::NoteClosureEnv(upvar_id) => {
+                        // we get just a closureenv ref if this is a
+                        // `move` closure, or if the upvar has already
+                        // been inferred to by-value. In any case, we
+                        // must still adjust the kind of the closure
+                        // to be a FnOnce closure to permit moves out
+                        // of the environment.
+                        self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
+                    }
+                    mc::NoteNone => {
+                    }
                 }
             }
             _ => { }
@@ -229,15 +310,7 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
 
             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
             mc::cat_deref(base, _, mc::Implicit(..)) => {
-                if let mc::NoteUpvarRef(upvar_id) = cmt.note {
-                    // if this is an implicit deref of an
-                    // upvar, then we need to modify the
-                    // borrow_kind of the upvar to make sure it
-                    // is inferred to mutable if necessary
-                    let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
-                    let ub = &mut upvar_capture_map[upvar_id];
-                    self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
-                } else {
+                if !self.try_adjust_upvar_deref(&cmt.note, ty::MutBorrow) {
                     // assignment to deref of an `&mut`
                     // borrowed pointer implies that the
                     // pointer itself must be unique, but not
@@ -271,15 +344,7 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
 
             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
             mc::cat_deref(base, _, mc::Implicit(..)) => {
-                if let mc::NoteUpvarRef(upvar_id) = cmt.note {
-                    // if this is an implicit deref of an
-                    // upvar, then we need to modify the
-                    // borrow_kind of the upvar to make sure it
-                    // is inferred to unique if necessary
-                    let mut ub = self.fcx.inh.upvar_capture_map.borrow_mut();
-                    let ub = &mut ub[upvar_id];
-                    self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
-                } else {
+                if !self.try_adjust_upvar_deref(&cmt.note, ty::UniqueImmBorrow) {
                     // for a borrowed pointer to be unique, its
                     // base must be unique
                     self.adjust_upvar_borrow_kind_for_unique(base);
@@ -295,6 +360,48 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
         }
     }
 
+    fn try_adjust_upvar_deref(&self,
+                              note: &mc::Note,
+                              borrow_kind: ty::BorrowKind)
+                              -> bool
+    {
+        assert!(match borrow_kind {
+            ty::MutBorrow => true,
+            ty::UniqueImmBorrow => true,
+
+            // imm borrows never require adjusting any kinds, so we don't wind up here
+            ty::ImmBorrow => false,
+        });
+
+        match *note {
+            mc::NoteUpvarRef(upvar_id) => {
+                // if this is an implicit deref of an
+                // upvar, then we need to modify the
+                // borrow_kind of the upvar to make sure it
+                // is inferred to mutable if necessary
+                let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
+                let ub = &mut upvar_capture_map[upvar_id];
+                self.adjust_upvar_borrow_kind(upvar_id, ub, borrow_kind);
+
+                // also need to be in an FnMut closure since this is not an ImmBorrow
+                self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
+
+                true
+            }
+            mc::NoteClosureEnv(upvar_id) => {
+                // this kind of deref occurs in a `move` closure, or
+                // for a by-value upvar; in either case, to mutate an
+                // upvar, we need to be an FnMut closure
+                self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
+
+                true
+            }
+            mc::NoteNone => {
+                false
+            }
+        }
+    }
+
     /// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
     /// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
     /// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
@@ -328,6 +435,40 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
             }
         }
     }
+
+    fn adjust_closure_kind(&self,
+                           closure_id: ast::NodeId,
+                           new_kind: ty::ClosureKind) {
+        debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
+               closure_id, new_kind);
+
+        if !self.closures_with_inferred_kinds.contains(&closure_id) {
+            return;
+        }
+
+        let closure_def_id = ast_util::local_def(closure_id);
+        let mut closure_kinds = self.fcx.inh.closure_kinds.borrow_mut();
+        let existing_kind = closure_kinds[closure_def_id];
+
+        debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
+               closure_id, existing_kind, new_kind);
+
+        match (existing_kind, new_kind) {
+            (ty::FnClosureKind, ty::FnClosureKind) |
+            (ty::FnMutClosureKind, ty::FnClosureKind) |
+            (ty::FnMutClosureKind, ty::FnMutClosureKind) |
+            (ty::FnOnceClosureKind, _) => {
+                // no change needed
+            }
+
+            (ty::FnClosureKind, ty::FnMutClosureKind) |
+            (ty::FnClosureKind, ty::FnOnceClosureKind) |
+            (ty::FnMutClosureKind, ty::FnOnceClosureKind) => {
+                // new kind is stronger than the old kind
+                closure_kinds.insert(closure_def_id, new_kind);
+            }
+        }
+    }
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
@@ -336,14 +477,14 @@ impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
                 decl: &'v ast::FnDecl,
                 body: &'v ast::Block,
                 span: Span,
-                _id: ast::NodeId)
+                id: ast::NodeId)
     {
         match fn_kind {
             visit::FkItemFn(..) | visit::FkMethod(..) => {
                 // ignore nested fn items
             }
             visit::FkFnBlock => {
-                self.analyze_fn(decl, body);
+                self.analyze_closure(id, decl, body);
                 visit::walk_fn(self, fn_kind, decl, body, span);
             }
         }
diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs
index 41b63830279..5cf71a9be6a 100644
--- a/src/librustc_typeck/check/vtable.rs
+++ b/src/librustc_typeck/check/vtable.rs
@@ -277,12 +277,22 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>,
     }
 }
 
-pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
-    debug!("select_all_fcx_obligations_or_error");
+pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) {
+    debug!("select_all_fcx_obligations_and_apply_defaults");
 
     select_fcx_obligations_where_possible(fcx);
     fcx.default_type_parameters();
+    select_fcx_obligations_where_possible(fcx);
+}
+
+pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
+    debug!("select_all_fcx_obligations_or_error");
+
+    // upvar inference should have ensured that all deferrred call
+    // resolutions are handled by now.
+    assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty());
 
+    select_all_fcx_obligations_and_apply_defaults(fcx);
     let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
     let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx);
     match r {
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index f7e1afed8fc..0eaecf8ac05 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -204,14 +204,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             return
         }
 
-        for (def_id, closure) in self.fcx.inh.closures.borrow().iter() {
-            let closure_ty = self.resolve(&closure.closure_type,
-                                          ResolvingClosure(*def_id));
-            let closure = ty::Closure {
-                closure_type: closure_ty,
-                kind: closure.kind,
-            };
-            self.fcx.tcx().closures.borrow_mut().insert(*def_id, closure);
+        for (def_id, closure_ty) in self.fcx.inh.closure_tys.borrow().iter() {
+            let closure_ty = self.resolve(closure_ty, ResolvingClosure(*def_id));
+            self.fcx.tcx().closure_tys.borrow_mut().insert(*def_id, closure_ty);
+        }
+
+        for (def_id, &closure_kind) in self.fcx.inh.closure_kinds.borrow().iter() {
+            self.fcx.tcx().closure_kinds.borrow_mut().insert(*def_id, closure_kind);
         }
     }
 
diff --git a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs
new file mode 100644
index 00000000000..7398e6f1089
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs
@@ -0,0 +1,33 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Various unsuccessful attempts to put the unboxed closure kind
+// inference into an awkward position that might require fixed point
+// iteration (basically where inferring the kind of a closure `c`
+// would require knowing the kind of `c`). I currently believe this is
+// impossible.
+
+fn a() {
+    // This case of recursion wouldn't even require fixed-point
+    // iteration, but it still doesn't work. The weird structure with
+    // the `Option` is to avoid giving any useful hints about the `Fn`
+    // kind via the expected type.
+    let mut factorial: Option<Box<Fn(u32) -> u32>> = None;
+
+    let f = |x: u32| -> u32 {
+        let g = factorial.as_ref().unwrap();
+        if x == 0 {1} else {x * g(x-1)}
+    };
+
+    factorial = Some(Box::new(f));
+    //~^ ERROR cannot assign to `factorial` because it is borrowed
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs
new file mode 100644
index 00000000000..f40c8fc7474
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs
@@ -0,0 +1,39 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Various unsuccessful attempts to put the unboxed closure kind
+// inference into an awkward position that might require fixed point
+// iteration (basically where inferring the kind of a closure `c`
+// would require knowing the kind of `c`). I currently believe this is
+// impossible.
+
+fn a() {
+    let mut closure0 = None;
+    let vec = vec!(1, 2, 3);
+
+    loop {
+        {
+            let closure1 = || {
+                match closure0.take() {
+                    Some(c) => {
+                        return c();
+                        //~^ ERROR the type of this value must be known in this context
+                    }
+                    None => { }
+                }
+            };
+            closure1();
+        }
+
+        closure0 = || vec;
+    }
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs b/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs
new file mode 100644
index 00000000000..f993b8fa8c4
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs
@@ -0,0 +1,18 @@
+// 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_closures)]
+
+fn main() {
+    let mut zero = || {};
+    let () = zero.call_mut(());
+    //~^ ERROR we have not yet inferred what kind of closure it is
+}
+
diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs
new file mode 100644
index 00000000000..afbc141b5d2
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs
@@ -0,0 +1,32 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    // Here this must be inferred to FnMut so that it can mutate counter,
+    // but we forgot the mut.
+    let tick1 = || {
+        counter += 1;
+    };
+
+    // In turn, tick2 must be inferred to FnMut so that it can call
+    // tick1, but we forgot the mut. The error message we currently
+    // get seems... suboptimal.
+    let tick2 = || { //~ ERROR closure cannot assign to immutable local variable `tick1`
+        tick1();
+    };
+
+    tick2(); //~ ERROR cannot borrow
+}
+
diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs
new file mode 100644
index 00000000000..9e4ed307996
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs
@@ -0,0 +1,18 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+    let tick = || counter += 1;
+    tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable
+}
diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs
new file mode 100644
index 00000000000..de17d25b4c3
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs
@@ -0,0 +1,18 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+    let tick = move || counter += 1;
+    tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable
+}
diff --git a/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs
new file mode 100644
index 00000000000..0050fbdde26
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+use std::mem;
+
+fn main() {
+    let mut counter: Vec<i32> = Vec::new();
+    let tick = || mem::drop(counter);
+    tick();
+    tick(); //~ ERROR use of moved value: `tick`
+}
diff --git a/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs b/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs
new file mode 100644
index 00000000000..f9709b8c596
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+use std::mem;
+
+fn main() {
+    let mut counter: Vec<i32> = Vec::new();
+    let tick = move || mem::drop(counter);
+    tick();
+    tick(); //~ ERROR use of moved value: `tick`
+}
diff --git a/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs b/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs
new file mode 100644
index 00000000000..215b2c6798e
--- /dev/null
+++ b/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs
@@ -0,0 +1,48 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(core,unboxed_closures)]
+
+use std::marker::CovariantType;
+
+// A erroneous variant of `run-pass/unboxed_closures-infer-recursive-fn.rs`
+// where we attempt to perform mutation in the recursive function. This fails to compile
+// because it winds up requiring `FnMut` which enforces linearity.
+
+struct YCombinator<F,A,R> {
+    func: F,
+    marker: CovariantType<(A,R)>,
+}
+
+impl<F,A,R> YCombinator<F,A,R> {
+    fn new(f: F) -> YCombinator<F,A,R> {
+        YCombinator { func: f, marker: CovariantType }
+    }
+}
+
+impl<A,R,F : FnMut(&mut FnMut(A) -> R, A) -> R> FnMut<(A,)> for YCombinator<F,A,R> {
+    type Output = R;
+
+    extern "rust-call" fn call_mut(&mut self, (arg,): (A,)) -> R {
+        (self.func)(self, arg)
+            //~^ ERROR cannot borrow `*self` as mutable more than once at a time
+    }
+}
+
+fn main() {
+    let mut counter = 0;
+    let factorial = |recur: &mut FnMut(u32) -> u32, arg: u32| -> u32 {
+        counter += 1;
+        if arg == 0 {1} else {arg * recur(arg-1)}
+    };
+    let mut factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial);
+    let mut r = factorial(10);
+    assert_eq!(3628800, r);
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs b/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs
new file mode 100644
index 00000000000..09b8c8f4454
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs
@@ -0,0 +1,29 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        // Here this must be inferred to FnMut so that it can mutate counter:
+        let mut tick1 = || counter += 1;
+
+        // In turn, tick2 must be inferred to FnMut so that it can call tick1:
+        let mut tick2 = || { tick1(); tick1(); };
+
+        tick2();
+    }
+
+    assert_eq!(counter, 2);
+}
+
diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs b/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs
new file mode 100644
index 00000000000..794527249bf
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs
@@ -0,0 +1,25 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this `move`
+// closure that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    let v = {
+        let mut tick = move || { counter += 1; counter };
+        tick();
+        tick()
+    };
+
+    assert_eq!(counter, 0);
+    assert_eq!(v, 2);
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut.rs b/src/test/run-pass/unboxed-closures-infer-fnmut.rs
new file mode 100644
index 00000000000..67f36b9a920
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-fnmut.rs
@@ -0,0 +1,24 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let mut tick = || counter += 1;
+        tick();
+        tick();
+    }
+
+    assert_eq!(counter, 2);
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs b/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs
new file mode 100644
index 00000000000..9f8fc80819b
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs
@@ -0,0 +1,37 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(unsafe_destructor)]
+
+// Test that we are able to infer a suitable kind for this `move`
+// closure that is just called (`FnOnce`).
+
+use std::mem;
+
+struct DropMe<'a>(&'a mut i32);
+
+#[unsafe_destructor]
+impl<'a> Drop for DropMe<'a> {
+    fn drop(&mut self) {
+        *self.0 += 1;
+    }
+}
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let drop_me = DropMe(&mut counter);
+        let tick = move || mem::drop(drop_me);
+        tick();
+    }
+
+    assert_eq!(counter, 1);
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce.rs b/src/test/run-pass/unboxed-closures-infer-fnonce.rs
new file mode 100644
index 00000000000..f0f10139c5b
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-fnonce.rs
@@ -0,0 +1,37 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(unsafe_destructor)]
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnOnce`).
+
+use std::mem;
+
+struct DropMe<'a>(&'a mut i32);
+
+#[unsafe_destructor]
+impl<'a> Drop for DropMe<'a> {
+    fn drop(&mut self) {
+        *self.0 += 1;
+    }
+}
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let drop_me = DropMe(&mut counter);
+        let tick = || mem::drop(drop_me);
+        tick();
+    }
+
+    assert_eq!(counter, 1);
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs b/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs
new file mode 100644
index 00000000000..1f9b821178c
--- /dev/null
+++ b/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs
@@ -0,0 +1,47 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(core,unboxed_closures)]
+
+use std::marker::CovariantType;
+
+// Test that we are able to infer a suitable kind for a "recursive"
+// closure.  As far as I can tell, coding up a recursive closure
+// requires the good ol' [Y Combinator].
+//
+// [Y Combinator]: http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator
+
+struct YCombinator<F,A,R> {
+    func: F,
+    marker: CovariantType<(A,R)>,
+}
+
+impl<F,A,R> YCombinator<F,A,R> {
+    fn new(f: F) -> YCombinator<F,A,R> {
+        YCombinator { func: f, marker: CovariantType }
+    }
+}
+
+impl<A,R,F : Fn(&Fn(A) -> R, A) -> R> Fn<(A,)> for YCombinator<F,A,R> {
+    type Output = R;
+
+    extern "rust-call" fn call(&self, (arg,): (A,)) -> R {
+        (self.func)(self, arg)
+    }
+}
+
+fn main() {
+    let factorial = |recur: &Fn(u32) -> u32, arg: u32| -> u32 {
+        if arg == 0 {1} else {arg * recur(arg-1)}
+    };
+    let factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial);
+    let r = factorial(10);
+    assert_eq!(3628800, r);
+}