From 7ba288dced2d47c53a826d7f46ac6e07a37e187e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Jul 2015 08:22:03 -0400 Subject: [PATCH] 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. --- src/librustc/middle/free_region.rs | 1 - src/librustc/middle/implicator.rs | 61 ++++++++- src/librustc/middle/infer/mod.rs | 14 -- src/librustc/middle/traits/select.rs | 87 ++++-------- src/librustc/middle/ty.rs | 64 +-------- src/librustc/util/ppaux.rs | 38 ++++-- src/librustc_trans/trans/adt.rs | 16 +-- src/librustc_trans/trans/base.rs | 9 +- src/librustc_typeck/check/closure.rs | 27 ++-- src/librustc_typeck/check/regionck.rs | 21 --- src/librustc_typeck/check/upvar.rs | 126 +++++++++++------- .../regions-proc-bound-capture.rs | 2 +- 12 files changed, 214 insertions(+), 252 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::Region, ty::Region), RegionSubGeneric(Option>, ty::Region, GenericKind<'tcx>), - RegionSubClosure(Option>, 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) { + // } + // ``` + // + // 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 (®ion, &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>> - { - 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 where struct Bar { x: T, y: u32 } -> [i32, u32] /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] /// ``` - fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Option>> { + fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { 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 - 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`, 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>> - { - // 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::>(); - 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::>(); - - 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 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() - }); + // 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); + // 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. + // + // 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). - // If we had not yet settled on a closure kind for this closure, - // then we should have by now. Process and remove any deferred resolutions. - // - // Interesting fact: all calls to this closure must come - // *after* its definition. Initially, I thought that some - // kind of fixed-point iteration would be required, due to the - // possibility of twisted examples like this one: - // - // ```rust - // let mut closure0 = None; - // let vec = vec!(1, 2, 3); - // - // loop { - // { - // let closure1 = || { - // match closure0.take() { - // Some(c) => { - // return c(); // (*) call to `closure0` before it is defined - // } - // None => { } - // } - // }; - // closure1(); - // } - // - // closure0 = || vec; - // } - // ``` - // - // However, this turns out to be wrong. Examples like this - // fail to compile because the type of the variable `c` above - // is an inference variable. And in fact since closure types - // cannot be written, there is no way to make this example - // work without a boxed closure. This implies that we can't - // have two closures that recursively call one another without - // some form of boxing (and hence explicit writing of a - // closure kind) involved. Huzzah. -nmatsakis + // 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> { + // 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(isize) + 'a> { fn static_proc(x: &isize) -> Box(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() { }