about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2015-07-17 08:22:03 -0400
committerNiko Matsakis <niko@alum.mit.edu>2015-07-24 04:53:41 -0400
commit7ba288dced2d47c53a826d7f46ac6e07a37e187e (patch)
tree4be9df4b9161430b116063c9d38316cfc707efdc
parenta5516971342ed8fa9ddfb0a05a73c27c180125d1 (diff)
downloadrust-7ba288dced2d47c53a826d7f46ac6e07a37e187e.tar.gz
rust-7ba288dced2d47c53a826d7f46ac6e07a37e187e.zip
Unify the upvar variables found in closures with the actual types of the
upvars after analysis is done. Remove the `closure_upvars` helper and
just consult this list of type variables directly.
-rw-r--r--src/librustc/middle/free_region.rs1
-rw-r--r--src/librustc/middle/implicator.rs61
-rw-r--r--src/librustc/middle/infer/mod.rs14
-rw-r--r--src/librustc/middle/traits/select.rs87
-rw-r--r--src/librustc/middle/ty.rs64
-rw-r--r--src/librustc/util/ppaux.rs38
-rw-r--r--src/librustc_trans/trans/adt.rs16
-rw-r--r--src/librustc_trans/trans/base.rs9
-rw-r--r--src/librustc_typeck/check/closure.rs29
-rw-r--r--src/librustc_typeck/check/regionck.rs21
-rw-r--r--src/librustc_typeck/check/upvar.rs126
-rw-r--r--src/test/compile-fail/regions-proc-bound-capture.rs2
12 files changed, 215 insertions, 253 deletions
diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs
index d902cb07494..102cd001a29 100644
--- a/src/librustc/middle/free_region.rs
+++ b/src/librustc/middle/free_region.rs
@@ -40,7 +40,6 @@ impl FreeRegionMap {
                     self.relate_free_regions(free_a, free_b);
                 }
                 Implication::RegionSubRegion(..) |
-                Implication::RegionSubClosure(..) |
                 Implication::RegionSubGeneric(..) |
                 Implication::Predicate(..) => {
                 }
