about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-11-21 22:52:19 +0000
committerbors <bors@rust-lang.org>2017-11-21 22:52:19 +0000
commitd6d09e0b4dac93ae07dae6206bf95e7cea0124a2 (patch)
tree245513cfdfc7bac11b0e7049ea60bf9908b01216
parent63739ab7b210c1a8c890c2ea5238a3284877daa3 (diff)
parent00732a31a0a9b00d4ffb333473ae95e66f8e1dfc (diff)
downloadrust-d6d09e0b4dac93ae07dae6206bf95e7cea0124a2.tar.gz
rust-d6d09e0b4dac93ae07dae6206bf95e7cea0124a2.zip
Auto merge of #45879 - nikomatsakis:nll-kill-cyclic-closures, r=arielb1
move closure kind, signature into `ClosureSubsts`

Instead of using side-tables, store the closure-kind and signature in the substitutions themselves. This has two key effects:

- It means that the closure's type changes as inference finds out more things, which is very nice.
    - As a result, it avoids the need for the `freshen_closure_like` code (though we still use it for generators).
- It avoids cyclic closures calls.
    - These were never meant to be supported, precisely because they make a lot of the fancy inference that we do much more complicated. However, due to an oversight, it was previously possible -- if challenging -- to create a setup where a closure *directly* called itself (see e.g. #21410).

We have to see what the effect of this change is, though. Needs a crater run. Marking as [WIP] until that has been assessed.

r? @arielb1
-rw-r--r--src/librustc/dep_graph/dep_node.rs2
-rw-r--r--src/librustc/diagnostics.rs31
-rw-r--r--src/librustc/ich/impls_ty.rs3
-rw-r--r--src/librustc/infer/combine.rs18
-rw-r--r--src/librustc/infer/error_reporting/mod.rs63
-rw-r--r--src/librustc/infer/error_reporting/need_type_info.rs4
-rw-r--r--src/librustc/infer/freshen.rs133
-rw-r--r--src/librustc/infer/mod.rs61
-rw-r--r--src/librustc/infer/type_variable.rs5
-rw-r--r--src/librustc/middle/mem_categorization.rs17
-rw-r--r--src/librustc/traits/error_reporting.rs14
-rw-r--r--src/librustc/traits/fulfill.rs4
-rw-r--r--src/librustc/traits/project.rs3
-rw-r--r--src/librustc/traits/select.rs13
-rw-r--r--src/librustc/traits/util.rs4
-rw-r--r--src/librustc/ty/context.rs97
-rw-r--r--src/librustc/ty/error.rs16
-rw-r--r--src/librustc/ty/instance.rs2
-rw-r--r--src/librustc/ty/maps/mod.rs8
-rw-r--r--src/librustc/ty/maps/plumbing.rs2
-rw-r--r--src/librustc/ty/mod.rs23
-rw-r--r--src/librustc/ty/structural_impls.rs20
-rw-r--r--src/librustc/ty/sty.rs162
-rw-r--r--src/librustc/ty/wf.rs50
-rw-r--r--src/librustc/util/ppaux.rs6
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs6
-rw-r--r--src/librustc_borrowck/lib.rs1
-rw-r--r--src/librustc_metadata/cstore_impl.rs2
-rw-r--r--src/librustc_metadata/decoder.rs24
-rw-r--r--src/librustc_metadata/encoder.rs32
-rw-r--r--src/librustc_metadata/schema.rs6
-rw-r--r--src/librustc_mir/build/mod.rs17
-rw-r--r--src/librustc_mir/hair/cx/expr.rs4
-rw-r--r--src/librustc_mir/transform/generator.rs6
-rw-r--r--src/librustc_mir/transform/inline.rs47
-rw-r--r--src/librustc_trans/common.rs4
-rw-r--r--src/librustc_trans_utils/monomorphize.rs2
-rw-r--r--src/librustc_typeck/check/callee.rs6
-rw-r--r--src/librustc_typeck/check/closure.rs36
-rw-r--r--src/librustc_typeck/check/mod.rs43
-rw-r--r--src/librustc_typeck/check/upvar.rs546
-rw-r--r--src/librustc_typeck/check/writeback.rs42
-rw-r--r--src/librustc_typeck/collect.rs56
-rw-r--r--src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs3
-rw-r--r--src/test/compile-fail/generator-yielding-or-returning-itself.rs45
-rw-r--r--src/test/compile-fail/issue-22638.rs2
-rw-r--r--src/test/compile-fail/issue-25439.rs (renamed from src/test/run-pass/issue-25439.rs)2
-rw-r--r--src/test/compile-fail/occurs-check-2.rs2
-rw-r--r--src/test/compile-fail/occurs-check.rs2
-rw-r--r--src/test/mir-opt/inline-closure-borrows-arg.rs50
-rw-r--r--src/test/mir-opt/inline-closure.rs6
-rw-r--r--src/test/ui/block-result/issue-3563.stderr13
-rw-r--r--src/test/ui/span/coerce-suggestions.stderr3
-rw-r--r--src/test/ui/unboxed-closure-no-cyclic-sig.rs (renamed from src/test/run-pass/issue-21410.rs)4
-rw-r--r--src/test/ui/unboxed-closure-no-cyclic-sig.stderr12
55 files changed, 1003 insertions, 782 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 523a244c836..db3aa9a1efa 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -498,9 +498,7 @@ define_dep_nodes!( <'tcx>
     [] IsAutoImpl(DefId),
     [] ImplTraitRef(DefId),
     [] ImplPolarity(DefId),
-    [] ClosureKind(DefId),
     [] FnSignature(DefId),
-    [] GenSignature(DefId),
     [] CoerceUnsizedInfo(DefId),
 
     [] ItemVarianceConstraints(DefId),
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 75ef3fc0c3d..cba1a66c17c 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -1969,8 +1969,39 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
 ```
 "##,
 
+E0644: r##"
+A closure or generator was constructed that references its own type.
+
+Erroneous example:
+
+```compile-fail,E0644
+fn fix<F>(f: &F)
+  where F: Fn(&F)
+{
+  f(&f);
 }
 
+fn main() {
+  fix(&|y| {
+    // Here, when `x` is called, the parameter `y` is equal to `x`.
+  });
+}
+```
+
+Rust does not permit a closure to directly reference its own type,
+either through an argument (as in the example above) or by capturing
+itself through its environment. This restriction helps keep closure
+inference tractable.
+
+The easiest fix is to rewrite your closure into a top-level function,
+or into a method. In some cases, you may also be able to have your
+closure call itself by capturing a `&Fn()` object or `fn()` pointer
+that refers to itself. That is permitting, since the closure would be
+invoking itself via a virtual call, and hence does not directly
+reference its own *type*.
+
+"##, }
+
 
 register_diagnostics! {
 //  E0006 // merged with E0005
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index e7627b110fa..8f2ad98f858 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -236,8 +236,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::Predicate<'gcx> {
             ty::Predicate::ObjectSafe(def_id) => {
                 def_id.hash_stable(hcx, hasher);
             }
-            ty::Predicate::ClosureKind(def_id, closure_kind) => {
+            ty::Predicate::ClosureKind(def_id, closure_substs, closure_kind) => {
                 def_id.hash_stable(hcx, hasher);
+                closure_substs.hash_stable(hcx, hasher);
                 closure_kind.hash_stable(hcx, hasher);
             }
             ty::Predicate::ConstEvaluatable(def_id, substs) => {
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 40e933b26a2..50a37e12531 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -270,6 +270,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
             for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
             ambient_variance,
             needs_wf: false,
+            root_ty: ty,
         };
 
         let ty = generalize.relate(&ty, &ty)?;
@@ -280,10 +281,23 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
 
 struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
     infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+
+    /// Span, used when creating new type variables and things.
     span: Span,
+
+    /// The vid of the type variable that is in the process of being
+    /// instantiated; if we find this within the type we are folding,
+    /// that means we would have created a cyclic type.
     for_vid_sub_root: ty::TyVid,
+
+    /// Track the variance as we descend into the type.
     ambient_variance: ty::Variance,
-    needs_wf: bool, // see the field `needs_wf` in `Generalization`
+
+    /// See the field `needs_wf` in `Generalization`.
+    needs_wf: bool,
+
+    /// The root type that we are generalizing. Used when reporting cycles.
+    root_ty: Ty<'tcx>,
 }
 
 /// Result from a generalization operation. This includes
@@ -386,7 +400,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                 if sub_vid == self.for_vid_sub_root {
                     // If sub-roots are equal, then `for_vid` and
                     // `vid` are related via subtyping.
-                    return Err(TypeError::CyclicTy);
+                    return Err(TypeError::CyclicTy(self.root_ty));
                 } else {
                     match variables.probe_root(vid) {
                         Some(u) => {
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 4f36193e197..6fadafc7b97 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -689,9 +689,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                          diag: &mut DiagnosticBuilder<'tcx>,
                          cause: &ObligationCause<'tcx>,
                          secondary_span: Option<(Span, String)>,
-                         values: Option<ValuePairs<'tcx>>,
+                         mut values: Option<ValuePairs<'tcx>>,
                          terr: &TypeError<'tcx>)
     {
+        // For some types of errors, expected-found does not make
+        // sense, so just ignore the values we were given.
+        match terr {
+            TypeError::CyclicTy(_) => { values = None; }
+            _ => { }
+        }
+
         let (expected_found, exp_found, is_simple_error) = match values {
             None => (None, None, false),
             Some(values) => {
@@ -780,17 +787,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                terr);
 
         let span = trace.cause.span;
-        let failure_str = trace.cause.as_failure_str();
-        let mut diag = match trace.cause.code {
-            ObligationCauseCode::IfExpressionWithNoElse => {
+        let failure_code = trace.cause.as_failure_code(terr);
+        let mut diag = match failure_code {
+            FailureCode::Error0317(failure_str) => {
                 struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
             }
-            ObligationCauseCode::MainFunctionType => {
+            FailureCode::Error0580(failure_str) => {
                 struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
             }
-            _ => {
+            FailureCode::Error0308(failure_str) => {
                 struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str)
             }
+            FailureCode::Error0644(failure_str) => {
+                struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
+            }
         };
         self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
         diag
@@ -1040,23 +1050,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 }
 
+enum FailureCode {
+    Error0317(&'static str),
+    Error0580(&'static str),
+    Error0308(&'static str),
+    Error0644(&'static str),
+}
+
 impl<'tcx> ObligationCause<'tcx> {
-    fn as_failure_str(&self) -> &'static str {
+    fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode {
+        use self::FailureCode::*;
         use traits::ObligationCauseCode::*;
         match self.code {
-            CompareImplMethodObligation { .. } => "method not compatible with trait",
-            MatchExpressionArm { source, .. } => match source {
+            CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
+            MatchExpressionArm { source, .. } => Error0308(match source {
                 hir::MatchSource::IfLetDesugar{..} => "`if let` arms have incompatible types",
                 _ => "match arms have incompatible types",
-            },
-            IfExpression => "if and else have incompatible types",
-            IfExpressionWithNoElse => "if may be missing an else clause",
-            EquatePredicate => "equality predicate not satisfied",
-            MainFunctionType => "main function has wrong type",
-            StartFunctionType => "start function has wrong type",
-            IntrinsicType => "intrinsic has wrong type",
-            MethodReceiver => "mismatched method receiver",
-            _ => "mismatched types",
+            }),
+            IfExpression => Error0308("if and else have incompatible types"),
+            IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
+            EquatePredicate => Error0308("equality predicate not satisfied"),
+            MainFunctionType => Error0580("main function has wrong type"),
+            StartFunctionType => Error0308("start function has wrong type"),
+            IntrinsicType => Error0308("intrinsic has wrong type"),
+            MethodReceiver => Error0308("mismatched method receiver"),
+
+            // In the case where we have no more specific thing to
+            // say, also take a look at the error code, maybe we can
+            // tailor to that.
+            _ => match terr {
+                TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() =>
+                    Error0644("closure/generator type that references itself"),
+                _ =>
+                    Error0308("mismatched types"),
+            }
         }
     }
 
diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs
index 22d9a9e313b..ea3c0a8ddb4 100644
--- a/src/librustc/infer/error_reporting/need_type_info.rs
+++ b/src/librustc/infer/error_reporting/need_type_info.rs
@@ -125,9 +125,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             // ```
             labels.clear();
             labels.push((pattern.span, format!("consider giving this closure parameter a type")));
-        }
-
-        if let Some(pattern) = local_visitor.found_local_pattern {
+        } else if let Some(pattern) = local_visitor.found_local_pattern {
             if let Some(simple_name) = pattern.simple_name() {
                 labels.push((pattern.span, format!("consider giving `{}` a type", simple_name)));
             } else {
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 41e7dffe54d..426c61e9ac0 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -43,9 +43,7 @@
 
 use ty::{self, Ty, TyCtxt, TypeFoldable};
 use ty::fold::TypeFolder;
-use ty::subst::Substs;
 use util::nodemap::FxHashMap;
-use hir::def_id::DefId;
 
 use std::collections::hash_map::Entry;
 
@@ -56,7 +54,6 @@ pub struct TypeFreshener<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
     freshen_count: u32,
     freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>,
-    closure_set: Vec<DefId>,
 }
 
 impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> {
@@ -66,7 +63,6 @@ impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> {
             infcx,
             freshen_count: 0,
             freshen_map: FxHashMap(),
-            closure_set: vec![],
         }
     }
 
@@ -92,88 +88,6 @@ impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> {
             }
         }
     }