diff --git a/src/librustc/middle/implicator.rs b/src/librustc/middle/implicator.rs
index 8fba98bead4..a129039002c 100644
--- a/src/librustc/middle/implicator.rs
+++ b/src/librustc/middle/implicator.rs
@@ -28,7 +28,6 @@ use util::nodemap::FnvHashSet;
 pub enum Implication<'tcx> {
     RegionSubRegion(Option<Ty<'tcx>>, ty::Region, ty::Region),
     RegionSubGeneric(Option<Ty<'tcx>>, ty::Region, GenericKind<'tcx>),
-    RegionSubClosure(Option<Ty<'tcx>>, ty::Region, ast::DefId, &'tcx ty::ClosureSubsts<'tcx>),
     Predicate(ast::DefId, ty::Predicate<'tcx>),
 }
 
@@ -96,10 +95,47 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
                 // No borrowed content reachable here.
             }
 
-            ty::TyClosure(def_id, ref substs) => {
-                // TODO remove RegionSubClosure
-                let &(r_a, opt_ty) = self.stack.last().unwrap();
-                self.out.push(Implication::RegionSubClosure(opt_ty, r_a, def_id, substs));
+            ty::TyClosure(_, ref substs) => {
+                // FIXME(#27086). We do not accumulate from substs, since they
+                // don't represent reachable data. This means that, in
+                // practice, some of the lifetime parameters might not
+                // be in scope when the body runs, so long as there is
+                // no reachable data with that lifetime. For better or
+                // worse, this is consistent with fn types, however,
+                // which can also encapsulate data in this fashion
+                // (though it's somewhat harder, and typically
+                // requires virtual dispatch).
+                //
+                // Note that changing this (in a naive way, at least)
+                // causes regressions for what appears to be perfectly
+                // reasonable code like this:
+                //
+                // ```
+                // fn foo<'a>(p: &Data<'a>) {
+                //    bar(|q: &mut Parser| q.read_addr())
+                // }
+                // fn bar(p: Box<FnMut(&mut Parser)+'static>) {
+                // }
+                // ```
+                //
+                // Note that `p` (and `'a`) are not used in the
+                // closure at all, but to meet the requirement that
+                // the closure type `C: 'static` (so it can be coerce
+                // to the object type), we get the requirement that
+                // `'a: 'static` since `'a` appears in the closure
+                // type `C`.
+                //
+                // A smarter fix might "prune" unused `func_substs` --
+                // this would avoid breaking simple examples like
+                // this, but would still break others (which might
+                // indeed be invalid, depending on your POV). Pruning
+                // would be a subtle process, since we have to see
+                // what func/type parameters are used and unused,
+                // taking into consideration UFCS and so forth.
+
+                for &upvar_ty in &substs.upvar_tys {
+                    self.accumulate_from_ty(upvar_ty);
+                }
             }
 
             ty::TyTrait(ref t) => {
@@ -274,6 +310,21 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
         self.out.extend(obligations);
 
         let variances = self.tcx().item_variances(def_id);
+        self.accumulate_from_substs(substs, Some(&variances));
+    }
+
+    fn accumulate_from_substs(&mut self,
+                              substs: &Substs<'tcx>,
+                              variances: Option<&ty::ItemVariances>)
+    {
+        let mut tmp_variances = None;
+        let variances = variances.unwrap_or_else(|| {
+            tmp_variances = Some(ty::ItemVariances {
+                types: substs.types.map(|_| ty::Variance::Invariant),
+                regions: substs.regions().map(|_| ty::Variance::Invariant),
+            });
+            tmp_variances.as_ref().unwrap()
+        });
 
         for (&region, &variance) in substs.regions().iter().zip(&variances.regions) {
             match variance {
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index 48042c152b8..46cbf9351e0 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -1399,20 +1399,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             closure_ty
         }
     }
-
-    pub fn closure_upvars(&self,
-                          def_id: ast::DefId,
-                          substs: &ty::ClosureSubsts<'tcx>)
-                          -> Option<Vec<ty::ClosureUpvar<'tcx>>>
-    {
-        let result = ty::ctxt::closure_upvars(self, def_id, substs);
-
-        if self.normalize {
-            normalize_associated_type(&self.tcx, &result)
-        } else {
-            result
-        }
-    }
 }
 
 impl<'tcx> TypeTrace<'tcx> {
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index c2c2ceb0b03..4061581ded8 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -1284,22 +1284,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     candidates.ambiguous = true;
                 }
                 _ => {
-                    if self.constituent_types_for_ty(self_ty).is_some() {
-                        candidates.vec.push(DefaultImplCandidate(def_id.clone()))
-                    } else {
-                        // We don't yet know what the constituent
-                        // types are. So call it ambiguous for now,
-                        // though this is a bit stronger than
-                        // necessary: that is, we know that the
-                        // defaulted impl applies, but we can't
-                        // process the confirmation step without
-                        // knowing the constituent types. (Anyway, in
-                        // the particular case of defaulted impls, it
-                        // doesn't really matter much either way,
-                        // since we won't be aiding inference by
-                        // processing the confirmation step.)
-                        candidates.ambiguous = true;
-                    }
+                    candidates.vec.push(DefaultImplCandidate(def_id.clone()))
                 }
             }
         }
@@ -1729,14 +1714,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     return ok_if(Vec::new());
                 }
 
-                // TODO
-                match self.infcx.closure_upvars(def_id, substs) {
-                    Some(upvars) => ok_if(upvars.iter().map(|c| c.ty).collect()),
-                    None => {
-                        debug!("assemble_builtin_bound_candidates: no upvar types available yet");
-                        Ok(AmbiguousBuiltin)
-                    }
-                }
+                ok_if(substs.upvar_tys.clone())
             }
 
             ty::TyStruct(def_id, substs) => {
@@ -1819,7 +1797,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// Bar<i32> where struct Bar<T> { x: T, y: u32 } -> [i32, u32]
     /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32]
     /// ```
-    fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Option<Vec<Ty<'tcx>>> {
+    fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec<Ty<'tcx>> {
         match t.sty {
             ty::TyUint(_) |
             ty::TyInt(_) |
@@ -1831,7 +1809,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ty::TyInfer(ty::IntVar(_)) |
             ty::TyInfer(ty::FloatVar(_)) |
             ty::TyChar => {
-                Some(Vec::new())
+                Vec::new()
             }
 
             ty::TyTrait(..) |
@@ -1848,56 +1826,56 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             ty::TyBox(referent_ty) => {  // Box<T>
-                Some(vec![referent_ty])
+                vec![referent_ty]
             }
 
             ty::TyRawPtr(ty::TypeAndMut { ty: element_ty, ..}) |
             ty::TyRef(_, ty::TypeAndMut { ty: element_ty, ..}) => {
-                Some(vec![element_ty])
+                vec![element_ty]
             },
 
             ty::TyArray(element_ty, _) | ty::TySlice(element_ty) => {
-                Some(vec![element_ty])
+                vec![element_ty]
             }
 
             ty::TyTuple(ref tys) => {
                 // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
-                Some(tys.clone())
+                tys.clone()
             }
 
             ty::TyClosure(def_id, ref substs) => {
+                // FIXME(#27086). We are invariant w/r/t our
+                // substs.func_substs, but we don't see them as
+                // constituent types; this seems RIGHT but also like
+                // something that a normal type couldn't simulate. Is
+                // this just a gap with the way that PhantomData and
+                // OIBIT interact? That is, there is no way to say
+                // "make me invariant with respect to this TYPE, but
+                // do not act as though I can reach it"
                 assert_eq!(def_id.krate, ast::LOCAL_CRATE);
-
-                // TODO
-                match self.infcx.closure_upvars(def_id, substs) {
-                    Some(upvars) => {
-                        Some(upvars.iter().map(|c| c.ty).collect())
-                    }
-                    None => {
-                        None
-                    }
-                }
+                substs.upvar_tys.clone()
             }
 
             // for `PhantomData<T>`, we pass `T`
             ty::TyStruct(def_id, substs)
                 if Some(def_id) == self.tcx().lang_items.phantom_data() =>
             {
-                Some(substs.types.get_slice(TypeSpace).to_vec())
+                substs.types.get_slice(TypeSpace).to_vec()
             }
 
             ty::TyStruct(def_id, substs) => {
-                Some(self.tcx().struct_fields(def_id, substs).iter()
-                     .map(|f| f.mt.ty)
-                     .collect())
+                self.tcx().struct_fields(def_id, substs)
+                          .iter()
+                          .map(|f| f.mt.ty)
+                          .collect()
             }
 
             ty::TyEnum(def_id, substs) => {
-                Some(self.tcx().substd_enum_variants(def_id, substs)
-                     .iter()
-                     .flat_map(|variant| &variant.args)
-                     .map(|&ty| ty)
-                     .collect())
+                self.tcx().substd_enum_variants(def_id, substs)
+                          .iter()
+                          .flat_map(|variant| &variant.args)
+                          .map(|&ty| ty)
+                          .collect()
             }
         }
     }
@@ -2147,15 +2125,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // binder is moved below
         let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