-
-    fn next_fresh<F>(&mut self,
-                     freshener: F)
-                     -> Ty<'tcx>
-        where F: FnOnce(u32) -> ty::InferTy,
-    {
-        let index = self.freshen_count;
-        self.freshen_count += 1;
-        self.infcx.tcx.mk_infer(freshener(index))
-    }
-
-    fn freshen_closure_like<M, C>(&mut self,
-                                  def_id: DefId,
-                                  substs: ty::ClosureSubsts<'tcx>,
-                                  t: Ty<'tcx>,
-                                  markers: M,
-                                  combine: C)
-                                  -> Ty<'tcx>
-        where M: FnOnce(&mut Self) -> (Ty<'tcx>, Ty<'tcx>),
-              C: FnOnce(&'tcx Substs<'tcx>) -> Ty<'tcx>
-    {
-        let tcx = self.infcx.tcx;
-
-        let closure_in_progress = self.infcx.in_progress_tables.map_or(false, |tables| {
-            tcx.hir.as_local_node_id(def_id).map_or(false, |closure_id| {
-                tables.borrow().local_id_root ==
-                    Some(DefId::local(tcx.hir.node_to_hir_id(closure_id).owner))
-            })
-        });
-
-        if !closure_in_progress {
-            // If this closure belongs to another infcx, its kind etc. were
-            // fully inferred and its signature/kind are exactly what's listed
-            // in its infcx. So we don't need to add the markers for them.
-            return t.super_fold_with(self);
-        }
-
-        // We are encoding a closure in progress. Because we want our freshening
-        // key to contain all inference information needed to make sense of our
-        // value, we need to encode the closure signature and kind. The way
-        // we do that is to add them as 2 variables to the closure substs,
-        // basically because it's there (and nobody cares about adding extra stuff
-        // to substs).
-        //
-        // This means the "freshened" closure substs ends up looking like
-        //     fresh_substs = [PARENT_SUBSTS* ; UPVARS* ; SIG_MARKER ; KIND_MARKER]
-        let (marker_1, marker_2) = if self.closure_set.contains(&def_id) {
-            // We found the closure def-id within its own signature. Just
-            // leave a new freshened type - any matching operations would
-            // have found and compared the exterior closure already to
-            // get here.
-            //
-            // In that case, we already know what the signature would
-            // be - the parent closure on the stack already contains a
-            // "copy" of the signature, so there is no reason to encode
-            // it again for injectivity. Just use a fresh type variable
-            // to make everything comparable.
-            //
-            // For example (closure kinds omitted for clarity)
-            //     t=[closure FOO sig=[closure BAR sig=[closure FOO ..]]]
-            // Would get encoded to
-            //     t=[closure FOO sig=[closure BAR sig=[closure FOO sig=$0]]]
-            //
-            // and we can decode by having
-            //     $0=[closure BAR {sig doesn't exist in decode}]
-            // and get
-            //     t=[closure FOO]
-            //     sig[FOO] = [closure BAR]
-            //     sig[BAR] = [closure FOO]
-            (self.next_fresh(ty::FreshTy), self.next_fresh(ty::FreshTy))
-        } else {
-            self.closure_set.push(def_id);
-            let markers = markers(self);
-            self.closure_set.pop();
-            markers
-        };
-
-        combine(tcx.mk_substs(
-            substs.substs.iter().map(|k| k.fold_with(self)).chain(
-                [marker_1, marker_2].iter().cloned().map(From::from)
-                    )))
-    }
 }
 
 impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
@@ -249,51 +163,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
                 t
             }
 
-            ty::TyClosure(def_id, substs) => {
-                self.freshen_closure_like(
-                    def_id, substs, t,
-                    |this| {
-                        // HACK: use a "random" integer type to mark the kind. Because
-                        // different closure kinds shouldn't get unified during
-                        // selection, the "subtyping" relationship (where any kind is
-                        // better than no kind) shouldn't  matter here, just that the
-                        // types are different.
-                        let closure_kind = this.infcx.closure_kind(def_id);
-                        let closure_kind_marker = match closure_kind {
-                            None => tcx.types.i8,
-                            Some(ty::ClosureKind::Fn) => tcx.types.i16,
-                            Some(ty::ClosureKind::FnMut) => tcx.types.i32,
-                            Some(ty::ClosureKind::FnOnce) => tcx.types.i64,
-                        };
-
-                        let closure_sig = this.infcx.fn_sig(def_id);
-                        (tcx.mk_fn_ptr(closure_sig.fold_with(this)),
-                         closure_kind_marker)
-                    },
-                    |substs| tcx.mk_closure(def_id, substs)
-                )
-            }
-
-            ty::TyGenerator(def_id, substs, interior) => {
-                self.freshen_closure_like(
-                    def_id, substs, t,
-                    |this| {
-                        let gen_sig = this.infcx.generator_sig(def_id).unwrap();
-                        // FIXME: want to revise this strategy when generator
-                        // signatures can actually contain LBRs.
-                        let sig = this.tcx().no_late_bound_regions(&gen_sig)
-                            .unwrap_or_else(|| {
-                                bug!("late-bound regions in signature of {:?}",
-                                     def_id)
-                            });
-                        (sig.yield_ty, sig.return_ty).fold_with(this)
-                    },
-                    |substs| {
-                        tcx.mk_generator(def_id, ty::ClosureSubsts { substs }, interior)
-                    }
-                )
-            }
-
+            ty::TyGenerator(..) |
             ty::TyBool |
             ty::TyChar |
             ty::TyInt(..) |
@@ -314,6 +184,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
             ty::TyProjection(..) |
             ty::TyForeign(..) |
             ty::TyParam(..) |
+            ty::TyClosure(..) |
             ty::TyAnon(..) => {
                 t.super_fold_with(self)
             }
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 4f923f0b249..7302bad0ca1 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -1463,26 +1463,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         !traits::type_known_to_meet_bound(self, param_env, ty, copy_def_id, span)
     }
 
+    /// Obtains the latest type of the given closure; this may be a
+    /// closure in the current function, in which case its
+    /// `ClosureKind` may not yet be known.
     pub fn closure_kind(&self,
-                        def_id: DefId)
+                        closure_def_id: DefId,
+                        closure_substs: ty::ClosureSubsts<'tcx>)
                         -> Option<ty::ClosureKind>
     {
-        if let Some(tables) = self.in_progress_tables {
-            if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
-                let hir_id = self.tcx.hir.node_to_hir_id(id);
-                return tables.borrow()
-                             .closure_kinds()
-                             .get(hir_id)
-                             .cloned()
-                             .map(|(kind, _)| kind);
-            }
-        }
-
-        // During typeck, ALL closures are local. But afterwards,
-        // during trans, we see closure ids from other traits.
-        // That may require loading the closure data out of the
-        // cstore.
-        Some(self.tcx.closure_kind(def_id))
+        let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx);
+        let closure_kind_ty = self.shallow_resolve(&closure_kind_ty);
+        closure_kind_ty.to_opt_closure_kind()
     }
 
     /// Obtain the signature of a function or closure.
@@ -1490,11 +1481,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// work during the type-checking of the enclosing function and
     /// return the closure signature in its partially inferred state.
     pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
+        // Do we have an in-progress set of tables we are inferring?
         if let Some(tables) = self.in_progress_tables {
+            // Is this a local item?
             if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
-                let hir_id = self.tcx.hir.node_to_hir_id(id);
-                if let Some(&ty) = tables.borrow().closure_tys().get(hir_id) {
-                    return ty;
+                // Is it a local *closure*?
+                if self.tcx.is_closure(def_id) {
+                    let hir_id = self.tcx.hir.node_to_hir_id(id);
+                    // Is this local closure contained within the tables we are inferring?
+                    if tables.borrow().local_id_root == Some(DefId::local(hir_id.owner)) {
+                        // if so, extract signature from there.
+                        let closure_ty = tables.borrow().node_id_to_type(hir_id);
+                        let (closure_def_id, closure_substs) = match closure_ty.sty {
+                            ty::TyClosure(closure_def_id, closure_substs) =>
+                                (closure_def_id, closure_substs),
+                            _ =>
+                                bug!("closure with non-closure type: {:?}", closure_ty),
+                        };
+                        assert_eq!(def_id, closure_def_id);
+                        let closure_sig_ty = closure_substs.closure_sig_ty(def_id, self.tcx);
+                        let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
+                        return closure_sig_ty.fn_sig(self.tcx);
+                    }
                 }
             }
         }
@@ -1502,19 +1510,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.tcx.fn_sig(def_id)
     }
 