-        match self.constituent_types_for_ty(self_ty) {
-            Some(types) => self.vtable_default_impl(obligation, trait_def_id, ty::Binder(types)),
-            None => {
-                self.tcx().sess.bug(
-                    &format!(
-                        "asked to confirm default implementation for ambiguous type: {:?}",
-                        self_ty));
-            }
-        }
+        let types = self.constituent_types_for_ty(self_ty);
+        self.vtable_default_impl(obligation, trait_def_id, ty::Binder(types))
     }
 
     fn confirm_default_impl_object_candidate(&mut self,
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 5528472d793..94b5e7e3a59 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -4247,12 +4247,8 @@ impl<'tcx> TyS<'tcx> {
                     apply_lang_items(cx, did, res)
                 }
 
-                TyClosure(did, ref substs) => {
-                    // TODO
-                    let param_env = cx.empty_parameter_environment();
-                    let infcx = infer::new_infer_ctxt(cx, &cx.tables, Some(param_env), false);
-                    let upvars = infcx.closure_upvars(did, substs).unwrap();
-                    TypeContents::union(&upvars, |f| tc_ty(cx, &f.ty, cache))
+                TyClosure(_, ref substs) => {
+                    TypeContents::union(&substs.upvar_tys, |ty| tc_ty(cx, &ty, cache))
                 }
 
                 TyTuple(ref tys) => {
@@ -6007,62 +6003,6 @@ impl<'tcx> ctxt<'tcx> {
         (a, b)
     }
 
-    // Returns a list of `ClosureUpvar`s for each upvar.
-    pub fn closure_upvars<'a>(typer: &infer::InferCtxt<'a, 'tcx>,
-                              closure_id: ast::DefId,
-                              substs: &ClosureSubsts<'tcx>)
-                              -> Option<Vec<ClosureUpvar<'tcx>>>
-    {
-        // Presently an unboxed closure type cannot "escape" out of a
-        // function, so we will only encounter ones that originated in the
-        // local crate or were inlined into it along with some function.
-        // This may change if abstract return types of some sort are
-        // implemented.
-        assert!(closure_id.krate == ast::LOCAL_CRATE);
-        let tcx = typer.tcx;
-        match tcx.freevars.borrow().get(&closure_id.node) {
-            None => Some(vec![]),
-            Some(ref freevars) => {
-                freevars.iter()
-                        .map(|freevar| {
-                            let freevar_def_id = freevar.def.def_id();
-                            let freevar_ty = match typer.node_ty(freevar_def_id.node) {
-                                Ok(t) => { t }
-                                Err(()) => { return None; }
-                            };
-                            let freevar_ty = freevar_ty.subst(tcx, &substs.func_substs);
-
-                            let upvar_id = ty::UpvarId {
-                                var_id: freevar_def_id.node,
-                                closure_expr_id: closure_id.node
-                            };
-
-                            typer.upvar_capture(upvar_id).map(|capture| {
-                                let freevar_ref_ty = match capture {
-                                    UpvarCapture::ByValue => {
-                                        freevar_ty
-                                    }
-                                    UpvarCapture::ByRef(borrow) => {
-                                        tcx.mk_ref(tcx.mk_region(borrow.region),
-                                            ty::TypeAndMut {
-                                                ty: freevar_ty,
-                                                mutbl: borrow.kind.to_mutbl_lossy(),
-                                            })
-                                    }
-                                };
-
-                                ClosureUpvar {
-                                    def: freevar.def,
-                                    span: freevar.span,
-                                    ty: freevar_ref_ty,
-                                }
-                            })
-                        })
-                        .collect()
-            }
-        }
-    }
-
     // Returns the repeat count for a repeating vector expression.
     pub fn eval_repeat_count(&self, count_expr: &ast::Expr) -> usize {
         let hint = UncheckedExprHint(self.types.usize);
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index d24fa5cef03..fd49d0468c9 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -665,22 +665,32 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
             TyClosure(ref did, ref substs) => ty::tls::with(|tcx| {
                 try!(write!(f, "[closure"));
 
-                // TODO consider changing this to print out the upvar types instead
-
-                let closure_tys = &tcx.tables.borrow().closure_tys;
-                try!(closure_tys.get(did).map(|cty| &cty.sig).and_then(|sig| {
-                    tcx.lift(&substs.func_substs).map(|substs| sig.subst(tcx, substs))
-                }).map(|sig| {
-                    fn_sig(f, &sig.0.inputs, false, sig.0.output)
-                }).unwrap_or_else(|| {
-                    if did.krate == ast::LOCAL_CRATE {
-                        try!(write!(f, " {:?}", tcx.map.span(did.node)));
+                if did.krate == ast::LOCAL_CRATE {
+                    try!(write!(f, "@{:?}", tcx.map.span(did.node)));
+                    let mut sep = " ";
+                    try!(tcx.with_freevars(did.node, |freevars| {
+                        for (freevar, upvar_ty) in freevars.iter().zip(&substs.upvar_tys) {
+                            let node_id = freevar.def.local_node_id();
+                            try!(write!(f,
+                                        "{}{}:{}",
+                                        sep,
+                                        tcx.local_var_name_str(node_id),
+                                        upvar_ty));
+                            sep = ", ";
+                        }
+                        Ok(())
+                    }))
+                } else {
+                    // cross-crate closure types should only be
+                    // visible in trans bug reports, I imagine.
+                    try!(write!(f, "@{:?}", did));
+                    let mut sep = " ";
+                    for (index, upvar_ty) in substs.upvar_tys.iter().enumerate() {
+                        try!(write!(f, "{}{}:{}", sep, index, upvar_ty));
+                        sep = ", ";
                     }
-                    Ok(())
-                }));
-                if verbose() {
-                    try!(write!(f, " id={:?}", did));
                 }
+
                 write!(f, "]")
             }),
             TyArray(ty, sz) => write!(f, "[{}; {}]",  ty, sz),
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index b51a3d4b2a0..dc7e34a386f 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -48,7 +48,6 @@ use std::rc::Rc;
 use llvm::{ValueRef, True, IntEQ, IntNE};
 use back::abi::FAT_PTR_ADDR;
 use middle::subst;
-use middle::infer;
 use middle::ty::{self, Ty};
 use middle::ty::Disr;
 use syntax::ast;
@@ -221,11 +220,8 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             Univariant(mk_struct(cx, &ftys[..], packed, t), dtor_to_init_u8(dtor))
         }
-        ty::TyClosure(def_id, ref substs) => {
-            let infcx = infer::normalizing_infer_ctxt(cx.tcx(), &cx.tcx().tables);
-            let upvars = infcx.closure_upvars(def_id, substs).unwrap(); // TODO
-            let upvar_types = upvars.iter().map(|u| u.ty).collect::<Vec<_>>();
-            Univariant(mk_struct(cx, &upvar_types[..], false, t), 0)
+        ty::TyClosure(_, ref substs) => {
+            Univariant(mk_struct(cx, &substs.upvar_tys, false, t), 0)
         }
         ty::TyEnum(def_id, substs) => {
             let cases = get_cases(cx.tcx(), def_id, substs);
@@ -441,12 +437,8 @@ fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>,
 
         // Perhaps one of the upvars of this struct is non-zero
         // Let's recurse and find out!
-        ty::TyClosure(def_id, ref substs) => {
-            let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
-            let upvars = infcx.closure_upvars(def_id, substs).unwrap(); // TODO
-            let upvar_types = upvars.iter().map(|u| u.ty).collect::<Vec<_>>();
-
-            for (j, &ty) in upvar_types.iter().enumerate() {
+        ty::TyClosure(_, ref substs) => {
+            for (j, &ty) in substs.upvar_tys.iter().enumerate() {
                 if let Some(mut fpath) = find_discr_field_candidate(tcx, ty, path.clone()) {
                     fpath.push(j);
                     return Some(fpath);
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index e18fc6c5da2..207251496e4 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -37,7 +37,6 @@ use llvm;
 use metadata::{csearch, encoder, loader};
 use middle::astencode;
 use middle::cfg;
-use middle::infer;
 use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
 use middle::weak_lang_items;
 use middle::pat_util::simple_identifier;
@@ -470,13 +469,11 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
               }
           })
       }
-      ty::TyClosure(def_id, ref substs) => { // TODO
+      ty::TyClosure(_, ref substs) => {
           let repr = adt::represent_type(cx.ccx(), t);
-          let infcx = infer::normalizing_infer_ctxt(cx.tcx(), &cx.tcx().tables);
-          let upvars = infcx.closure_upvars(def_id, substs).unwrap();
-          for (i, upvar) in upvars.iter().enumerate() {
+          for (i, upvar_ty) in substs.upvar_tys.iter().enumerate() {
               let llupvar = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i);
-              cx = f(cx, llupvar, upvar.ty);
+              cx = f(cx, llupvar, upvar_ty);
           }
       }
       ty::TyArray(_, n) => {
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index 71e51292a78..cb5875ec8bc 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -53,25 +53,26 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
            opt_kind,
            expected_sig);
 
-    let mut fn_ty = astconv::ty_of_closure(
-        fcx,
-        ast::Unsafety::Normal,
-        decl,
-        abi::RustCall,
-        expected_sig);
-
-    let freevar_tys =
-        fcx.tcx().with_freevars(expr.id, |fv| {
-            fv.iter()
-              .map(|_| fcx.tcx().types.bool) // TODO
-              .collect()
-        });
+    let mut fn_ty = astconv::ty_of_closure(fcx,
+                                           ast::Unsafety::Normal,
+                                           decl,
+                                           abi::RustCall,
+                                           expected_sig);
+
+    // Create type variables (for now) to represent the transformed
+    // types of upvars. These will be unified during the upvar
+    // inference phase (`upvar.rs`).
+    let num_upvars = fcx.tcx().with_freevars(expr.id, |fv| fv.len());
+    let upvar_tys = fcx.infcx().next_ty_vars(num_upvars);
+
+    debug!("check_closure: expr.id={:?} upvar_tys={:?}",
+           expr.id, upvar_tys);
 
     let closure_type =
         fcx.ccx.tcx.mk_closure(
             expr_def_id,
             fcx.ccx.tcx.mk_substs(fcx.inh.infcx.parameter_environment.free_substs.clone()),
-            freevar_tys);
+            upvar_tys);
 
     fcx.write_ty(expr.id, closure_type);
 
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index ba89908bbe7..c6f543210ad 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -382,7 +382,6 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
                         self.region_bound_pairs.push((r_a, generic_b.clone()));
                     }
                     implicator::Implication::RegionSubRegion(..) |
-                    implicator::Implication::RegionSubClosure(..) |
                     implicator::Implication::Predicate(..) => {
                         // In principle, we could record (and take
                         // advantage of) every relationship here, but
@@ -1425,9 +1424,6 @@ pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
                 let o1 = infer::ReferenceOutlivesReferent(ty, origin.span());
                 generic_must_outlive(rcx, o1, r_a, generic_b);
             }
-            implicator::Implication::RegionSubClosure(_, r_a, def_id, substs) => {
-                closure_must_outlive(rcx, origin.clone(), r_a, def_id, substs);
-            }
             implicator::Implication::Predicate(def_id, predicate) => {
                 let cause = traits::ObligationCause::new(origin.span(),
                                                          rcx.body_id,
@@ -1439,23 +1435,6 @@ pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
     }
 }
 
-fn closure_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
-                                  origin: infer::SubregionOrigin<'tcx>,
-                                  region: ty::Region,
-                                  def_id: ast::DefId,
-                                  substs: &'tcx ty::ClosureSubsts<'tcx>) {
-    debug!("closure_must_outlive(region={:?}, def_id={:?}, substs={:?})",
-           region, def_id, substs);
-
-    let upvars = rcx.fcx.infcx().closure_upvars(def_id, substs).unwrap();
-    for upvar in upvars {
-        let var_id = upvar.def.def_id().local_id();
-        type_must_outlive(
-            rcx, infer::FreeVariable(origin.span(), var_id),
-            upvar.ty, region);
-    }
-}
-
 fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
                                   origin: infer::SubregionOrigin<'tcx>,
                                   region: ty::Region,
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 20db3b69bd1..0e3fa654efa 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -42,9 +42,10 @@
 
 use super::FnCtxt;
 
+use check::demand;
 use middle::expr_use_visitor as euv;
 use middle::mem_categorization as mc;
-use middle::ty::{self};
+use middle::ty::{self, Ty};
 use middle::infer::{InferCtxt, UpvarRegion};
 use std::collections::HashSet;
 use syntax::ast;
@@ -178,54 +179,55 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
         AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds }
     }
 
-    fn analyze_closure(&mut self, id: ast::NodeId, decl: &ast::FnDecl, body: &ast::Block) {
+    fn analyze_closure(&mut self,
+                       id: ast::NodeId,
+                       span: Span,
+                       decl: &ast::FnDecl,
+                       body: &ast::Block) {
         /*!
          * Analysis starting point.
          */
 
         debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id);
 
+        {
+            let mut euv = euv::ExprUseVisitor::new(self, self.fcx.infcx());
+            euv.walk_fn(decl, body);
+        }
 
-        let mut euv = euv::ExprUseVisitor::new(self, self.fcx.infcx());
-        euv.walk_fn(decl, body);
-
-        // If we had not yet settled on a closure kind for this closure,
-        // then we should have by now. Process and remove any deferred resolutions.
-        //
-        // Interesting fact: all calls to this closure must come
-        // *after* its definition.  Initially, I thought that some
-        // kind of fixed-point iteration would be required, due to the
-        // possibility of twisted examples like this one:
-        //
-        // ```rust
-        // let mut closure0 = None;
-        // let vec = vec!(1, 2, 3);
-        //
-        // loop {
-        //     {
-        //         let closure1 = || {
-        //             match closure0.take() {
-        //                 Some(c) => {
-        //                     return c(); // (*) call to `closure0` before it is defined
-        //                 }
-        //                 None => { }
-        //             }
-        //         };
-        //         closure1();
-        //     }
-        //
-        //     closure0 = || vec;
-        // }
-        // ```
+        // 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
+        // with that information.
         //
-        // However, this turns out to be wrong. Examples like this
-        // fail to compile because the type of the variable `c` above
-        // is an inference variable.  And in fact since closure types
-        // cannot be written, there is no way to make this example
-        // work without a boxed closure. This implies that we can't
-        // have two closures that recursively call one another without
-        // some form of boxing (and hence explicit writing of a
-        // closure kind) involved. Huzzah. -nmatsakis
+        // Note that no closure type C may have an upvar of type C
+        // (though it may reference itself via a trait object). This
+        // results from the desugaring of closures to a struct like
+        // `Foo<..., UV0...UVn>`. If one of those upvars referenced
+        // C, then the type would have infinite size (and the
+        // inference algorithm will reject it).
+
+        // Extract the type variables UV0...UVn.
+        let closure_substs = match self.fcx.node_ty(id).sty {
+            ty::TyClosure(_, ref substs) => substs,
+            ref t => {
+                self.fcx.tcx().sess.span_bug(
+                    span,
+                    &format!("type of closure expr {:?} is not a closure {:?}",
+                             id, t));
+            }
+        };
+
+        // Equate the type variables with the actual types.
+        let final_upvar_tys = self.final_upvar_tys(id);
+        debug!("analyze_closure: id={:?} closure_substs={:?} final_upvar_tys={:?}",
+               id, closure_substs, final_upvar_tys);
+        for (&upvar_ty, final_upvar_ty) in closure_substs.upvar_tys.iter().zip(final_upvar_tys) {
+            demand::eqtype(self.fcx, span, final_upvar_ty, upvar_ty);
+        }
+
+        // Now we must process and remove any deferred resolutions,
+        // since we have a concrete closure kind.
         let closure_def_id = ast_util::local_def(id);
         if self.closures_with_inferred_kinds.contains(&id) {
             let mut deferred_call_resolutions =
@@ -236,6 +238,42 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
         }
     }
 
+    // Returns a list of `ClosureUpvar`s for each upvar.
+    fn final_upvar_tys(&mut self, closure_id: ast::NodeId) -> Vec<Ty<'tcx>> {
+        // Presently an unboxed closure type cannot "escape" out of a
+        // function, so we will only encounter ones that originated in the
+        // local crate or were inlined into it along with some function.
+        // This may change if abstract return types of some sort are
+        // implemented.
+        let tcx = self.fcx.tcx();
+        tcx.with_freevars(closure_id, |freevars| {
+            freevars.iter()
+                    .map(|freevar| {
+                        let freevar_def_id = freevar.def.def_id();
+                        let freevar_ty = self.fcx.node_ty(freevar_def_id.node);
+                        let upvar_id = ty::UpvarId {
+                            var_id: freevar_def_id.node,
+                            closure_expr_id: closure_id
+                        };
+                        let capture = self.fcx.infcx().upvar_capture(upvar_id).unwrap();
+
+                        debug!("freevar_def_id={:?} freevar_ty={:?} capture={:?}",
+                               freevar_def_id, freevar_ty, capture);
+
+                        match capture {
+                            ty::UpvarCapture::ByValue => freevar_ty,
+                            ty::UpvarCapture::ByRef(borrow) =>
+                                tcx.mk_ref(tcx.mk_region(borrow.region),
+                                           ty::TypeAndMut {
+                                               ty: freevar_ty,
+                                               mutbl: borrow.kind.to_mutbl_lossy(),
+                                           }),
+                        }
+                    })
+                    .collect()
+            })
+    }
+
     fn adjust_upvar_borrow_kind_for_consume(&self,
                                             cmt: mc::cmt<'tcx>,
                                             mode: euv::ConsumeMode)
@@ -267,10 +305,8 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
                         // to move out of an upvar, this must be a FnOnce closure
                         self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
 
-                        let upvar_capture_map = &mut self.fcx
-                                                         .inh
-                                                         .tables.borrow_mut()
-                                                         .upvar_capture_map;
+                        let upvar_capture_map =
+                            &mut self.fcx.inh.tables.borrow_mut().upvar_capture_map;
                         upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
                     }
                     mc::NoteClosureEnv(upvar_id) => {
diff --git a/src/test/compile-fail/regions-proc-bound-capture.rs b/src/test/compile-fail/regions-proc-bound-capture.rs
index 3c137133c98..48b6e8b773f 100644
--- a/src/test/compile-fail/regions-proc-bound-capture.rs
+++ b/src/test/compile-fail/regions-proc-bound-capture.rs
@@ -18,7 +18,7 @@ fn borrowed_proc<'a>(x: &'a isize) -> Box<FnMut()->(isize) + 'a> {
 
 fn static_proc(x: &isize) -> Box<FnMut()->(isize) + 'static> {
     // This is illegal, because the region bound on `proc` is 'static.
-    Box::new(move|| { *x }) //~ ERROR captured variable `x` does not outlive the enclosing closure
+    Box::new(move|| { *x }) //~ ERROR does not fulfill the required lifetime
 }
 
 fn main() { }