-    pub fn generator_sig(&self, def_id: DefId) -> Option<ty::PolyGenSig<'tcx>> {
-        if let Some(tables) = self.in_progress_tables {
-            if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
-                let hir_id = self.tcx.hir.node_to_hir_id(id);
-                if let Some(&ty) = tables.borrow().generator_sigs().get(hir_id) {
-                    return ty.map(|t| ty::Binder(t));
-                }
-            }
-        }
-
-        self.tcx.generator_sig(def_id)
-    }
-
     /// Normalizes associated types in `value`, potentially returning
     /// new obligations that must further be processed.
     pub fn partially_normalize_associated_types_in<T>(&self,
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index cc91a637b89..6aa094d2cd6 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -56,7 +56,10 @@ pub enum TypeVariableOrigin {
     NormalizeProjectionType(Span),
     TypeInference(Span),
     TypeParameterDefinition(Span, ast::Name),
-    TransformedUpvar(Span),
+
+    /// one of the upvars or closure kind parameters in a `ClosureSubsts`
+    /// (before it has been determined)
+    ClosureSynthetic(Span),
     SubstitutionPlaceholder(Span),
     AutoDeref(Span),
     AdjustmentType(Span),
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index c89d67d4aab..0d4429de22a 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -750,10 +750,19 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
 
         let kind = match self.node_ty(fn_hir_id)?.sty {
             ty::TyGenerator(..) => ty::ClosureKind::FnOnce,
-            ty::TyClosure(..) => {
-                match self.tables.closure_kinds().get(fn_hir_id) {
-                    Some(&(kind, _)) => kind,
-                    None => span_bug!(span, "missing closure kind"),
+            ty::TyClosure(closure_def_id, closure_substs) => {
+                match self.infcx {
+                    // During upvar inference we may not know the
+                    // closure kind, just use the LATTICE_BOTTOM value.
+                    Some(infcx) =>
+                        infcx.closure_kind(closure_def_id, closure_substs)
+                             .unwrap_or(ty::ClosureKind::LATTICE_BOTTOM),
+
+                    None =>
+                        self.tcx.global_tcx()
+                                .lift(&closure_substs)
+                                .expect("no inference cx, but inference variables in closure ty")
+                                .closure_kind(closure_def_id, self.tcx.global_tcx()),
                 }
             }
             ref t => span_bug!(span, "unexpected type for fn in mem_categorization: {:?}", t),
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 7c38cf75b8d..46ec2be4a1f 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -643,8 +643,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                                             violations)
                     }
 
-                    ty::Predicate::ClosureKind(closure_def_id, kind) => {
-                        let found_kind = self.closure_kind(closure_def_id).unwrap();
+                    ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
+                        let found_kind = self.closure_kind(closure_def_id, closure_substs).unwrap();
                         let closure_span = self.tcx.hir.span_if_local(closure_def_id).unwrap();
                         let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
                         let mut err = struct_span_err!(
@@ -663,14 +663,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                         if let Some(tables) = self.in_progress_tables {
                             let tables = tables.borrow();
                             let closure_hir_id = self.tcx.hir.node_to_hir_id(node_id);
-                            match tables.closure_kinds().get(closure_hir_id) {
-                                Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) => {
-                                    err.span_note(span, &format!(
+                            match (found_kind, tables.closure_kind_origins().get(closure_hir_id)) {
+                                (ty::ClosureKind::FnOnce, Some((span, name))) => {
+                                    err.span_note(*span, &format!(
                                         "closure is `FnOnce` because it moves the \
                                          variable `{}` out of its environment", name));
                                 },
-                                Some(&(ty::ClosureKind::FnMut, Some((span, name)))) => {
-                                    err.span_note(span, &format!(
+                                (ty::ClosureKind::FnMut, Some((span, name))) => {
+                                    err.span_note(*span, &format!(
                                         "closure is `FnMut` because it mutates the \
                                          variable `{}` here", name));
                                 },
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index 297feead617..6b681322c9b 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -438,8 +438,8 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             }
         }
 
-        ty::Predicate::ClosureKind(closure_def_id, kind) => {
-            match selcx.infcx().closure_kind(closure_def_id) {
+        ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
+            match selcx.infcx().closure_kind(closure_def_id, closure_substs) {
                 Some(closure_kind) => {
                     if closure_kind.extends(kind) {
                         Ok(Some(vec![]))
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 9c56df058c3..0cc755dc427 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -1264,8 +1264,7 @@ fn confirm_generator_candidate<'cx, 'gcx, 'tcx>(
     vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>)
     -> Progress<'tcx>
 {
-    let gen_sig = selcx.infcx().generator_sig(vtable.closure_def_id).unwrap()
-        .subst(selcx.tcx(), vtable.substs.substs);
+    let gen_sig = vtable.substs.generator_poly_sig(vtable.closure_def_id, selcx.tcx());
     let Normalized {
         value: gen_sig,
         obligations
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 7716770d318..4bc3e2dd4d8 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -718,8 +718,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::ClosureKind(closure_def_id, kind) => {
-                match self.infcx.closure_kind(closure_def_id) {
+            ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
+                match self.infcx.closure_kind(closure_def_id, closure_substs) {
                     Some(closure_kind) => {
                         if closure_kind.extends(kind) {
                             EvaluatedToOk
@@ -1593,10 +1593,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // touch bound regions, they just capture the in-scope
         // type/region parameters
         match obligation.self_ty().skip_binder().sty {
-            ty::TyClosure(closure_def_id, _) => {
+            ty::TyClosure(closure_def_id, closure_substs) => {
                 debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}",
                        kind, obligation);
-                match self.infcx.closure_kind(closure_def_id) {
+                match self.infcx.closure_kind(closure_def_id, closure_substs) {
                     Some(closure_kind) => {
                         debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind);
                         if closure_kind.extends(kind) {
@@ -2726,7 +2726,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         obligations.push(Obligation::new(
             obligation.cause.clone(),
             obligation.param_env,
-            ty::Predicate::ClosureKind(closure_def_id, kind)));
+            ty::Predicate::ClosureKind(closure_def_id, substs, kind)));
 
         Ok(VtableClosureData {
             closure_def_id,
@@ -3184,8 +3184,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                       substs: ty::ClosureSubsts<'tcx>)
                                       -> ty::PolyTraitRef<'tcx>
     {
-        let gen_sig = self.infcx.generator_sig(closure_def_id).unwrap()
-            .subst(self.tcx(), substs.substs);
+        let gen_sig = substs.generator_poly_sig(closure_def_id, self.tcx());
         let ty::Binder((trait_ref, ..)) =
             self.tcx().generator_trait_ref_and_outputs(obligation.predicate.def_id(),
                                                        obligation.predicate.0.self_ty(), // (1)
diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs
index 42e0834e8e4..898accb9021 100644
--- a/src/librustc/traits/util.rs
+++ b/src/librustc/traits/util.rs
@@ -43,8 +43,8 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
         ty::Predicate::ObjectSafe(data) =>
             ty::Predicate::ObjectSafe(data),
 
-        ty::Predicate::ClosureKind(closure_def_id, kind) =>
-            ty::Predicate::ClosureKind(closure_def_id, kind),
+        ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) =>
+            ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind),
 
         ty::Predicate::Subtype(ref data) =>
             ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)),
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 904f9a09125..b0dce1f6684 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -356,16 +356,9 @@ pub struct TypeckTables<'tcx> {
     /// Borrows
     pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>,
 
-    /// Records the type of each closure.
-    closure_tys: ItemLocalMap<ty::PolyFnSig<'tcx>>,
-
-    /// Records the kind of each closure and the span and name of the variable
-    /// that caused the closure to be this kind.
-    closure_kinds: ItemLocalMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>,
-
-    generator_sigs: ItemLocalMap<Option<ty::GenSig<'tcx>>>,
-
-    generator_interiors: ItemLocalMap<ty::GeneratorInterior<'tcx>>,
+    /// Records the reasons that we picked the kind of each closure;
+    /// not all closures are present in the map.
+    closure_kind_origins: ItemLocalMap<(Span, ast::Name)>,
 
     /// For each fn, records the "liberated" types of its arguments
     /// and return type. Liberated means that all bound regions
@@ -411,10 +404,7 @@ impl<'tcx> TypeckTables<'tcx> {
             pat_binding_modes: ItemLocalMap(),
             pat_adjustments: ItemLocalMap(),
             upvar_capture_map: FxHashMap(),
-            generator_sigs: ItemLocalMap(),
-            generator_interiors: ItemLocalMap(),
-            closure_tys: ItemLocalMap(),
-            closure_kinds: ItemLocalMap(),
+            closure_kind_origins: ItemLocalMap(),
             liberated_fn_sigs: ItemLocalMap(),
             fru_field_types: ItemLocalMap(),
             cast_kinds: ItemLocalMap(),
@@ -609,34 +599,17 @@ impl<'tcx> TypeckTables<'tcx> {
         self.upvar_capture_map[&upvar_id]
     }
 
-    pub fn closure_tys(&self) -> LocalTableInContext<ty::PolyFnSig<'tcx>> {
-        LocalTableInContext {
-            local_id_root: self.local_id_root,
-            data: &self.closure_tys
-        }
-    }
-
-    pub fn closure_tys_mut(&mut self)
-                           -> LocalTableInContextMut<ty::PolyFnSig<'tcx>> {
-        LocalTableInContextMut {
-            local_id_root: self.local_id_root,
-            data: &mut self.closure_tys
-        }
-    }
-
-    pub fn closure_kinds(&self) -> LocalTableInContext<(ty::ClosureKind,
-                                                        Option<(Span, ast::Name)>)> {
+    pub fn closure_kind_origins(&self) -> LocalTableInContext<(Span, ast::Name)> {
         LocalTableInContext {
             local_id_root: self.local_id_root,
-            data: &self.closure_kinds
+            data: &self.closure_kind_origins
         }
     }
 
-    pub fn closure_kinds_mut(&mut self)
-            -> LocalTableInContextMut<(ty::ClosureKind, Option<(Span, ast::Name)>)> {
+    pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<(Span, ast::Name)> {
         LocalTableInContextMut {
             local_id_root: self.local_id_root,
-            data: &mut self.closure_kinds
+            data: &mut self.closure_kind_origins
         }
     }
 
@@ -681,42 +654,6 @@ impl<'tcx> TypeckTables<'tcx> {
             data: &mut self.cast_kinds
         }
     }
-
-    pub fn generator_sigs(&self)
-        -> LocalTableInContext<Option<ty::GenSig<'tcx>>>
-    {
-        LocalTableInContext {
-            local_id_root: self.local_id_root,
-            data: &self.generator_sigs,
-        }
-    }
-
-    pub fn generator_sigs_mut(&mut self)
-        -> LocalTableInContextMut<Option<ty::GenSig<'tcx>>>
-    {
-        LocalTableInContextMut {
-            local_id_root: self.local_id_root,
-            data: &mut self.generator_sigs,
-        }
-    }
-
-    pub fn generator_interiors(&self)
-        -> LocalTableInContext<ty::GeneratorInterior<'tcx>>
-    {
-        LocalTableInContext {
-            local_id_root: self.local_id_root,
-            data: &self.generator_interiors,
-        }
-    }
-
-    pub fn generator_interiors_mut(&mut self)
-        -> LocalTableInContextMut<ty::GeneratorInterior<'tcx>>
-    {
-        LocalTableInContextMut {
-            local_id_root: self.local_id_root,
-            data: &mut self.generator_interiors,
-        }
-    }
 }
 
 impl<'gcx> HashStable<StableHashingContext<'gcx>> for TypeckTables<'gcx> {
@@ -732,8 +669,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for TypeckTables<'gcx> {
             ref pat_binding_modes,
             ref pat_adjustments,
             ref upvar_capture_map,
-            ref closure_tys,
-            ref closure_kinds,
+            ref closure_kind_origins,
             ref liberated_fn_sigs,
             ref fru_field_types,
 
@@ -742,8 +678,6 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for TypeckTables<'gcx> {
             ref used_trait_imports,
             tainted_by_errors,
             ref free_region_map,
-            ref generator_sigs,
-            ref generator_interiors,
         } = *self;
 
         hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
@@ -775,13 +709,10 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for TypeckTables<'gcx> {
                  hcx.def_path_hash(closure_def_id))
             });
 
-            closure_tys.hash_stable(hcx, hasher);
-            closure_kinds.hash_stable(hcx, hasher);
+            closure_kind_origins.hash_stable(hcx, hasher);
             liberated_fn_sigs.hash_stable(hcx, hasher);
             fru_field_types.hash_stable(hcx, hasher);
             cast_kinds.hash_stable(hcx, hasher);
-            generator_sigs.hash_stable(hcx, hasher);
-            generator_interiors.hash_stable(hcx, hasher);
             used_trait_imports.hash_stable(hcx, hasher);
             tainted_by_errors.hash_stable(hcx, hasher);
             free_region_map.hash_stable(hcx, hasher);
@@ -1981,11 +1912,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 
     pub fn mk_closure(self,
                       closure_id: DefId,
-                      substs: &'tcx Substs<'tcx>)
-        -> Ty<'tcx> {
-        self.mk_closure_from_closure_substs(closure_id, ClosureSubsts {
-            substs,
-        })
+                      substs: ClosureSubsts<'tcx>)
+                      -> Ty<'tcx> {
+        self.mk_closure_from_closure_substs(closure_id, substs)
     }
 
     pub fn mk_closure_from_closure_substs(self,
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index 228ca76ed9a..cb68e576e5a 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -49,7 +49,11 @@ pub enum TypeError<'tcx> {
     FloatMismatch(ExpectedFound<ast::FloatTy>),
     Traits(ExpectedFound<DefId>),
     VariadicMismatch(ExpectedFound<bool>),
-    CyclicTy,
+
+    /// Instantiating a type variable with the given type would have
+    /// created a cycle (because it appears somewhere within that
+    /// type).
+    CyclicTy(Ty<'tcx>),
     ProjectionMismatched(ExpectedFound<DefId>),
     ProjectionBoundsLength(ExpectedFound<usize>),
     TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>),
@@ -84,7 +88,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
         }
 
         match *self {
-            CyclicTy => write!(f, "cyclic type of infinite size"),
+            CyclicTy(_) => write!(f, "cyclic type of infinite size"),
             Mismatch => write!(f, "types differ"),
             UnsafetyMismatch(values) => {
                 write!(f, "expected {} fn, found {} fn",
@@ -304,6 +308,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 
                 self.note_and_explain_type_err(db, &err, sp);
             }
+            CyclicTy(ty) => {
+                // Watch out for various cases of cyclic types and try to explain.
+                if ty.is_closure() || ty.is_generator() {
+                    db.note("closures cannot capture themselves or take themselves as argument;\n\
+                             this error may be the result of a recent compiler bug-fix,\n\
+                             see https://github.com/rust-lang/rust/issues/46062 for more details");
+                }
+            }
             _ => {}
         }
     }
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index 6ea953c3f73..70636f8b6fe 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -189,7 +189,7 @@ fn resolve_closure<'a, 'tcx>(
                    requested_kind: ty::ClosureKind)
 -> Instance<'tcx>
 {
-    let actual_kind = tcx.closure_kind(def_id);
+    let actual_kind = substs.closure_kind(def_id, tcx);
 
     match needs_fn_once_adapter_shim(actual_kind, requested_kind) {
         Ok(true) => fn_once_adapter_instance(tcx, def_id, substs),
diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs
index 2f648e8d3ff..a477e779af9 100644
--- a/src/librustc/ty/maps/mod.rs
+++ b/src/librustc/ty/maps/mod.rs
@@ -166,20 +166,12 @@ define_maps! { <'tcx>
     /// for trans. This is also the only query that can fetch non-local MIR, at present.
     [] fn optimized_mir: MirOptimized(DefId) -> &'tcx mir::Mir<'tcx>,
 
-    /// Type of each closure. The def ID is the ID of the
-    /// expression defining the closure.
-    [] fn closure_kind: ClosureKind(DefId) -> ty::ClosureKind,
-
     /// The result of unsafety-checking this def-id.
     [] fn unsafety_check_result: UnsafetyCheckResult(DefId) -> mir::UnsafetyCheckResult,
 
     /// The signature of functions and closures.
     [] fn fn_sig: FnSignature(DefId) -> ty::PolyFnSig<'tcx>,
 
-    /// Records the signature of each generator. The def ID is the ID of the
-    /// expression defining the closure.
-    [] fn generator_sig: GenSignature(DefId) -> Option<ty::PolyGenSig<'tcx>>,
-
     /// Caches CoerceUnsized kinds for impls on custom types.
     [] fn coerce_unsized_info: CoerceUnsizedInfo(DefId)
         -> ty::adjustment::CoerceUnsizedInfo,
diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs
index 1ca8fc6eb48..739537c7c3a 100644
--- a/src/librustc/ty/maps/plumbing.rs
+++ b/src/librustc/ty/maps/plumbing.rs
@@ -782,9 +782,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
         DepKind::IsAutoImpl => { force!(is_auto_impl, def_id!()); }
         DepKind::ImplTraitRef => { force!(impl_trait_ref, def_id!()); }
         DepKind::ImplPolarity => { force!(impl_polarity, def_id!()); }
-        DepKind::ClosureKind => { force!(closure_kind, def_id!()); }
         DepKind::FnSignature => { force!(fn_sig, def_id!()); }
-        DepKind::GenSignature => { force!(generator_sig, def_id!()); }
         DepKind::CoerceUnsizedInfo => { force!(coerce_unsized_info, def_id!()); }
         DepKind::ItemVariances => { force!(variances_of, def_id!()); }
         DepKind::IsConstFn => { force!(is_const_fn, def_id!()); }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 48ec92a255b..583dcb46f00 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -896,7 +896,7 @@ pub enum Predicate<'tcx> {
     /// No direct syntax. May be thought of as `where T : FnFoo<...>`
     /// for some substitutions `...` and T being a closure type.
     /// Satisfied (or refuted) once we know the closure's kind.
-    ClosureKind(DefId, ClosureKind),
+    ClosureKind(DefId, ClosureSubsts<'tcx>, ClosureKind),
 
     /// `T1 <: T2`
     Subtype(PolySubtypePredicate<'tcx>),
@@ -999,8 +999,8 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> {
                 Predicate::WellFormed(data.subst(tcx, substs)),
             Predicate::ObjectSafe(trait_def_id) =>
                 Predicate::ObjectSafe(trait_def_id),
-            Predicate::ClosureKind(closure_def_id, kind) =>
-                Predicate::ClosureKind(closure_def_id, kind),
+            Predicate::ClosureKind(closure_def_id, closure_substs, kind) =>
+                Predicate::ClosureKind(closure_def_id, closure_substs.subst(tcx, substs), kind),
             Predicate::ConstEvaluatable(def_id, const_substs) =>
                 Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)),
         }
@@ -1182,8 +1182,8 @@ impl<'tcx> Predicate<'tcx> {
             ty::Predicate::ObjectSafe(_trait_def_id) => {
                 vec![]
             }
-            ty::Predicate::ClosureKind(_closure_def_id, _kind) => {
-                vec![]
+            ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) => {
+                closure_substs.substs.types().collect()
             }
             ty::Predicate::ConstEvaluatable(_, substs) => {
                 substs.types().collect()
@@ -1932,6 +1932,9 @@ pub enum ClosureKind {
 }
 
 impl<'a, 'tcx> ClosureKind {
+    // This is the initial value used when doing upvar inference.
+    pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
+
     pub fn trait_did(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> DefId {
         match *self {
             ClosureKind::Fn => tcx.require_lang_item(FnTraitLangItem),
@@ -1957,6 +1960,16 @@ impl<'a, 'tcx> ClosureKind {
             _ => false,
         }
     }
+
+    /// Returns the representative scalar type for this closure kind.
+    /// See `TyS::to_opt_closure_kind` for more details.
+    pub fn to_ty(self, tcx: TyCtxt<'_, '_, 'tcx>) -> Ty<'tcx> {
+        match self {
+            ty::ClosureKind::Fn => tcx.types.i8,
+            ty::ClosureKind::FnMut => tcx.types.i16,
+            ty::ClosureKind::FnOnce => tcx.types.i32,
+        }
+    }
 }
 
 impl<'tcx> TyS<'tcx> {
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index e5c24b4fcf9..83207fbe3c3 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -211,8 +211,11 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> {
             ty::Predicate::WellFormed(ty) => {
                 tcx.lift(&ty).map(ty::Predicate::WellFormed)
             }
-            ty::Predicate::ClosureKind(closure_def_id, kind) => {
-                Some(ty::Predicate::ClosureKind(closure_def_id, kind))
+            ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
+                tcx.lift(&closure_substs)
+                   .map(|closure_substs| ty::Predicate::ClosureKind(closure_def_id,
+                                                                    closure_substs,
+                                                                    kind))
             }
             ty::Predicate::ObjectSafe(trait_def_id) => {
                 Some(ty::Predicate::ObjectSafe(trait_def_id))
@@ -420,7 +423,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             FloatMismatch(x) => FloatMismatch(x),
             Traits(x) => Traits(x),
             VariadicMismatch(x) => VariadicMismatch(x),
-            CyclicTy => CyclicTy,
+            CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)),
             ProjectionMismatched(x) => ProjectionMismatched(x),
             ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
 
@@ -966,8 +969,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
                 ty::Predicate::Projection(binder.fold_with(folder)),
             ty::Predicate::WellFormed(data) =>
                 ty::Predicate::WellFormed(data.fold_with(folder)),
-            ty::Predicate::ClosureKind(closure_def_id, kind) =>
-                ty::Predicate::ClosureKind(closure_def_id, kind),
+            ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) =>
+                ty::Predicate::ClosureKind(closure_def_id, closure_substs.fold_with(folder), kind),
             ty::Predicate::ObjectSafe(trait_def_id) =>
                 ty::Predicate::ObjectSafe(trait_def_id),
             ty::Predicate::ConstEvaluatable(def_id, substs) =>
@@ -984,7 +987,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
             ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor),
             ty::Predicate::Projection(ref binder) => binder.visit_with(visitor),
             ty::Predicate::WellFormed(data) => data.visit_with(visitor),
-            ty::Predicate::ClosureKind(_closure_def_id, _kind) => false,
+            ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) =>
+                closure_substs.visit_with(visitor),
             ty::Predicate::ObjectSafe(_trait_def_id) => false,
             ty::Predicate::ConstEvaluatable(_def_id, substs) => substs.visit_with(visitor),
         }
@@ -1169,7 +1173,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
             FloatMismatch(x) => FloatMismatch(x),
             Traits(x) => Traits(x),
             VariadicMismatch(x) => VariadicMismatch(x),
-            CyclicTy => CyclicTy,
+            CyclicTy(t) => CyclicTy(t.fold_with(folder)),
             ProjectionMismatched(x) => ProjectionMismatched(x),
             ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
             Sorts(x) => Sorts(x.fold_with(folder)),
@@ -1196,6 +1200,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
             OldStyleLUB(ref x) => x.visit_with(visitor),
             TyParamDefaultMismatch(ref x) => x.visit_with(visitor),
             ExistentialMismatch(x) => x.visit_with(visitor),
+            CyclicTy(t) => t.visit_with(visitor),
             Mismatch |
             Mutability |
             TupleSize(_) |
@@ -1205,7 +1210,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
             FloatMismatch(_) |
             Traits(_) |
             VariadicMismatch(_) |
-            CyclicTy |
             ProjectionMismatched(_) |
             ProjectionBoundsLength(_) => false,
         }
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 65406c3d16c..7406fbf8208 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -174,16 +174,26 @@ pub enum TypeVariants<'tcx> {
 
 /// A closure can be modeled as a struct that looks like:
 ///
-///     struct Closure<'l0...'li, T0...Tj, U0...Uk> {
+///     struct Closure<'l0...'li, T0...Tj, CK, CS, U0...Uk> {
 ///         upvar0: U0,
 ///         ...
 ///         upvark: Uk
 ///     }
 ///
-/// where 'l0...'li and T0...Tj are the lifetime and type parameters
-/// in scope on the function that defined the closure, and U0...Uk are
-/// type parameters representing the types of its upvars (borrowed, if
-/// appropriate).
+/// where:
+///
+/// - 'l0...'li and T0...Tj are the lifetime and type parameters
+///   in scope on the function that defined the closure,
+/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This
+///   is rather hackily encoded via a scalar type. See
+///   `TyS::to_opt_closure_kind` for details.
+/// - CS represents the *closure signature*, representing as a `fn()`
+///   type. For example, `fn(u32, u32) -> u32` would mean that the closure
+///   implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait
+///   specified above.
+/// - U0...Uk are type parameters representing the types of its upvars
+///   (borrowed, if appropriate; that is, if Ui represents a by-ref upvar,
+///    and the up-var has the type `Foo`, then `Ui = &Foo`).
 ///
 /// So, for example, given this function:
 ///
@@ -246,6 +256,17 @@ pub enum TypeVariants<'tcx> {
 /// closure C wind up influencing the decisions we ought to make for
 /// closure C (which would then require fixed point iteration to
 /// handle). Plus it fixes an ICE. :P
+///
+/// ## Generators
+///
+/// Perhaps surprisingly, `ClosureSubsts` are also used for
+/// generators.  In that case, what is written above is only half-true
+/// -- the set of type parameters is similar, but the role of CK and
+/// CS are different.  CK represents the "yield type" and CS
+/// represents the "return type" of the generator.
+///
+/// It'd be nice to split this struct into ClosureSubsts and
+/// GeneratorSubsts, I believe. -nmatsakis
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct ClosureSubsts<'tcx> {
     /// Lifetime and type parameters from the enclosing function,
@@ -256,14 +277,97 @@ pub struct ClosureSubsts<'tcx> {
     pub substs: &'tcx Substs<'tcx>,
 }
 
-impl<'a, 'gcx, 'acx, 'tcx> ClosureSubsts<'tcx> {
+/// Struct returned by `split()`. Note that these are subslices of the
+/// parent slice and not canonical substs themselves.
+struct SplitClosureSubsts<'tcx> {
+    closure_kind_ty: Ty<'tcx>,
+    closure_sig_ty: Ty<'tcx>,
+    upvar_kinds: &'tcx [Kind<'tcx>],
+}
+
+impl<'tcx> ClosureSubsts<'tcx> {
+    /// Divides the closure substs into their respective
+    /// components. Single source of truth with respect to the
+    /// ordering.
+    fn split(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> SplitClosureSubsts<'tcx> {
+        let generics = tcx.generics_of(def_id);
+        let parent_len = generics.parent_count();
+        SplitClosureSubsts {
+            closure_kind_ty: self.substs[parent_len].as_type().expect("CK should be a type"),
+            closure_sig_ty: self.substs[parent_len + 1].as_type().expect("CS should be a type"),
+            upvar_kinds: &self.substs[parent_len + 2..],
+        }
+    }
+
     #[inline]
-    pub fn upvar_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'acx>) ->
+    pub fn upvar_tys(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) ->
         impl Iterator<Item=Ty<'tcx>> + 'tcx
     {
-        let generics = tcx.generics_of(def_id);
-        self.substs[self.substs.len()-generics.own_count()..].iter().map(
-            |t| t.as_type().expect("unexpected region in upvars"))
+        let SplitClosureSubsts { upvar_kinds, .. } = self.split(def_id, tcx);
+        upvar_kinds.iter().map(|t| t.as_type().expect("upvar should be type"))
+    }
+
+    /// Returns the closure kind for this closure; may return a type
+    /// variable during inference. To get the closure kind during
+    /// inference, use `infcx.closure_kind(def_id, substs)`.
+    pub fn closure_kind_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> {
+        self.split(def_id, tcx).closure_kind_ty
+    }
+
+    /// Returns the type representing the closure signature for this
+    /// closure; may contain type variables during inference. To get
+    /// the closure signature during inference, use
+    /// `infcx.fn_sig(def_id)`.
+    pub fn closure_sig_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> {
+        self.split(def_id, tcx).closure_sig_ty
+    }
+
+    /// Returns the type representing the yield type of the generator.
+    pub fn generator_yield_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> {
+        self.closure_kind_ty(def_id, tcx)
+    }
+
+    /// Returns the type representing the return type of the generator.
+    pub fn generator_return_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> {
+        self.closure_sig_ty(def_id, tcx)
+    }
+
+    /// Return the "generator signature", which consists of its yield
+    /// and return types.
+    ///
+    /// NB. Some bits of the code prefers to see this wrapped in a
+    /// binder, but it never contains bound regions. Probably this
+    /// function should be removed.
+    pub fn generator_poly_sig(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> PolyGenSig<'tcx> {
+        ty::Binder(self.generator_sig(def_id, tcx))
+    }
+
+    /// Return the "generator signature", which consists of its yield
+    /// and return types.
+    pub fn generator_sig(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> GenSig<'tcx> {
+        ty::GenSig {
+            yield_ty: self.generator_yield_ty(def_id, tcx),
+            return_ty: self.generator_return_ty(def_id, tcx),
+        }
+    }
+}
+
+impl<'tcx> ClosureSubsts<'tcx> {
+    /// Returns the closure kind for this closure; only usable outside
+    /// of an inference context, because in that context we know that
+    /// there are no type variables.
+    pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::ClosureKind {
+        self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap()
+    }
+
+    /// Extracts the signature from the closure; only usable outside
+    /// of an inference context, because in that context we know that
+    /// there are no type variables.
+    pub fn closure_sig(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::PolyFnSig<'tcx> {
+        match self.closure_sig_ty(def_id, tcx).sty {
+            ty::TyFnPtr(sig) => sig,
+            ref t => bug!("closure_sig_ty is not a fn-ptr: {:?}", t),
+        }
     }
 }
 
@@ -1268,6 +1372,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    pub fn is_generator(&self) -> bool {
+        match self.sty {
+            TyGenerator(..) => true,
+            _ => false,
+        }
+    }
+
     pub fn is_integral(&self) -> bool {
         match self.sty {
             TyInfer(IntVar(_)) | TyInt(_) | TyUint(_) => true,
@@ -1442,6 +1553,37 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
             }
         }
     }
+
+    /// When we create a closure, we record its kind (i.e., what trait
+    /// it implements) into its `ClosureSubsts` using a type
+    /// parameter. This is kind of a phantom type, except that the
+    /// most convenient thing for us to are the integral types. This
+    /// function converts such a special type into the closure
+    /// kind. To go the other way, use
+    /// `tcx.closure_kind_ty(closure_kind)`.
+    ///
+    /// Note that during type checking, we use an inference variable
+    /// to represent the closure kind, because it has not yet been
+    /// inferred. Once [upvar inference] is complete, that type varibale
+    /// will be unified.
+    ///
+    /// [upvar inference]: src/librustc_typeck/check/upvar.rs
+    pub fn to_opt_closure_kind(&self) -> Option<ty::ClosureKind> {
+        match self.sty {
+            TyInt(int_ty) => match int_ty {
+                ast::IntTy::I8 => Some(ty::ClosureKind::Fn),
+                ast::IntTy::I16 => Some(ty::ClosureKind::FnMut),
+                ast::IntTy::I32 => Some(ty::ClosureKind::FnOnce),
+                _ => bug!("cannot convert type `{:?}` to a closure kind", self),
+            },
+
+            TyInfer(_) => None,
+
+            TyError => Some(ty::ClosureKind::Fn),
+
+            _ => bug!("cannot convert type `{:?}` to a closure kind", self),
+        }
+    }
 }
 
 /// Typed constant value.
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index c631e2c4db5..a851ccc34bf 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -336,14 +336,50 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
                     }
                 }
 
-                ty::TyGenerator(..) | ty::TyClosure(..) => {
-                    // the types in a closure or generator are always the types of
-                    // local variables (or possibly references to local
-                    // variables), we'll walk those.
+                ty::TyGenerator(..) => {
+                    // Walk ALL the types in the generator: this will
+                    // include the upvar types as well as the yield
+                    // type. Note that this is mildly distinct from
+                    // the closure case, where we have to be careful
+                    // about the signature of the closure. We don't
+                    // have the problem of implied bounds here since
+                    // generators don't take arguments.
+                }
+
+                ty::TyClosure(def_id, substs) => {
+                    // Only check the upvar types for WF, not the rest
+                    // of the types within. This is needed because we
+                    // capture the signature and it may not be WF
+                    // without the implied bounds. Consider a closure
+                    // like `|x: &'a T|` -- it may be that `T: 'a` is
+                    // not known to hold in the creator's context (and
+                    // indeed the closure may not be invoked by its
+                    // creator, but rather turned to someone who *can*
+                    // verify that).
+                    //
+                    // The special treatment of closures here really
+                    // ought not to be necessary either; the problem
+                    // is related to #25860 -- there is no way for us
+                    // to express a fn type complete with the implied
+                    // bounds that it is assuming. I think in reality
+                    // the WF rules around fn are a bit messed up, and
+                    // that is the rot problem: `fn(&'a T)` should
+                    // probably always be WF, because it should be
+                    // shorthand for something like `where(T: 'a) {
+                    // fn(&'a T) }`, as discussed in #25860.
                     //
-                    // (Though, local variables are probably not
-                    // needed, as they are separately checked w/r/t
-                    // WFedness.)
+                    // Note that we are also skipping the generic
+                    // types. This is consistent with the `outlives`
+                    // code, but anyway doesn't matter: within the fn
+                    // body where they are created, the generics will
+                    // always be WF, and outside of that fn body we
+                    // are not directly inspecting closure types
+                    // anyway, except via auto trait matching (which
+                    // only inspects the upvar types).
+                    subtys.skip_current_subtree(); // subtree handled by compute_projection
+                    for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
+                        self.compute(upvar_ty);
+                    }
                 }
 
                 ty::TyFnDef(..) | ty::TyFnPtr(_) => {
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 4b0cf018cb5..2c3a32b2d15 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -1261,7 +1261,7 @@ define_print! {
                     ty::tls::with(|tcx| {
                         write!(f, "the trait `{}` is object-safe", tcx.item_path_str(trait_def_id))
                     }),
-                ty::Predicate::ClosureKind(closure_def_id, kind) =>
+                ty::Predicate::ClosureKind(closure_def_id, _closure_substs, kind) =>
                     ty::tls::with(|tcx| {
                         write!(f, "the closure `{}` implements the trait `{}`",
                                tcx.item_path_str(closure_def_id), kind)
@@ -1285,8 +1285,8 @@ define_print! {
                 ty::Predicate::ObjectSafe(trait_def_id) => {
                     write!(f, "ObjectSafe({:?})", trait_def_id)
                 }
-                ty::Predicate::ClosureKind(closure_def_id, kind) => {
-                    write!(f, "ClosureKind({:?}, {:?})", closure_def_id, kind)
+                ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
+                    write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind)
                 }
                 ty::Predicate::ConstEvaluatable(def_id, substs) => {
                     write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs)
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 7b09e45fe96..36b397bbbe5 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -655,10 +655,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                     ty::TypeVariants::TyClosure(id, _) => {
                         let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
                         let hir_id = self.tcx.hir.node_to_hir_id(node_id);
-                        if let Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) =
-                            self.tables.closure_kinds().get(hir_id)
-                        {
-                            err.span_note(span, &format!(
+                        if let Some((span, name)) = self.tables.closure_kind_origins().get(hir_id) {
+                            err.span_note(*span, &format!(
                                 "closure cannot be invoked more than once because \
                                 it moves the variable `{}` out of its environment",
                                 name
diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs
index 78aacd49f80..c8b71be86f8 100644
--- a/src/librustc_borrowck/lib.rs
+++ b/src/librustc_borrowck/lib.rs
@@ -15,6 +15,7 @@
 
 #![allow(non_camel_case_types)]
 
+#![feature(match_default_bindings)]
 #![feature(quote)]
 
 #[macro_use] extern crate log;
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index b26ebfd6121..1f671adf4f8 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -136,12 +136,10 @@ provide! { <'tcx> tcx, def_id, other, cdata,
 
         mir
     }
-    generator_sig => { cdata.generator_sig(def_id.index, tcx) }
     mir_const_qualif => {
         (cdata.mir_const_qualif(def_id.index), Rc::new(IdxSetBuf::new_empty(0)))
     }
     typeck_tables_of => { cdata.item_body_tables(def_id.index, tcx) }
-    closure_kind => { cdata.closure_kind(def_id.index) }
     fn_sig => { cdata.fn_sig(def_id.index, tcx) }
     inherent_impls => { Rc::new(cdata.get_inherent_implementations_for_type(def_id.index)) }
     is_const_fn => { cdata.is_const_fn(def_id.index) }
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 0dd1b9e500c..633806d5ef5 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -1020,13 +1020,6 @@ impl<'a, 'tcx> CrateMetadata {
         }
     }
 
-    pub fn closure_kind(&self, closure_id: DefIndex) -> ty::ClosureKind {
-        match self.entry(closure_id).kind {
-            EntryKind::Closure(data) => data.decode(self).kind,
-            _ => bug!(),
-        }
-    }
-
     pub fn fn_sig(&self,
                   id: DefIndex,
                   tcx: TyCtxt<'a, 'tcx, 'tcx>)
@@ -1043,23 +1036,6 @@ impl<'a, 'tcx> CrateMetadata {
         sig.decode((self, tcx))
     }
 
-    fn get_generator_data(&self,
-                      id: DefIndex,
-                      tcx: TyCtxt<'a, 'tcx, 'tcx>)
-                      -> Option<GeneratorData<'tcx>> {
-        match self.entry(id).kind {
-            EntryKind::Generator(data) => Some(data.decode((self, tcx))),
-            _ => None,
-        }
-    }
-
-    pub fn generator_sig(&self,
-                      id: DefIndex,
-                      tcx: TyCtxt<'a, 'tcx, 'tcx>)
-                      -> Option<ty::PolyGenSig<'tcx>> {
-        self.get_generator_data(id, tcx).map(|d| d.sig)
-    }
-
     #[inline]
     pub fn def_key(&self, index: DefIndex) -> DefKey {
         self.def_path_table.def_key(index)
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 7e5d690cc78..d82d50164cb 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -1205,19 +1205,25 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
         debug!("IsolatedEncoder::encode_info_for_closure({:?})", def_id);
         let tcx = self.tcx;
 
-        let kind = if let Some(sig) = self.tcx.generator_sig(def_id) {
-            let layout = self.tcx.generator_layout(def_id);
-            let data = GeneratorData {
-                sig,
-                layout: layout.clone(),
-            };
-            EntryKind::Generator(self.lazy(&data))
-        } else {
-            let data = ClosureData {
-                kind: tcx.closure_kind(def_id),
-                sig: self.lazy(&tcx.fn_sig(def_id)),
-            };
-            EntryKind::Closure(self.lazy(&data))
+        let tables = self.tcx.typeck_tables_of(def_id);
+        let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
+        let hir_id = self.tcx.hir.node_to_hir_id(node_id);
+        let kind = match tables.node_id_to_type(hir_id).sty {
+            ty::TyGenerator(def_id, ..) => {
+                let layout = self.tcx.generator_layout(def_id);
+                let data = GeneratorData {
+                    layout: layout.clone(),
+                };
+                EntryKind::Generator(self.lazy(&data))
+            }
+
+            ty::TyClosure(def_id, substs) => {
+                let sig = substs.closure_sig(def_id, self.tcx);
+                let data = ClosureData { sig: self.lazy(&sig) };
+                EntryKind::Closure(self.lazy(&data))
+            }
+
+            _ => bug!("closure that is neither generator nor closure")
         };
 
         Entry {
diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs
index 3efe74bfecc..8ff32746391 100644
--- a/src/librustc_metadata/schema.rs
+++ b/src/librustc_metadata/schema.rs
@@ -512,14 +512,12 @@ impl_stable_hash_for!(struct MethodData<'tcx> { fn_data, container, has_self });
 
 #[derive(RustcEncodable, RustcDecodable)]
 pub struct ClosureData<'tcx> {
-    pub kind: ty::ClosureKind,
     pub sig: Lazy<ty::PolyFnSig<'tcx>>,
 }
-impl_stable_hash_for!(struct ClosureData<'tcx> { kind, sig });
+impl_stable_hash_for!(struct ClosureData<'tcx> { sig });
 
 #[derive(RustcEncodable, RustcDecodable)]
 pub struct GeneratorData<'tcx> {
-    pub sig: ty::PolyGenSig<'tcx>,
     pub layout: mir::GeneratorLayout<'tcx>,
 }
-impl_stable_hash_for!(struct GeneratorData<'tcx> { sig, layout });
+impl_stable_hash_for!(struct GeneratorData<'tcx> { layout });
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 287d1083189..080cf4b47cf 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -103,7 +103,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
                     Some((closure_self_ty(tcx, id, body_id), None))
                 }
                 ty::TyGenerator(..) => {
-                    let gen_ty =  tcx.body_tables(body_id).node_id_to_type(fn_hir_id);
+                    let gen_ty = tcx.body_tables(body_id).node_id_to_type(fn_hir_id);
                     Some((gen_ty, None))
                 }
                 _ => None,
@@ -127,7 +127,12 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
             let arguments = implicit_argument.into_iter().chain(explicit_arguments);
 
             let (yield_ty, return_ty) = if body.is_generator {
-                let gen_sig = cx.tables().generator_sigs()[fn_hir_id].clone().unwrap();
+                let gen_sig = match ty.sty {
+                    ty::TyGenerator(gen_def_id, gen_substs, ..) =>
+                        gen_substs.generator_sig(gen_def_id, tcx),
+                    _ =>
+                        span_bug!(tcx.hir.span(id), "generator w/o generator type: {:?}", ty),
+                };
                 (Some(gen_sig.yield_ty), gen_sig.return_ty)
             } else {
                 (None, fn_sig.output())
@@ -248,14 +253,18 @@ pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id);
     let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id);
 
-    let closure_def_id = tcx.hir.local_def_id(closure_expr_id);
+    let (closure_def_id, closure_substs) = match closure_ty.sty {
+        ty::TyClosure(closure_def_id, closure_substs) => (closure_def_id, closure_substs),
+        _ => bug!("closure expr does not have closure type: {:?}", closure_ty)
+    };
+
     let region = ty::ReFree(ty::FreeRegion {
         scope: closure_def_id,
         bound_region: ty::BoundRegion::BrEnv,
     });
     let region = tcx.mk_region(region);
 
-    match tcx.closure_kind(closure_def_id) {
+    match closure_substs.closure_kind_ty(closure_def_id, tcx).to_opt_closure_kind().unwrap() {
         ty::ClosureKind::Fn =>
             tcx.mk_ref(region,
                        ty::TypeAndMut { ty: closure_ty,
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 798928e7ae7..00d7cdc0ff7 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -713,8 +713,8 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             });
             let region = cx.tcx.mk_region(region);
 
-            let self_expr = if let ty::TyClosure(..) = closure_ty.sty {
-                match cx.tcx.closure_kind(closure_def_id) {
+            let self_expr = if let ty::TyClosure(_, closure_substs) = closure_ty.sty {
+                match cx.infcx.closure_kind(closure_def_id, closure_substs).unwrap() {
                     ty::ClosureKind::Fn => {
                         let ref_closure_ty = cx.tcx.mk_ref(region,
                                                            ty::TypeAndMut {
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index f676372193a..43635bcb631 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -767,7 +767,11 @@ impl MirPass for StateTransform {
         let hir_id = tcx.hir.node_to_hir_id(node_id);
 
         // Get the interior types which typeck computed
-        let interior = *tcx.typeck_tables_of(def_id).generator_interiors().get(hir_id).unwrap();
+        let tables = tcx.typeck_tables_of(def_id);
+        let interior = match tables.node_id_to_type(hir_id).sty {
+            ty::TyGenerator(_, _, interior) => interior,
+            ref t => bug!("type of generator not a generator: {:?}", t),
+        };
 
         // The first argument is the generator type passed by value
         let gen_ty = mir.local_decls.raw[1].ty;
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index 4b7856f857b..35cade25f77 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -23,6 +23,7 @@ use rustc::ty::layout::LayoutOf;
 use rustc::ty::subst::{Subst,Substs};
 
 use std::collections::VecDeque;
+use std::iter;
 use transform::{MirPass, MirSource};
 use super::simplify::{remove_dead_blocks, CfgSimplifier};
 
@@ -559,8 +560,29 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
     ) -> Vec<Operand<'tcx>> {
         let tcx = self.tcx;
 
-        // A closure is passed its self-type and a tuple like `(arg1, arg2, ...)`,
-        // hence mappings to tuple fields are needed.
+        // There is a bit of a mismatch between the *caller* of a closure and the *callee*.
+        // The caller provides the arguments wrapped up in a tuple:
+        //
+        //     tuple_tmp = (a, b, c)
+        //     Fn::call(closure_ref, tuple_tmp)
+        //
+        // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`)
+        // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, trans has
+        // the job of unpacking this tuple. But here, we are trans. =) So we want to create
+        // a vector like
+        //
+        //     [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2]
+        //
+        // Except for one tiny wrinkle: we don't actually want `tuple_tmp.0`. It's more convenient
+        // if we "spill" that into *another* temporary, so that we can map the argument
+        // variable in the callee MIR directly to an argument variable on our side.
+        // So we introduce temporaries like:
+        //
+        //     tmp0 = tuple_tmp.0
+        //     tmp1 = tuple_tmp.1
+        //     tmp2 = tuple_tmp.2
+        //
+        // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`.
         if tcx.is_closure(callsite.callee) {
             let mut args = args.into_iter();
             let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir);
@@ -573,12 +595,21 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                 bug!("Closure arguments are not passed as a tuple");
             };
 
-            let mut res = Vec::with_capacity(1 + tuple_tys.len());
-            res.push(Operand::Consume(self_));
-            res.extend(tuple_tys.iter().enumerate().map(|(i, ty)| {
-                Operand::Consume(tuple.clone().field(Field::new(i), ty))
-            }));
-            res
+            // The `closure_ref` in our example above.
+            let closure_ref_arg = iter::once(Operand::Consume(self_));
+
+            // The `tmp0`, `tmp1`, and `tmp2` in our example abonve.
+            let tuple_tmp_args =
+                tuple_tys.iter().enumerate().map(|(i, ty)| {
+                    // This is e.g. `tuple_tmp.0` in our example above.
+                    let tuple_field = Operand::Consume(tuple.clone().field(Field::new(i), ty));
+
+                    // Spill to a local to make e.g. `tmp0`.
+                    let tmp = self.create_temp_if_necessary(tuple_field, callsite, caller_mir);
+                    Operand::Consume(tmp)
+                });
+
+            closure_ref_arg.chain(tuple_tmp_args).collect()
         } else {
             args.into_iter()
                 .map(|a| Operand::Consume(self.create_temp_if_necessary(a, callsite, caller_mir)))
diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs
index 7bd8a0c81ee..1f92c106784 100644
--- a/src/librustc_trans/common.rs
+++ b/src/librustc_trans/common.rs
@@ -396,7 +396,7 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             let sig = tcx.fn_sig(def_id).subst(tcx, substs.substs);
 
             let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
-            let env_ty = match tcx.closure_kind(def_id) {
+            let env_ty = match substs.closure_kind(def_id, tcx) {
                 ty::ClosureKind::Fn => tcx.mk_imm_ref(tcx.mk_region(env_region), ty),
                 ty::ClosureKind::FnMut => tcx.mk_mut_ref(tcx.mk_region(env_region), ty),
                 ty::ClosureKind::FnOnce => ty,
@@ -412,7 +412,7 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         }
         ty::TyGenerator(def_id, substs, _) => {
             let tcx = ccx.tcx();
-            let sig = tcx.generator_sig(def_id).unwrap().subst(tcx, substs.substs);
+            let sig = substs.generator_poly_sig(def_id, ccx.tcx());
 
             let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
             let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
diff --git a/src/librustc_trans_utils/monomorphize.rs b/src/librustc_trans_utils/monomorphize.rs
index eee5c1d9ef2..66833a1a7c2 100644
--- a/src/librustc_trans_utils/monomorphize.rs
+++ b/src/librustc_trans_utils/monomorphize.rs
@@ -86,7 +86,7 @@ pub fn resolve_closure<'a, 'tcx> (
     requested_kind: ty::ClosureKind)
     -> Instance<'tcx>
 {
-    let actual_kind = tcx.closure_kind(def_id);
+    let actual_kind = substs.closure_kind(def_id, tcx);
 
     match needs_fn_once_adapter_shim(actual_kind, requested_kind) {
         Ok(true) => fn_once_adapter_instance(tcx, def_id, substs),
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 91ce4511a31..8f409b68752 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -108,7 +108,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // 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 self.closure_kind(def_id).is_none() {
+                if self.closure_kind(def_id, substs).is_none() {
                     let closure_ty = self.fn_sig(def_id).subst(self.tcx, substs.substs);
                     let fn_sig = self.replace_late_bound_regions_with_fresh_var(call_expr.span,
                                                                    infer::FnCall,
@@ -122,6 +122,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         adjustments,
                         fn_sig,
                         closure_def_id: def_id,
+                        closure_substs: substs,
                     });
                     return Some(CallStep::DeferredClosure(fn_sig));
                 }
@@ -336,6 +337,7 @@ pub struct DeferredCallResolution<'gcx: 'tcx, 'tcx> {
     adjustments: Vec<Adjustment<'tcx>>,
     fn_sig: ty::FnSig<'tcx>,
     closure_def_id: DefId,
+    closure_substs: ty::ClosureSubsts<'tcx>,
 }
 
 impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> {
@@ -344,7 +346,7 @@ impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, '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());
+        assert!(fcx.closure_kind(self.closure_def_id, self.closure_substs).is_some());
 
         // We may now know enough to figure out fn vs fnmut etc.
         match fcx.try_overloaded_call_traits(self.call_expr,
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index d475fb0cf1a..5b5d697bcf4 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -10,7 +10,7 @@
 
 //! Code for type-checking closure expressions.
 
-use super::{check_fn, Expectation, FnCtxt};
+use super::{check_fn, Expectation, FnCtxt, GeneratorTypes};
 
 use astconv::AstConv;
 use rustc::hir::def_id::DefId;
@@ -79,7 +79,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         debug!("check_closure: ty_of_closure returns {:?}", liberated_sig);
 
-        let interior = check_fn(
+        let generator_types = check_fn(
             self,
             self.param_env,
             liberated_sig,
@@ -100,14 +100,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             |_, _| span_bug!(expr.span, "closure has region param"),
             |_, _| {
                 self.infcx
-                    .next_ty_var(TypeVariableOrigin::TransformedUpvar(expr.span))
+                    .next_ty_var(TypeVariableOrigin::ClosureSynthetic(expr.span))
             },
         );
+        let substs = ty::ClosureSubsts { substs };
         let closure_type = self.tcx.mk_closure(expr_def_id, substs);
 
-        if let Some(interior) = interior {
-            let closure_substs = ty::ClosureSubsts { substs: substs };
-            return self.tcx.mk_generator(expr_def_id, closure_substs, interior);
+        if let Some(GeneratorTypes { yield_ty, interior }) = generator_types {
+            self.demand_eqtype(expr.span,
+                               yield_ty,
+                               substs.generator_yield_ty(expr_def_id, self.tcx));
+            self.demand_eqtype(expr.span,
+                               liberated_sig.output(),
+                               substs.generator_return_ty(expr_def_id, self.tcx));
+            return self.tcx.mk_generator(expr_def_id, substs, interior);
         }
 
         debug!(
@@ -135,15 +141,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             opt_kind
         );
 
-        {
-            let mut tables = self.tables.borrow_mut();
-            tables.closure_tys_mut().insert(expr.hir_id, sig);
-            match opt_kind {
-                Some(kind) => {
-                    tables.closure_kinds_mut().insert(expr.hir_id, (kind, None));
-                }
-                None => {}
-            }
+        let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig);
+        self.demand_eqtype(expr.span,
+                           sig_fn_ptr_ty,
+                           substs.closure_sig_ty(expr_def_id, self.tcx));
+
+        if let Some(kind) = opt_kind {
+            self.demand_eqtype(expr.span,
+                               kind.to_ty(self.tcx),
+                               substs.closure_kind_ty(expr_def_id, self.tcx));
         }
 
         closure_type
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 0cb1408a450..09dd334a62d 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -759,30 +759,12 @@ pub fn provide(providers: &mut Providers) {
         typeck_item_bodies,
         typeck_tables_of,
         has_typeck_tables,
-        closure_kind,
-        generator_sig,
         adt_destructor,
         used_trait_imports,
         ..*providers
     };
 }
 
-fn generator_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          def_id: DefId)
-                          -> Option<ty::PolyGenSig<'tcx>> {
-    let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
-    let hir_id = tcx.hir.node_to_hir_id(node_id);
-    tcx.typeck_tables_of(def_id).generator_sigs()[hir_id].map(|s| ty::Binder(s))
-}
-
-fn closure_kind<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          def_id: DefId)
-                          -> ty::ClosureKind {
-    let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
-    let hir_id = tcx.hir.node_to_hir_id(node_id);
-    tcx.typeck_tables_of(def_id).closure_kinds()[hir_id].0
-}
-
 fn adt_destructor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             def_id: DefId)
                             -> Option<ty::Destructor> {
@@ -1021,6 +1003,17 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
                 _: hir::BodyId, _: Span, _: ast::NodeId) { }
 }
 
+/// When `check_fn` is invoked on a generator (i.e., a body that
+/// includes yield), it returns back some information about the yield
+/// points.
+struct GeneratorTypes<'tcx> {
+    /// Type of value that is yielded.
+    yield_ty: ty::Ty<'tcx>,
+
+    /// Types that are captured (see `GeneratorInterior` for more).
+    interior: ty::GeneratorInterior<'tcx>
+}
+
 /// Helper used for fns and closures. Does the grungy work of checking a function
 /// body and returns the function context used for that purpose, since in the case of a fn item
 /// there is still a bit more to do.
@@ -1034,7 +1027,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
                             fn_id: ast::NodeId,
                             body: &'gcx hir::Body,
                             can_be_generator: bool)
-                            -> (FnCtxt<'a, 'gcx, 'tcx>, Option<ty::GeneratorInterior<'tcx>>)
+                            -> (FnCtxt<'a, 'gcx, 'tcx>, Option<GeneratorTypes<'tcx>>)
 {
     let mut fn_sig = fn_sig.clone();
 
@@ -1084,21 +1077,11 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
 
     let fn_hir_id = fcx.tcx.hir.node_to_hir_id(fn_id);
     let gen_ty = if can_be_generator && body.is_generator {
-        let gen_sig = ty::GenSig {
-            yield_ty: fcx.yield_ty.unwrap(),
-            return_ty: ret_ty,
-        };
-        inherited.tables.borrow_mut().generator_sigs_mut().insert(fn_hir_id, Some(gen_sig));
-
         let witness = fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span));
         fcx.deferred_generator_interiors.borrow_mut().push((body.id(), witness));
         let interior = ty::GeneratorInterior::new(witness);
-
-        inherited.tables.borrow_mut().generator_interiors_mut().insert(fn_hir_id, interior);
-
-        Some(interior)
+        Some(GeneratorTypes { yield_ty: fcx.yield_ty.unwrap(), interior: interior })
     } else {
-        inherited.tables.borrow_mut().generator_sigs_mut().insert(fn_hir_id, None);
         None
     };
     inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_hir_id, fn_sig);
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 9a70a8699aa..2e0d0ddfc39 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -45,16 +45,14 @@ use super::FnCtxt;
 use middle::expr_use_visitor as euv;
 use middle::mem_categorization as mc;
 use middle::mem_categorization::Categorization;
+use rustc::hir::def_id::DefId;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::infer::UpvarRegion;
 use syntax::ast;
 use syntax_pos::Span;
 use rustc::hir;
 use rustc::hir::def_id::LocalDefId;
-use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
-use rustc::util::nodemap::FxHashMap;
-
-use std::collections::hash_map::Entry;
+use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn closure_analyze(&self, body: &'gcx hir::Body) {
@@ -65,7 +63,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     }
 }
 
-struct InferBorrowKindVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+struct InferBorrowKindVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
 }
 
@@ -79,14 +77,11 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for InferBorrowKindVisitor<'a, 'gcx, 'tcx> {
             hir::ExprClosure(cc, _, body_id, _, is_generator) => {
                 let body = self.fcx.tcx.hir.body(body_id);
                 self.visit_body(body);
-                self.fcx.analyze_closure((expr.id, expr.hir_id),
-                                         expr.span,
-                                         body,
-                                         cc,
-                                         is_generator);
+                self.fcx
+                    .analyze_closure(expr.id, expr.hir_id, expr.span, body, cc, is_generator);
             }
 
-            _ => { }
+            _ => {}
         }
 
         intravisit::walk_expr(self, expr);
@@ -94,35 +89,43 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for InferBorrowKindVisitor<'a, 'gcx, 'tcx> {
 }
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
-    fn analyze_closure(&self,
-                       (closure_node_id, closure_hir_id): (ast::NodeId, hir::HirId),
-                       span: Span,
-                       body: &hir::Body,
-                       capture_clause: hir::CaptureClause,
-                       gen: bool) {
+    fn analyze_closure(
+        &self,
+        closure_node_id: ast::NodeId,
+        closure_hir_id: hir::HirId,
+        span: Span,
+        body: &hir::Body,
+        capture_clause: hir::CaptureClause,
+        is_generator: bool,
+    ) {
         /*!
          * Analysis starting point.
          */
 
-        debug!("analyze_closure(id={:?}, body.id={:?})", closure_node_id, body.id());
+        debug!(
+            "analyze_closure(id={:?}, body.id={:?})",
+            closure_node_id,
+            body.id()
+        );
 
-        let infer_kind = if gen {
-            false
-        } else {
-            match self.tables
-                      .borrow_mut()
-                      .closure_kinds_mut()
-                      .entry(closure_hir_id) {
-                Entry::Occupied(_) => false,
-                Entry::Vacant(entry) => {
-                    debug!("check_closure: adding closure {:?} as Fn", closure_node_id);
-                    entry.insert((ty::ClosureKind::Fn, None));
-                    true
-                }
+        // Extract the type of the closure.
+        let (closure_def_id, closure_substs) = match self.node_ty(closure_hir_id).sty {
+            ty::TyClosure(def_id, substs) | ty::TyGenerator(def_id, substs, _) => (def_id, substs),
+            ref t => {
+                span_bug!(
+                    span,
+                    "type of closure expr {:?} is not a closure {:?}",
+                    closure_node_id,
+                    t
+                );
             }
         };
 
-        let closure_def_id = self.tcx.hir.local_def_id(closure_node_id);
+        let infer_kind = if is_generator {
+            false
+        } else {
+            self.closure_kind(closure_def_id, closure_substs).is_none()
+        };
 
         self.tcx.with_freevars(closure_node_id, |freevars| {
             for freevar in freevars {
@@ -133,51 +136,63 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 debug!("seed upvar_id {:?}", upvar_id);
 
                 let capture_kind = match capture_clause {
-                    hir::CaptureByValue => {
-                        ty::UpvarCapture::ByValue
-                    }
+                    hir::CaptureByValue => ty::UpvarCapture::ByValue,
                     hir::CaptureByRef => {
                         let origin = UpvarRegion(upvar_id, span);
                         let freevar_region = self.next_region_var(origin);
-                        let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
-                                                             region: freevar_region };
+                        let upvar_borrow = ty::UpvarBorrow {
+                            kind: ty::ImmBorrow,
+                            region: freevar_region,
+                        };
                         ty::UpvarCapture::ByRef(upvar_borrow)
                     }
                 };
 
-                self.tables.borrow_mut().upvar_capture_map.insert(upvar_id, capture_kind);
+                self.tables
+                    .borrow_mut()
+                    .upvar_capture_map
+                    .insert(upvar_id, capture_kind);
             }
         });
 
-        {
-            let body_owner_def_id = self.tcx.hir.body_owner_def_id(body.id());
-            let region_scope_tree = &self.tcx.region_scope_tree(body_owner_def_id);
-            let mut delegate = InferBorrowKind {
-                fcx: self,
-                adjust_closure_kinds: FxHashMap(),
-                adjust_upvar_captures: ty::UpvarCaptureMap::default(),
-            };
-            euv::ExprUseVisitor::with_infer(&mut delegate,
-                                            &self.infcx,
-                                            self.param_env,
-                                            region_scope_tree,
-                                            &self.tables.borrow())
-                .consume_body(body);
-
-            // Write the adjusted values back into the main tables.
-            if infer_kind {
-                if let Some(kind) = delegate.adjust_closure_kinds
-                                            .remove(&closure_def_id.to_local()) {
-                    self.tables
-                        .borrow_mut()
-                        .closure_kinds_mut()
-                        .insert(closure_hir_id, kind);
-                }
+        let body_owner_def_id = self.tcx.hir.body_owner_def_id(body.id());
+        let region_scope_tree = &self.tcx.region_scope_tree(body_owner_def_id);
+        let mut delegate = InferBorrowKind {
+            fcx: self,
+            closure_def_id: closure_def_id,
+            current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
+            current_origin: None,
+            adjust_upvar_captures: ty::UpvarCaptureMap::default(),
+        };
+        euv::ExprUseVisitor::with_infer(
+            &mut delegate,
+            &self.infcx,
+            self.param_env,
+            region_scope_tree,
+            &self.tables.borrow(),
+        ).consume_body(body);
+
+        if infer_kind {
+            // Unify the (as yet unbound) type variable in the closure
+            // substs with the kind we inferred.
+            let inferred_kind = delegate.current_closure_kind;
+            let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx);
+            self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty);
+
+            // If we have an origin, store it.
+            if let Some(origin) = delegate.current_origin {
+                self.tables
+                    .borrow_mut()
+                    .closure_kind_origins_mut()
+                    .insert(closure_hir_id, origin);
             }
-            self.tables.borrow_mut().upvar_capture_map.extend(
-                delegate.adjust_upvar_captures);
         }
 
+        self.tables
+            .borrow_mut()
+            .upvar_capture_map
+            .extend(delegate.adjust_upvar_captures);
+
         // Now that we've analyzed the closure, we know how each
         // variable is borrowed, and we know what traits the closure
         // implements (Fn vs FnMut etc). We now have some updates to do
@@ -190,36 +205,26 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // C, then the type would have infinite size (and the
         // inference algorithm will reject it).
 
-        // Extract the type variables UV0...UVn.
-        let (def_id, closure_substs) = match self.node_ty(closure_hir_id).sty {
-            ty::TyClosure(def_id, substs) |
-            ty::TyGenerator(def_id, substs, _) => (def_id, substs),
-            ref t => {
-                span_bug!(
-                    span,
-                    "type of closure expr {:?} is not a closure {:?}",
-                    closure_node_id, t);
-            }
-        };
-
-        // Equate the type variables with the actual types.
+        // Equate the type variables for the upvars with the actual types.
         let final_upvar_tys = self.final_upvar_tys(closure_node_id);
-        debug!("analyze_closure: id={:?} closure_substs={:?} final_upvar_tys={:?}",
-               closure_node_id, closure_substs, final_upvar_tys);
-        for (upvar_ty, final_upvar_ty) in
-            closure_substs.upvar_tys(def_id, self.tcx).zip(final_upvar_tys)
+        debug!(
+            "analyze_closure: id={:?} closure_substs={:?} final_upvar_tys={:?}",
+            closure_node_id,
+            closure_substs,
+            final_upvar_tys
+        );
+        for (upvar_ty, final_upvar_ty) in closure_substs
+            .upvar_tys(closure_def_id, self.tcx)
+            .zip(final_upvar_tys)
         {
             self.demand_eqtype(span, final_upvar_ty, upvar_ty);
         }
 
         // If we are also inferred the closure kind here,
         // process any deferred resolutions.
-        if infer_kind {
-            let deferred_call_resolutions =
-                self.remove_deferred_call_resolutions(closure_def_id);
-            for deferred_call_resolution in deferred_call_resolutions {
-                deferred_call_resolution.resolve(self);
-            }
+        let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id);
+        for deferred_call_resolution in deferred_call_resolutions {
+            deferred_call_resolution.resolve(self);
         }
     }
 
@@ -234,51 +239,78 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let closure_def_index = tcx.hir.local_def_id(closure_id);
 
         tcx.with_freevars(closure_id, |freevars| {
-            freevars.iter().map(|freevar| {
-                let var_node_id = freevar.var_id();
-                let var_hir_id = tcx.hir.node_to_hir_id(var_node_id);
-                let freevar_ty = self.node_ty(var_hir_id);
-                let upvar_id = ty::UpvarId {
-                    var_id: var_hir_id,
-                    closure_expr_id: LocalDefId::from_def_id(closure_def_index),
-                };
-                let capture = self.tables.borrow().upvar_capture(upvar_id);
-
-                debug!("var_id={:?} freevar_ty={:?} capture={:?}",
-                       var_node_id, freevar_ty, capture);
-
-                match capture {
-                    ty::UpvarCapture::ByValue => freevar_ty,
-                    ty::UpvarCapture::ByRef(borrow) =>
-                        tcx.mk_ref(borrow.region,
-                                    ty::TypeAndMut {
-                                        ty: freevar_ty,
-                                        mutbl: borrow.kind.to_mutbl_lossy(),
-                                    }),
-                }
-            }).collect()
+            freevars
+                .iter()
+                .map(|freevar| {
+                    let var_node_id = freevar.var_id();
+                    let var_hir_id = tcx.hir.node_to_hir_id(var_node_id);
+                    let freevar_ty = self.node_ty(var_hir_id);
+                    let upvar_id = ty::UpvarId {
+                        var_id: var_hir_id,
+                        closure_expr_id: LocalDefId::from_def_id(closure_def_index),
+                    };
+                    let capture = self.tables.borrow().upvar_capture(upvar_id);
+
+                    debug!(
+                        "var_id={:?} freevar_ty={:?} capture={:?}",
+                        var_node_id,
+                        freevar_ty,
+                        capture
+                    );
+
+                    match capture {
+                        ty::UpvarCapture::ByValue => freevar_ty,
+                        ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref(
+                            borrow.region,
+                            ty::TypeAndMut {
+                                ty: freevar_ty,
+                                mutbl: borrow.kind.to_mutbl_lossy(),
+                            },
+                        ),
+                    }
+                })
+                .collect()
         })
     }
 }
 
-struct InferBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+struct InferBorrowKind<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
-    adjust_closure_kinds: FxHashMap<LocalDefId, (ty::ClosureKind, Option<(Span, ast::Name)>)>,
+
+    // The def-id of the closure whose kind and upvar accesses are being inferred.
+    closure_def_id: DefId,
+
+    // The kind that we have inferred that the current closure
+    // requires. Note that we *always* infer a minimal kind, even if
+    // we don't always *use* that in the final result (i.e., sometimes
+    // we've taken the closure kind from the expectations instead, and
+    // for generators we don't even implement the closure traits
+    // really).
+    current_closure_kind: ty::ClosureKind,
+
+    // If we modified `current_closure_kind`, this field contains a `Some()` with the
+    // variable access that caused us to do so.
+    current_origin: Option<(Span, ast::Name)>,
+
+    // For each upvar that we access, we track the minimal kind of
+    // access we need (ref, ref mut, move, etc).
     adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>,
 }
 
 impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
-    fn adjust_upvar_borrow_kind_for_consume(&mut self,
-                                            cmt: mc::cmt<'tcx>,
-                                            mode: euv::ConsumeMode)
-    {
-        debug!("adjust_upvar_borrow_kind_for_consume(cmt={:?}, mode={:?})",
-               cmt, mode);
+    fn adjust_upvar_borrow_kind_for_consume(&mut self, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
+        debug!(
+            "adjust_upvar_borrow_kind_for_consume(cmt={:?}, mode={:?})",
+            cmt,
+            mode
+        );
 
         // we only care about moves
         match mode {
-            euv::Copy => { return; }
-            euv::Move(_) => { }
+            euv::Copy => {
+                return;
+            }
+            euv::Move(_) => {}
         }
 
         let tcx = self.fcx.tcx;
@@ -287,28 +319,39 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
         // for that to be legal, the upvar would have to be borrowed
         // by value instead
         let guarantor = cmt.guarantor();
-        debug!("adjust_upvar_borrow_kind_for_consume: guarantor={:?}",
-               guarantor);
-        debug!("adjust_upvar_borrow_kind_for_consume: guarantor.cat={:?}",
-               guarantor.cat);
+        debug!(
+            "adjust_upvar_borrow_kind_for_consume: guarantor={:?}",
+            guarantor
+        );
+        debug!(
+            "adjust_upvar_borrow_kind_for_consume: guarantor.cat={:?}",
+            guarantor.cat
+        );
         match guarantor.cat {
             Categorization::Deref(_, mc::BorrowedPtr(..)) |
             Categorization::Deref(_, mc::Implicit(..)) => {
-                debug!("adjust_upvar_borrow_kind_for_consume: found deref with note {:?}",
-                       cmt.note);
+                debug!(
+                    "adjust_upvar_borrow_kind_for_consume: found deref with note {:?}",
+                    cmt.note
+                );
                 match guarantor.note {
                     mc::NoteUpvarRef(upvar_id) => {
-                        debug!("adjust_upvar_borrow_kind_for_consume: \
-                                setting upvar_id={:?} to by value",
-                               upvar_id);
+                        debug!(
+                            "adjust_upvar_borrow_kind_for_consume: \
+                             setting upvar_id={:?} to by value",
+                            upvar_id
+                        );
 
                         // to move out of an upvar, this must be a FnOnce closure
-                        self.adjust_closure_kind(upvar_id.closure_expr_id,
-                                                 ty::ClosureKind::FnOnce,
-                                                 guarantor.span,
-                                                 var_name(tcx, upvar_id.var_id));
-
-                        self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue);
+                        self.adjust_closure_kind(
+                            upvar_id.closure_expr_id,
+                            ty::ClosureKind::FnOnce,
+                            guarantor.span,
+                            var_name(tcx, upvar_id.var_id),
+                        );
+
+                        self.adjust_upvar_captures
+                            .insert(upvar_id, ty::UpvarCapture::ByValue);
                     }
                     mc::NoteClosureEnv(upvar_id) => {
                         // we get just a closureenv ref if this is a
@@ -317,16 +360,17 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
                         // 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::ClosureKind::FnOnce,
-                                                 guarantor.span,
-                                                 var_name(tcx, upvar_id.var_id));
-                    }
-                    mc::NoteNone => {
+                        self.adjust_closure_kind(
+                            upvar_id.closure_expr_id,
+                            ty::ClosureKind::FnOnce,
+                            guarantor.span,
+                            var_name(tcx, upvar_id.var_id),
+                        );
                     }
+                    mc::NoteNone => {}
                 }
             }
-            _ => { }
+            _ => {}
         }
     }
 
@@ -334,8 +378,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
     /// to). If cmt contains any by-ref upvars, this implies that
     /// those upvars must be borrowed using an `&mut` borrow.
     fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) {
-        debug!("adjust_upvar_borrow_kind_for_mut(cmt={:?})",
-               cmt);
+        debug!("adjust_upvar_borrow_kind_for_mut(cmt={:?})", cmt);
 
         match cmt.cat.clone() {
             Categorization::Deref(base, mc::Unique) |
@@ -368,8 +411,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
     }
 
     fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: mc::cmt<'tcx>) {
-        debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})",
-               cmt);
+        debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})", cmt);
 
         match cmt.cat.clone() {
             Categorization::Deref(base, mc::Unique) |
@@ -393,16 +435,11 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
             Categorization::StaticItem |
             Categorization::Rvalue(..) |
             Categorization::Local(_) |
-            Categorization::Upvar(..) => {
-            }
+            Categorization::Upvar(..) => {}
         }
     }
 
-    fn try_adjust_upvar_deref(&mut self,
-                              cmt: mc::cmt<'tcx>,
-                              borrow_kind: ty::BorrowKind)
-                              -> bool
-    {
+    fn try_adjust_upvar_deref(&mut self, cmt: mc::cmt<'tcx>, borrow_kind: ty::BorrowKind) -> bool {
         assert!(match borrow_kind {
             ty::MutBorrow => true,
             ty::UniqueImmBorrow => true,
@@ -422,10 +459,12 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
                 self.adjust_upvar_borrow_kind(upvar_id, 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::ClosureKind::FnMut,
-                                         cmt.span,
-                                         var_name(tcx, upvar_id.var_id));
+                self.adjust_closure_kind(
+                    upvar_id.closure_expr_id,
+                    ty::ClosureKind::FnMut,
+                    cmt.span,
+                    var_name(tcx, upvar_id.var_id),
+                );
 
                 true
             }
@@ -433,16 +472,16 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
                 // 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::ClosureKind::FnMut,
-                                         cmt.span,
-                                         var_name(tcx, upvar_id.var_id));
+                self.adjust_closure_kind(
+                    upvar_id.closure_expr_id,
+                    ty::ClosureKind::FnMut,
+                    cmt.span,
+                    var_name(tcx, upvar_id.var_id),
+                );
 
                 true
             }
-            mc::NoteNone => {
-                false
-            }
+            mc::NoteNone => false,
         }
     }
 
@@ -451,13 +490,17 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
     /// moving from left to right as needed (but never right to left).
     /// Here the argument `mutbl` is the borrow_kind that is required by
     /// some particular use.
-    fn adjust_upvar_borrow_kind(&mut self,
-                                upvar_id: ty::UpvarId,
-                                kind: ty::BorrowKind) {
-        let upvar_capture = self.adjust_upvar_captures.get(&upvar_id).cloned()
+    fn adjust_upvar_borrow_kind(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKind) {
+        let upvar_capture = self.adjust_upvar_captures
+            .get(&upvar_id)
+            .cloned()
             .unwrap_or_else(|| self.fcx.tables.borrow().upvar_capture(upvar_id));
-        debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
-               upvar_id, upvar_capture, kind);
+        debug!(
+            "adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
+            upvar_id,
+            upvar_capture,
+            kind
+        );
 
         match upvar_capture {
             ty::UpvarCapture::ByValue => {
@@ -470,99 +513,107 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
                     (ty::ImmBorrow, ty::MutBorrow) |
                     (ty::UniqueImmBorrow, ty::MutBorrow) => {
                         upvar_borrow.kind = kind;
-                        self.adjust_upvar_captures.insert(upvar_id,
-                            ty::UpvarCapture::ByRef(upvar_borrow));
+                        self.adjust_upvar_captures
+                            .insert(upvar_id, ty::UpvarCapture::ByRef(upvar_borrow));
                     }
                     // Take LHS:
                     (ty::ImmBorrow, ty::ImmBorrow) |
                     (ty::UniqueImmBorrow, ty::ImmBorrow) |
                     (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
-                    (ty::MutBorrow, _) => {
-                    }
+                    (ty::MutBorrow, _) => {}
                 }
             }
         }
     }
 
-    fn adjust_closure_kind(&mut self,
-                           closure_id: LocalDefId,
-                           new_kind: ty::ClosureKind,
-                           upvar_span: Span,
-                           var_name: ast::Name) {
-        debug!("adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})",
-               closure_id, new_kind, upvar_span, var_name);
-
-        let closure_kind = self.adjust_closure_kinds.get(&closure_id).cloned()
-            .or_else(|| {
-                let closure_id = self.fcx.tcx.hir.local_def_id_to_hir_id(closure_id);
-                self.fcx.tables.borrow().closure_kinds().get(closure_id).cloned()
-            });
-
-        if let Some((existing_kind, _)) = closure_kind {
-            debug!("adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}",
-                   closure_id, existing_kind, new_kind);
-
-            match (existing_kind, new_kind) {
-                (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
-                (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
-                (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
-                (ty::ClosureKind::FnOnce, _) => {
-                    // no change needed
-                }
+    fn adjust_closure_kind(
+        &mut self,
+        closure_id: LocalDefId,
+        new_kind: ty::ClosureKind,
+        upvar_span: Span,
+        var_name: ast::Name,
+    ) {
+        debug!(
+            "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})",
+            closure_id,
+            new_kind,
+            upvar_span,
+            var_name
+        );
+
+        // Is this the closure whose kind is currently being inferred?
+        if closure_id.to_def_id() != self.closure_def_id {
+            debug!("adjust_closure_kind: not current closure");
+            return;
+        }
 
-                (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
-                (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
-                (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
-                    // new kind is stronger than the old kind
-                    self.adjust_closure_kinds.insert(
-                        closure_id,
-                        (new_kind, Some((upvar_span, var_name)))
-                    );
-                }
+        // closures start out as `Fn`.
+        let existing_kind = self.current_closure_kind;
+
+        debug!(
+            "adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}",
+            closure_id,
+            existing_kind,
+            new_kind
+        );
+
+        match (existing_kind, new_kind) {
+            (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
+            (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
+            (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
+            (ty::ClosureKind::FnOnce, _) => {
+                // no change needed
+            }
+
+            (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
+            (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
+            (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
+                // new kind is stronger than the old kind
+                self.current_closure_kind = new_kind;
+                self.current_origin = Some((upvar_span, var_name));
             }
         }
     }
 }
 
 impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'gcx, 'tcx> {
-    fn consume(&mut self,
-               _consume_id: ast::NodeId,
-               _consume_span: Span,
-               cmt: mc::cmt<'tcx>,
-               mode: euv::ConsumeMode)
-    {
+    fn consume(
+        &mut self,
+        _consume_id: ast::NodeId,
+        _consume_span: Span,
+        cmt: mc::cmt<'tcx>,
+        mode: euv::ConsumeMode,
+    ) {
         debug!("consume(cmt={:?},mode={:?})", cmt, mode);
         self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
     }
 
-    fn matched_pat(&mut self,
-                   _matched_pat: &hir::Pat,
-                   _cmt: mc::cmt<'tcx>,
-                   _mode: euv::MatchMode)
-    {}
-
-    fn consume_pat(&mut self,
-                   _consume_pat: &hir::Pat,
-                   cmt: mc::cmt<'tcx>,
-                   mode: euv::ConsumeMode)
-    {
+    fn matched_pat(&mut self, _matched_pat: &hir::Pat, _cmt: mc::cmt<'tcx>, _mode: euv::MatchMode) {
+    }
+
+    fn consume_pat(&mut self, _consume_pat: &hir::Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
         debug!("consume_pat(cmt={:?},mode={:?})", cmt, mode);
         self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
     }
 
-    fn borrow(&mut self,
-              borrow_id: ast::NodeId,
-              _borrow_span: Span,
-              cmt: mc::cmt<'tcx>,
-              _loan_region: ty::Region<'tcx>,
-              bk: ty::BorrowKind,
-              _loan_cause: euv::LoanCause)
-    {
-        debug!("borrow(borrow_id={}, cmt={:?}, bk={:?})",
-               borrow_id, cmt, bk);
+    fn borrow(
+        &mut self,
+        borrow_id: ast::NodeId,
+        _borrow_span: Span,
+        cmt: mc::cmt<'tcx>,
+        _loan_region: ty::Region<'tcx>,
+        bk: ty::BorrowKind,
+        _loan_cause: euv::LoanCause,
+    ) {
+        debug!(
+            "borrow(borrow_id={}, cmt={:?}, bk={:?})",
+            borrow_id,
+            cmt,
+            bk
+        );
 
         match bk {
-            ty::ImmBorrow => { }
+            ty::ImmBorrow => {}
             ty::UniqueImmBorrow => {
                 self.adjust_upvar_borrow_kind_for_unique(cmt);
             }
@@ -572,19 +623,16 @@ impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn decl_without_init(&mut self,
-                         _id: ast::NodeId,
-                         _span: Span)
-    {}
-
-    fn mutate(&mut self,
-              _assignment_id: ast::NodeId,
-              _assignment_span: Span,
-              assignee_cmt: mc::cmt<'tcx>,
-              _mode: euv::MutateMode)
-    {
-        debug!("mutate(assignee_cmt={:?})",
-               assignee_cmt);
+    fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {}
+
+    fn mutate(
+        &mut self,
+        _assignment_id: ast::NodeId,
+        _assignment_span: Span,
+        assignee_cmt: mc::cmt<'tcx>,
+        _mode: euv::MutateMode,
+    ) {
+        debug!("mutate(assignee_cmt={:?})", assignee_cmt);
 
         self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
     }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 6bb5a839950..f287d286656 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -47,8 +47,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         wbcx.visit_anon_types();
         wbcx.visit_cast_types();
         wbcx.visit_free_region_map();
-        wbcx.visit_generator_sigs();
-        wbcx.visit_generator_interiors();
 
         let used_trait_imports = mem::replace(&mut self.tables.borrow_mut().used_trait_imports,
                                               Rc::new(DefIdSet()));
@@ -244,21 +242,12 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
         debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root);
         let common_local_id_root = fcx_tables.local_id_root.unwrap();
 
-        for (&id, closure_ty) in fcx_tables.closure_tys().iter() {
+        for (&id, &origin) in fcx_tables.closure_kind_origins().iter() {
             let hir_id = hir::HirId {
                 owner: common_local_id_root.index,
                 local_id: id,
             };
-            let closure_ty = self.resolve(closure_ty, &hir_id);
-            self.tables.closure_tys_mut().insert(hir_id, closure_ty);
-        }
-
-        for (&id, &closure_kind) in fcx_tables.closure_kinds().iter() {
-            let hir_id = hir::HirId {
-                owner: common_local_id_root.index,
-                local_id: id,
-            };
-            self.tables.closure_kinds_mut().insert(hir_id, closure_kind);
+            self.tables.closure_kind_origins_mut().insert(hir_id, origin);
         }
     }
 
@@ -418,33 +407,6 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn visit_generator_interiors(&mut self) {
-        let common_local_id_root = self.fcx.tables.borrow().local_id_root.unwrap();
-        for (&id, interior) in self.fcx.tables.borrow().generator_interiors().iter() {
-            let hir_id = hir::HirId {
-                owner: common_local_id_root.index,
-                local_id: id,
-            };
-            let interior = self.resolve(interior, &hir_id);
-            self.tables.generator_interiors_mut().insert(hir_id, interior);
-        }
-    }
-
-    fn visit_generator_sigs(&mut self) {
-        let common_local_id_root = self.fcx.tables.borrow().local_id_root.unwrap();
-        for (&id, gen_sig) in self.fcx.tables.borrow().generator_sigs().iter() {
-            let hir_id = hir::HirId {
-                owner: common_local_id_root.index,
-                local_id: id,
-            };
-            let gen_sig = gen_sig.map(|s| ty::GenSig {
-                yield_ty: self.resolve(&s.yield_ty, &hir_id),
-                return_ty: self.resolve(&s.return_ty, &hir_id),
-            });
-            self.tables.generator_sigs_mut().insert(hir_id, gen_sig);
-        }
-    }
-
     fn visit_liberated_fn_sigs(&mut self) {
         let fcx_tables = self.fcx.tables.borrow();
         debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root);
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 916fe3be58c..4c6bad61b19 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1019,9 +1019,31 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // cares about anything but the length is instantiation,
     // and we don't do that for closures.
     if let NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) = node {
+        // add a dummy parameter for the closure kind
+        types.push(ty::TypeParameterDef {
+            index: type_start,
+            name: Symbol::intern("<closure_kind>"),
+            def_id,
+            has_default: false,
+            object_lifetime_default: rl::Set1::Empty,
+            pure_wrt_drop: false,
+            synthetic: None,
+        });
+
+        // add a dummy parameter for the closure signature
+        types.push(ty::TypeParameterDef {
+            index: type_start + 1,
+            name: Symbol::intern("<closure_signature>"),
+            def_id,
+            has_default: false,
+            object_lifetime_default: rl::Set1::Empty,
+            pure_wrt_drop: false,
+            synthetic: None,
+        });
+
         tcx.with_freevars(node_id, |fv| {
-            types.extend(fv.iter().enumerate().map(|(i, _)| ty::TypeParameterDef {
-                index: type_start + i as u32,
+            types.extend(fv.iter().zip(2..).map(|(_, i)| ty::TypeParameterDef {
+                index: type_start + i,
                 name: Symbol::intern("<upvar>"),
                 def_id,
                 has_default: false,
@@ -1156,14 +1178,19 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 return tcx.typeck_tables_of(def_id).node_id_to_type(hir_id);
             }
 
-            tcx.mk_closure(def_id, Substs::for_item(
-                tcx, def_id,
-                |def, _| {
-                    let region = def.to_early_bound_region_data();
-                    tcx.mk_region(ty::ReEarlyBound(region))
-                },
-                |def, _| tcx.mk_param_from_def(def)
-            ))
+            let substs = ty::ClosureSubsts {
+                substs: Substs::for_item(
+                    tcx,
+                    def_id,
+                    |def, _| {
+                        let region = def.to_early_bound_region_data();
+                        tcx.mk_region(ty::ReEarlyBound(region))
+                    },
+                    |def, _| tcx.mk_param_from_def(def)
+                )
+            };
+
+            tcx.mk_closure(def_id, substs)
         }
 
         NodeExpr(_) => match tcx.hir.get(tcx.hir.get_parent_node(node_id)) {
@@ -1242,7 +1269,14 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
 
         NodeExpr(&hir::Expr { node: hir::ExprClosure(..), hir_id, .. }) => {
-            tcx.typeck_tables_of(def_id).closure_tys()[hir_id]
+            let tables = tcx.typeck_tables_of(def_id);
+            match tables.node_id_to_type(hir_id).sty {
+                ty::TyClosure(closure_def_id, closure_substs) => {
+                    assert_eq!(def_id, closure_def_id);
+                    return closure_substs.closure_sig(closure_def_id, tcx);
+                }
+                ref t => bug!("closure with non-closure type: {:?}", t),
+            }
         }
 
         x => {
diff --git a/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs
index 16ed73e9095..513a17e2ef2 100644
--- a/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs
+++ b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs
@@ -13,8 +13,7 @@ fn bar<F>(blk: F) where F: FnOnce() + 'static {
 
 fn foo(x: &()) {
     bar(|| {
-        //~^ ERROR cannot infer
-        //~| ERROR does not fulfill
+        //~^ ERROR does not fulfill
         let _ = x;
     })
 }
diff --git a/src/test/compile-fail/generator-yielding-or-returning-itself.rs b/src/test/compile-fail/generator-yielding-or-returning-itself.rs
new file mode 100644
index 00000000000..13abdf616b2
--- /dev/null
+++ b/src/test/compile-fail/generator-yielding-or-returning-itself.rs
@@ -0,0 +1,45 @@
+// Copyright 2016 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(generator_trait)]
+#![feature(generators)]
+
+// Test that we cannot create a generator that returns a value of its
+// own type.
+
+use std::ops::Generator;
+
+pub fn want_cyclic_generator_return<T>(_: T)
+    where T: Generator<Yield = (), Return = T>
+{
+}
+
+fn supply_cyclic_generator_return() {
+    want_cyclic_generator_return(|| {
+        //~^ ERROR type mismatch
+        if false { yield None.unwrap(); }
+        None.unwrap()
+    })
+}
+
+pub fn want_cyclic_generator_yield<T>(_: T)
+    where T: Generator<Yield = T, Return = ()>
+{
+}
+
+fn supply_cyclic_generator_yield() {
+    want_cyclic_generator_yield(|| {
+        //~^ ERROR type mismatch
+        if false { yield None.unwrap(); }
+        None.unwrap()
+    })
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/issue-22638.rs b/src/test/compile-fail/issue-22638.rs
index 53b0d9f4e9f..1c534ebbd43 100644
--- a/src/test/compile-fail/issue-22638.rs
+++ b/src/test/compile-fail/issue-22638.rs
@@ -19,7 +19,6 @@ struct A (B);
 
 impl A {
     pub fn matches<F: Fn()>(&self, f: &F) {
-        //~^ ERROR reached the recursion limit while instantiating `A::matches::<[closure
         let &A(ref term) = self;
         term.matches(f);
     }
@@ -59,6 +58,7 @@ struct D (Box<A>);
 
 impl D {
     pub fn matches<F: Fn()>(&self, f: &F) {
+        //~^ ERROR reached the type-length limit while instantiating `D::matches::<[closure
         let &D(ref a) = self;
         a.matches(f)
     }
diff --git a/src/test/run-pass/issue-25439.rs b/src/test/compile-fail/issue-25439.rs
index 88c48f42c51..6e33fd5ae71 100644
--- a/src/test/run-pass/issue-25439.rs
+++ b/src/test/compile-fail/issue-25439.rs
@@ -15,5 +15,5 @@ fn fix<F>(f: F) -> i32 where F: Fn(Helper<F>, i32) -> i32 {
 }
 
 fn main() {
-    fix(|_, x| x);
+    fix(|_, x| x); //~ ERROR closure/generator type that references itself [E0644]
 }
diff --git a/src/test/compile-fail/occurs-check-2.rs b/src/test/compile-fail/occurs-check-2.rs
index a276af83dee..5d162fe944e 100644
--- a/src/test/compile-fail/occurs-check-2.rs
+++ b/src/test/compile-fail/occurs-check-2.rs
@@ -16,7 +16,5 @@ fn main() {
     g = f;
     f = box g;
     //~^  ERROR mismatched types
-    //~| expected type `_`
-    //~| found type `std::boxed::Box<_>`
     //~| cyclic type of infinite size
 }
diff --git a/src/test/compile-fail/occurs-check.rs b/src/test/compile-fail/occurs-check.rs
index 5b6a11e58c2..2c784365ea9 100644
--- a/src/test/compile-fail/occurs-check.rs
+++ b/src/test/compile-fail/occurs-check.rs
@@ -14,7 +14,5 @@ fn main() {
     let f;
     f = box f;
     //~^ ERROR mismatched types
-    //~| expected type `_`
-    //~| found type `std::boxed::Box<_>`
     //~| cyclic type of infinite size
 }
diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs
new file mode 100644
index 00000000000..de7b38d5519
--- /dev/null
+++ b/src/test/mir-opt/inline-closure-borrows-arg.rs
@@ -0,0 +1,50 @@
+// Copyright 2017 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.
+
+// compile-flags: -Z span_free_formats
+
+// Tests that MIR inliner can handle closure arguments,
+// even when (#45894)
+
+fn main() {
+    println!("{}", foo(0, &14));
+}
+
+fn foo<T: Copy>(_t: T, q: &i32) -> i32 {
+    let x = |r: &i32, _s: &i32| {
+        let variable = &*r;
+        *variable
+    };
+    x(q, q)
+}
+
+// END RUST SOURCE
+// START rustc.foo.Inline.after.mir
+// ...
+// bb0: {
+//     ...
+//     _3 = [closure@NodeId(39)];
+//     ...
+//     _4 = &_3;
+//     ...
+//     _6 = &(*_2);
+//     ...
+//     _7 = &(*_2);
+//     _5 = (_6, _7);
+//     _9 = (_5.0: &i32);
+//     _10 = (_5.1: &i32);
+//     StorageLive(_8);
+//     _8 = (*_9);
+//     _0 = _8;
+//     ...
+//     return;
+// }
+// ...
+// END rustc.foo.Inline.after.mir
diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs
index 3f3428714d1..9d3fb923f5b 100644
--- a/src/test/mir-opt/inline-closure.rs
+++ b/src/test/mir-opt/inline-closure.rs
@@ -34,9 +34,11 @@ fn foo<T: Copy>(_t: T, q: i32) -> i32 {
 //     ...
 //     _7 = _2;
 //     _5 = (_6, _7);
-//     _0 = (_5.0: i32);
+//     _8 = (_5.0: i32);
+//     _9 = (_5.1: i32);
+//     _0 = _8;
 //     ...
 //     return;
 // }
 // ...
-// END rustc.foo.Inline.after.mir
\ No newline at end of file
+// END rustc.foo.Inline.after.mir
diff --git a/src/test/ui/block-result/issue-3563.stderr b/src/test/ui/block-result/issue-3563.stderr
index e3f0df6fb5f..c3d5f21b0a5 100644
--- a/src/test/ui/block-result/issue-3563.stderr
+++ b/src/test/ui/block-result/issue-3563.stderr
@@ -6,16 +6,5 @@ error[E0599]: no method named `b` found for type `&Self` in the current scope
    |
    = help: did you mean `a`?
 
-error[E0308]: mismatched types
-  --> $DIR/issue-3563.rs:13:9
-   |
-12 |     fn a(&self) {
-   |                 - possibly return type missing here?
-13 |         || self.b()
-   |         ^^^^^^^^^^^ expected (), found closure
-   |
-   = note: expected type `()`
-              found type `[closure@$DIR/issue-3563.rs:13:9: 13:20 self:_]`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr
index 07f49d999a4..5bd6ef806b5 100644
--- a/src/test/ui/span/coerce-suggestions.stderr
+++ b/src/test/ui/span/coerce-suggestions.stderr
@@ -43,9 +43,6 @@ error[E0308]: mismatched types
    |
 41 |     f = box f;
    |         ^^^^^ cyclic type of infinite size
-   |
-   = note: expected type `_`
-              found type `std::boxed::Box<_>`
 
 error[E0308]: mismatched types
   --> $DIR/coerce-suggestions.rs:48:9
diff --git a/src/test/run-pass/issue-21410.rs b/src/test/ui/unboxed-closure-no-cyclic-sig.rs
index bc525ba54c3..78d119ef329 100644
--- a/src/test/run-pass/issue-21410.rs
+++ b/src/test/ui/unboxed-closure-no-cyclic-sig.rs
@@ -8,6 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// Test that unboxed closures cannot capture their own type.
+//
+// Also regression test for issue #21410.
+
 fn g<F>(_: F) where F: FnOnce(Option<F>) {}
 
 fn main() {
diff --git a/src/test/ui/unboxed-closure-no-cyclic-sig.stderr b/src/test/ui/unboxed-closure-no-cyclic-sig.stderr
new file mode 100644
index 00000000000..a4279a2afac
--- /dev/null
+++ b/src/test/ui/unboxed-closure-no-cyclic-sig.stderr
@@ -0,0 +1,12 @@
+error[E0644]: closure/generator type that references itself
+  --> $DIR/unboxed-closure-no-cyclic-sig.rs:18:7
+   |
+18 |     g(|_| {  });
+   |       ^^^^^^^^ cyclic type of infinite size
+   |
+   = note: closures cannot capture themselves or take themselves as argument;
+           this error may be the result of a recent compiler bug-fix,
+           see https://github.com/rust-lang/rust/issues/46062 for more details
+
+error: aborting due to previous error
+