diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index ee9e199a6c52a..72e1525b506d1 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -564,8 +564,13 @@ fn parse_ty_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F) -> Ty<'tcx> w assert_eq!(next(st), '['); let did = parse_def_(st, ClosureSource, conv); let substs = parse_substs_(st, conv); + let mut tys = vec![]; + while peek(st) != '.' { + tys.push(parse_ty_(st, conv)); + } + assert_eq!(next(st), '.'); assert_eq!(next(st), ']'); - return st.tcx.mk_closure(did, st.tcx.mk_substs(substs)); + return st.tcx.mk_closure(did, st.tcx.mk_substs(substs), tys); } 'P' => { assert_eq!(next(st), '['); diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index e29c0f2b83705..c77e96f164888 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -143,9 +143,13 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) { enc_substs(w, cx, substs); mywrite!(w, "]"); } - ty::TyClosure(def, substs) => { + ty::TyClosure(def, ref substs) => { mywrite!(w, "k[{}|", (cx.ds)(def)); - enc_substs(w, cx, substs); + enc_substs(w, cx, &substs.func_substs); + for ty in &substs.upvar_tys { + enc_ty(w, cx, ty); + } + mywrite!(w, "."); mywrite!(w, "]"); } ty::TyProjection(ref data) => { diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 54fc2daff2b8e..1df8b0ecc33b7 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -256,7 +256,10 @@ macro_rules! return_if_err { ($inp: expr) => ( match $inp { Ok(v) => v, - Err(()) => return + Err(()) => { + debug!("mc reported err"); + return + } } ) } diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index d902cb07494e3..102cd001a296a 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 18059848481e6..799d9a653ae38 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 Substs<'tcx>), Predicate(ast::DefId, ty::Predicate<'tcx>), } @@ -96,9 +95,47 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> { // No borrowed content reachable here. } - ty::TyClosure(def_id, substs) => { - 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 coerced + // 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) => { @@ -273,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 a293170966aac..a54aee2436772 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -1162,7 +1162,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// these unconstrained type variables. fn resolve_type_vars_or_error(&self, t: &Ty<'tcx>) -> mc::McResult> { let ty = self.resolve_type_vars_if_possible(t); - if ty.has_infer_types() || ty.references_error() { Err(()) } else { Ok(ty) } + if ty.references_error() || ty.is_ty_var() { + debug!("resolve_type_vars_or_error: error from {:?}", ty); + Err(()) + } else { + Ok(ty) + } } pub fn fully_resolve>(&self, value: &T) -> FixupResult { @@ -1374,17 +1379,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn closure_type(&self, - def_id: ast::DefId, - substs: &subst::Substs<'tcx>) - -> ty::ClosureTy<'tcx> + def_id: ast::DefId, + substs: &ty::ClosureSubsts<'tcx>) + -> ty::ClosureTy<'tcx> { - let closure_ty = self.tables .borrow() .closure_tys .get(&def_id) .unwrap() - .subst(self.tcx, substs); + .subst(self.tcx, &substs.func_substs); if self.normalize { normalize_associated_type(&self.tcx, &closure_ty) @@ -1392,20 +1396,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { closure_ty } } - - pub fn closure_upvars(&self, - def_id: ast::DefId, - substs: &Substs<'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/liveness.rs b/src/librustc/middle/liveness.rs index 4345649de0c81..7db740798bd40 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1493,7 +1493,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn fn_ret(&self, id: NodeId) -> ty::PolyFnOutput<'tcx> { let fn_ty = self.ir.tcx.node_id_to_type(id); match fn_ty.sty { - ty::TyClosure(closure_def_id, substs) => + ty::TyClosure(closure_def_id, ref substs) => self.ir.tcx.closure_type(closure_def_id, substs).sig.output(), _ => fn_ty.fn_ret() } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index bf47396bb9f65..ee7079bb47d59 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -367,7 +367,13 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { } fn expr_ty(&self, expr: &ast::Expr) -> McResult> { - self.typer.node_ty(expr.id) + match self.typer.node_ty(expr.id) { + Ok(t) => Ok(t), + Err(()) => { + debug!("expr_ty({:?}) yielded Err", expr); + Err(()) + } + } } fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult> { diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 148b27adf64b2..376430e87c6f0 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -270,7 +270,7 @@ pub struct VtableImplData<'tcx, N> { #[derive(Clone, PartialEq, Eq)] pub struct VtableClosureData<'tcx, N> { pub closure_def_id: ast::DefId, - pub substs: subst::Substs<'tcx>, + pub substs: ty::ClosureSubsts<'tcx>, /// Nested obligations. This can be non-empty if the closure /// signature contains associated types. pub nested: Vec @@ -548,7 +548,7 @@ impl<'tcx, N> Vtable<'tcx, N> { VtableClosure(c) => VtableClosure(VtableClosureData { closure_def_id: c.closure_def_id, substs: c.substs, - nested: c.nested.into_iter().map(f).collect() + nested: c.nested.into_iter().map(f).collect(), }) } } diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index cd38e9d0d31bc..ef3a217ecdbf2 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -154,7 +154,7 @@ fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext debug!("consider_unification_despite_ambiguity: self_ty.sty={:?}", self_ty.sty); match self_ty.sty { - ty::TyClosure(closure_def_id, substs) => { + ty::TyClosure(closure_def_id, ref substs) => { let closure_typer = selcx.closure_typer(); let closure_type = closure_typer.closure_type(closure_def_id, substs); let ty::Binder((_, ret_type)) = diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 81e59f57ae7de..4061581ded8f0 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -201,7 +201,7 @@ enum SelectionCandidate<'tcx> { /// Implementation of a `Fn`-family trait by one of the /// anonymous types generated for a `||` expression. - ClosureCandidate(/* closure */ ast::DefId, Substs<'tcx>), + ClosureCandidate(/* closure */ ast::DefId, &'tcx ty::ClosureSubsts<'tcx>), /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int)->int`) @@ -348,7 +348,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // lifetimes can appear inside the self-type. let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); let (closure_def_id, substs) = match self_ty.sty { - ty::TyClosure(id, ref substs) => (id, substs.clone()), + ty::TyClosure(id, ref substs) => (id, substs), _ => { return; } }; assert!(!substs.has_escaping_regions()); @@ -1143,7 +1143,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // type/region parameters let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); let (closure_def_id, substs) = match self_ty.sty { - ty::TyClosure(id, substs) => (id, substs), + ty::TyClosure(id, ref substs) => (id, substs), ty::TyInfer(ty::TyVar(_)) => { debug!("assemble_unboxed_closure_candidates: ambiguous self-type"); candidates.ambiguous = true; @@ -1161,8 +1161,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(closure_kind) => { debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); if closure_kind.extends(kind) { - candidates.vec.push(ClosureCandidate(closure_def_id, - substs.clone())); + candidates.vec.push(ClosureCandidate(closure_def_id, substs)); } } None => { @@ -1285,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())) } } } @@ -1704,7 +1688,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet ty::TyTuple(ref tys) => ok_if(tys.clone()), - ty::TyClosure(def_id, substs) => { + ty::TyClosure(def_id, ref substs) => { // FIXME -- This case is tricky. In the case of by-ref // closures particularly, we need the results of // inference to decide how to reflect the type of each @@ -1730,13 +1714,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return ok_if(Vec::new()); } - 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,55 +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()) - } - - ty::TyClosure(def_id, substs) => { + 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); - - 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() } } } @@ -2016,7 +1995,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ClosureCandidate(closure_def_id, substs) => { let vtable_closure = - try!(self.confirm_closure_candidate(obligation, closure_def_id, &substs)); + try!(self.confirm_closure_candidate(obligation, closure_def_id, substs)); Ok(VtableClosure(vtable_closure)) } @@ -2146,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, @@ -2365,7 +2337,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_closure_candidate(&mut self, obligation: &TraitObligation<'tcx>, closure_def_id: ast::DefId, - substs: &Substs<'tcx>) + substs: &ty::ClosureSubsts<'tcx>) -> Result>, SelectionError<'tcx>> { @@ -2852,7 +2824,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn closure_trait_ref_unnormalized(&mut self, obligation: &TraitObligation<'tcx>, closure_def_id: ast::DefId, - substs: &Substs<'tcx>) + substs: &ty::ClosureSubsts<'tcx>) -> ty::PolyTraitRef<'tcx> { let closure_type = self.infcx.closure_type(closure_def_id, substs); @@ -2874,7 +2846,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn closure_trait_ref(&mut self, obligation: &TraitObligation<'tcx>, closure_def_id: ast::DefId, - substs: &Substs<'tcx>) + substs: &ty::ClosureSubsts<'tcx>) -> Normalized<'tcx, ty::PolyTraitRef<'tcx>> { let trait_ref = self.closure_trait_ref_unnormalized( diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 64e707f6264e6..aa1c8bfaa904f 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1767,7 +1767,7 @@ pub enum TypeVariants<'tcx> { /// The anonymous type of a closure. Used to represent the type of /// `|a| a`. - TyClosure(DefId, &'tcx Substs<'tcx>), + TyClosure(DefId, Box>), /// A tuple type. For example, `(i32, bool)`. TyTuple(Vec>), @@ -1787,6 +1787,93 @@ pub enum TypeVariants<'tcx> { TyError, } +/// A closure can be modeled as a struct that looks like: +/// +/// struct Closure<'l0...'li, T0...Tj, 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). +/// +/// So, for example, given this function: +/// +/// fn foo<'a, T>(data: &'a mut T) { +/// do(|| data.count += 1) +/// } +/// +/// the type of the closure would be something like: +/// +/// struct Closure<'a, T, U0> { +/// data: U0 +/// } +/// +/// Note that the type of the upvar is not specified in the struct. +/// You may wonder how the impl would then be able to use the upvar, +/// if it doesn't know it's type? The answer is that the impl is +/// (conceptually) not fully generic over Closure but rather tied to +/// instances with the expected upvar types: +/// +/// impl<'b, 'a, T> FnMut() for Closure<'a, T, &'b mut &'a mut T> { +/// ... +/// } +/// +/// You can see that the *impl* fully specified the type of the upvar +/// and thus knows full well that `data` has type `&'b mut &'a mut T`. +/// (Here, I am assuming that `data` is mut-borrowed.) +/// +/// Now, the last question you may ask is: Why include the upvar types +/// as extra type parameters? The reason for this design is that the +/// upvar types can reference lifetimes that are internal to the +/// creating function. In my example above, for example, the lifetime +/// `'b` represents the extent of the closure itself; this is some +/// subset of `foo`, probably just the extent of the call to the to +/// `do()`. If we just had the lifetime/type parameters from the +/// enclosing function, we couldn't name this lifetime `'b`. Note that +/// there can also be lifetimes in the types of the upvars themselves, +/// if one of them happens to be a reference to something that the +/// creating fn owns. +/// +/// OK, you say, so why not create a more minimal set of parameters +/// that just includes the extra lifetime parameters? The answer is +/// primarily that it would be hard --- we don't know at the time when +/// we create the closure type what the full types of the upvars are, +/// nor do we know which are borrowed and which are not. In this +/// design, we can just supply a fresh type parameter and figure that +/// out later. +/// +/// All right, you say, but why include the type parameters from the +/// original function then? The answer is that trans may need them +/// when monomorphizing, and they may not appear in the upvars. A +/// closure could capture no variables but still make use of some +/// in-scope type parameter with a bound (e.g., if our example above +/// had an extra `U: Default`, and the closure called `U::default()`). +/// +/// There is another reason. This design (implicitly) prohibits +/// closures from capturing themselves (except via a trait +/// object). This simplifies closure inference considerably, since it +/// means that when we infer the kind of a closure or its upvars, we +/// don't have to handle cycles where the decisions we make for +/// 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 +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct ClosureSubsts<'tcx> { + /// Lifetime and type parameters from the enclosing function. + /// These are separated out because trans wants to pass them around + /// when monomorphizing. + pub func_substs: &'tcx Substs<'tcx>, + + /// The types of the upvars. The list parallels the freevars and + /// `upvar_borrows` lists. These are kept distinct so that we can + /// easily index into them. + pub upvar_tys: Vec> +} + #[derive(Clone, PartialEq, Eq, Hash)] pub struct TraitTy<'tcx> { pub principal: ty::PolyTraitRef<'tcx>, @@ -3214,10 +3301,11 @@ impl FlagComputation { } } - &TyClosure(_, substs) => { + &TyClosure(_, ref substs) => { self.add_flags(TypeFlags::HAS_TY_CLOSURE); self.add_flags(TypeFlags::HAS_LOCAL_NAMES); - self.add_substs(substs); + self.add_substs(&substs.func_substs); + self.add_tys(&substs.upvar_tys); } &TyInfer(_) => { @@ -3461,10 +3549,10 @@ impl<'tcx> ctxt<'tcx> { pub fn closure_type(&self, def_id: ast::DefId, - substs: &subst::Substs<'tcx>) + substs: &ClosureSubsts<'tcx>) -> ty::ClosureTy<'tcx> { - self.tables.borrow().closure_tys.get(&def_id).unwrap().subst(self, substs) + self.tables.borrow().closure_tys.get(&def_id).unwrap().subst(self, &substs.func_substs) } pub fn type_parameter_def(&self, @@ -3659,9 +3747,22 @@ impl<'tcx> ctxt<'tcx> { self.mk_ty(TyStruct(struct_id, substs)) } - pub fn mk_closure(&self, closure_id: ast::DefId, substs: &'tcx Substs<'tcx>) + pub fn mk_closure(&self, + closure_id: ast::DefId, + substs: &'tcx Substs<'tcx>, + tys: Vec>) -> Ty<'tcx> { - self.mk_ty(TyClosure(closure_id, substs)) + self.mk_closure_from_closure_substs(closure_id, Box::new(ClosureSubsts { + func_substs: substs, + upvar_tys: tys + })) + } + + pub fn mk_closure_from_closure_substs(&self, + closure_id: ast::DefId, + closure_substs: Box>) + -> Ty<'tcx> { + self.mk_ty(TyClosure(closure_id, closure_substs)) } pub fn mk_var(&self, v: TyVid) -> Ty<'tcx> { @@ -4146,11 +4247,8 @@ impl<'tcx> TyS<'tcx> { apply_lang_items(cx, did, res) } - TyClosure(did, substs) => { - 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) => { @@ -5905,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: &Substs<'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); - - 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); @@ -6759,6 +6801,13 @@ impl<'tcx> RegionEscape for Substs<'tcx> { } } +impl<'tcx> RegionEscape for ClosureSubsts<'tcx> { + fn has_regions_escaping_depth(&self, depth: u32) -> bool { + self.func_substs.has_regions_escaping_depth(depth) || + self.upvar_tys.iter().any(|t| t.has_regions_escaping_depth(depth)) + } +} + impl RegionEscape for Vec { fn has_regions_escaping_depth(&self, depth: u32) -> bool { self.iter().any(|t| t.has_regions_escaping_depth(depth)) @@ -7102,6 +7151,13 @@ impl<'tcx> HasTypeFlags for BareFnTy<'tcx> { } } +impl<'tcx> HasTypeFlags for ClosureSubsts<'tcx> { + fn has_type_flags(&self, flags: TypeFlags) -> bool { + self.func_substs.has_type_flags(flags) || + self.upvar_tys.iter().any(|t| t.has_type_flags(flags)) + } +} + impl<'tcx> fmt::Debug for ClosureTy<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "ClosureTy({},{:?},{})", diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index eae2bb4966408..b6bb82ad7b15b 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -296,6 +296,16 @@ impl<'tcx> TypeFoldable<'tcx> for subst::Substs<'tcx> { } } +impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> { + fn fold_with>(&self, folder: &mut F) -> ty::ClosureSubsts<'tcx> { + let func_substs = self.func_substs.fold_with(folder); + ty::ClosureSubsts { + func_substs: folder.tcx().mk_substs(func_substs), + upvar_tys: self.upvar_tys.fold_with(folder), + } + } +} + impl<'tcx> TypeFoldable<'tcx> for ty::ItemSubsts<'tcx> { fn fold_with>(&self, folder: &mut F) -> ty::ItemSubsts<'tcx> { ty::ItemSubsts { @@ -604,7 +614,7 @@ pub fn super_fold_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T, } ty::TyClosure(did, ref substs) => { let s = substs.fold_with(this); - ty::TyClosure(did, this.tcx().mk_substs(s)) + ty::TyClosure(did, s) } ty::TyProjection(ref data) => { ty::TyProjection(data.fold_with(this)) diff --git a/src/librustc/middle/ty_relate/mod.rs b/src/librustc/middle/ty_relate/mod.rs index 0159801d5beea..f8678b4d8e3bd 100644 --- a/src/librustc/middle/ty_relate/mod.rs +++ b/src/librustc/middle/ty_relate/mod.rs @@ -48,6 +48,12 @@ pub trait TypeRelation<'a,'tcx> : Sized { Relate::relate(self, a, b) } + /// Relete elements of two slices pairwise. + fn relate_zip>(&mut self, a: &[T], b: &[T]) -> RelateResult<'tcx, Vec> { + assert_eq!(a.len(), b.len()); + a.iter().zip(b).map(|(a, b)| self.relate(a, b)).collect() + } + /// Switch variance for the purpose of relating `a` and `b`. fn relate_with_variance>(&mut self, variance: ty::Variance, @@ -500,15 +506,15 @@ pub fn super_relate_tys<'a,'tcx:'a,R>(relation: &mut R, Ok(tcx.mk_struct(a_id, tcx.mk_substs(substs))) } - (&ty::TyClosure(a_id, a_substs), - &ty::TyClosure(b_id, b_substs)) + (&ty::TyClosure(a_id, ref a_substs), + &ty::TyClosure(b_id, ref b_substs)) if a_id == b_id => { // All TyClosure types with the same id represent // the (anonymous) type of the same closure expression. So // all of their regions should be equated. - let substs = try!(relate_substs(relation, None, a_substs, b_substs)); - Ok(tcx.mk_closure(a_id, tcx.mk_substs(substs))) + let substs = try!(relation.relate(a_substs, b_substs)); + Ok(tcx.mk_closure_from_closure_substs(a_id, substs)) } (&ty::TyBox(a_inner), &ty::TyBox(b_inner)) => @@ -581,6 +587,20 @@ pub fn super_relate_tys<'a,'tcx:'a,R>(relation: &mut R, } } +impl<'a,'tcx:'a> Relate<'a,'tcx> for ty::ClosureSubsts<'tcx> { + fn relate(relation: &mut R, + a: &ty::ClosureSubsts<'tcx>, + b: &ty::ClosureSubsts<'tcx>) + -> RelateResult<'tcx, ty::ClosureSubsts<'tcx>> + where R: TypeRelation<'a,'tcx> + { + let func_substs = try!(relate_substs(relation, None, a.func_substs, b.func_substs)); + let upvar_tys = try!(relation.relate_zip(&a.upvar_tys, &b.upvar_tys)); + Ok(ty::ClosureSubsts { func_substs: relation.tcx().mk_substs(func_substs), + upvar_tys: upvar_tys }) + } +} + impl<'a,'tcx:'a> Relate<'a,'tcx> for ty::Region { fn relate(relation: &mut R, a: &ty::Region, diff --git a/src/librustc/middle/ty_walk.rs b/src/librustc/middle/ty_walk.rs index 3e9a402f9499c..81cad4486904b 100644 --- a/src/librustc/middle/ty_walk.rs +++ b/src/librustc/middle/ty_walk.rs @@ -88,10 +88,13 @@ fn push_subtypes<'tcx>(stack: &mut Vec>, parent_ty: Ty<'tcx>) { }).collect::>()); } ty::TyEnum(_, ref substs) | - ty::TyStruct(_, ref substs) | - ty::TyClosure(_, ref substs) => { + ty::TyStruct(_, ref substs) => { push_reversed(stack, substs.types.as_slice()); } + ty::TyClosure(_, ref substs) => { + push_reversed(stack, substs.func_substs.types.as_slice()); + push_reversed(stack, &substs.upvar_tys); + } ty::TyTuple(ref ts) => { push_reversed(stack, ts); } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index b0510a76385be..fd49d0468c906 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -662,22 +662,35 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> { TyTrait(ref data) => write!(f, "{}", data), ty::TyProjection(ref data) => write!(f, "{}", data), TyStr => write!(f, "str"), - TyClosure(ref did, substs) => ty::tls::with(|tcx| { + TyClosure(ref did, ref substs) => ty::tls::with(|tcx| { try!(write!(f, "[closure")); - let closure_tys = &tcx.tables.borrow().closure_tys; - try!(closure_tys.get(did).map(|cty| &cty.sig).and_then(|sig| { - tcx.lift(&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 7b2bdee50fe78..dc7e34a386f93 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, substs) => { - let infcx = infer::normalizing_infer_ctxt(cx.tcx(), &cx.tcx().tables); - let upvars = infcx.closure_upvars(def_id, substs).unwrap(); - 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, substs) => { - let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let upvars = infcx.closure_upvars(def_id, substs).unwrap(); - 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/attributes.rs b/src/librustc_trans/trans/attributes.rs index f8daefa87a5ff..62b03c9fb0f1f 100644 --- a/src/librustc_trans/trans/attributes.rs +++ b/src/librustc_trans/trans/attributes.rs @@ -145,7 +145,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx let function_type; let (fn_sig, abi, env_ty) = match fn_type.sty { ty::TyBareFn(_, ref f) => (&f.sig, f.abi, None), - ty::TyClosure(closure_did, substs) => { + ty::TyClosure(closure_did, ref substs) => { let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); function_type = infcx.closure_type(closure_did, substs); let self_type = base::self_type_for_closure(ccx, closure_did, fn_type); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 258051357b105..207251496e457 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, substs) => { + 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_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 7900000d3a9df..0f75c1f8ab65a 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -32,7 +32,6 @@ use trans::build::*; use trans::callee; use trans::cleanup; use trans::cleanup::CleanupMethods; -use trans::closure; use trans::common::{self, Block, Result, NodeIdAndSpan, ExprId, CrateContext, ExprOrMethodCall, FunctionContext, MethodCallKey}; use trans::consts; @@ -446,12 +445,6 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( } }; - // If this is a closure, redirect to it. - match closure::get_or_create_declaration_if_closure(ccx, def_id, substs) { - None => {} - Some(llfn) => return llfn, - } - // Check whether this fn has an inlined copy and, if so, redirect // def_id to the local id of the inlined copy. let def_id = inline::maybe_instantiate_inline(ccx, def_id); diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index f00029ec2ff93..ef5da3e40dfda 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -23,10 +23,9 @@ use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue, ByValue}; use trans::debuginfo::{self, DebugLoc}; use trans::declare; use trans::expr; -use trans::monomorphize::{self, MonoId}; +use trans::monomorphize::{MonoId}; use trans::type_of::*; use middle::ty; -use middle::subst::Substs; use session::config::FullDebugInfo; use syntax::abi::RustCall; @@ -126,46 +125,29 @@ impl<'a> ClosureEnv<'a> { /// Returns the LLVM function declaration for a closure, creating it if /// necessary. If the ID does not correspond to a closure ID, returns None. -pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - closure_id: ast::DefId, - substs: &Substs<'tcx>) - -> Option> { - if !ccx.tcx().tables.borrow().closure_kinds.contains_key(&closure_id) { - // Not a closure. - return None - } - - let function_type = ccx.tcx().node_id_to_type(closure_id.node); - let function_type = monomorphize::apply_param_substs(ccx.tcx(), substs, &function_type); - +pub fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + closure_id: ast::DefId, + substs: &ty::ClosureSubsts<'tcx>) + -> ValueRef { // Normalize type so differences in regions and typedefs don't cause // duplicate declarations - let function_type = erase_regions(ccx.tcx(), &function_type); - let params = match function_type.sty { - ty::TyClosure(_, substs) => &substs.types, - _ => unreachable!() - }; + let substs = erase_regions(ccx.tcx(), substs); let mono_id = MonoId { def: closure_id, - params: params + params: &substs.func_substs.types }; - match ccx.closure_vals().borrow().get(&mono_id) { - Some(&llfn) => { - debug!("get_or_create_declaration_if_closure(): found closure {:?}: {:?}", - mono_id, ccx.tn().val_to_string(llfn)); - return Some(Datum::new(llfn, function_type, Rvalue::new(ByValue))) - } - None => {} + if let Some(&llfn) = ccx.closure_vals().borrow().get(&mono_id) { + debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}", + mono_id, ccx.tn().val_to_string(llfn)); + return llfn; } let symbol = ccx.tcx().map.with_path(closure_id.node, |path| { mangle_internal_name_by_path_and_seq(path, "closure") }); - // Currently there’s only a single user of - // get_or_create_declaration_if_closure and it unconditionally defines the - // function, therefore we use define_* here. + let function_type = ccx.tcx().mk_closure_from_closure_substs(closure_id, Box::new(substs)); let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type); // set an inline hint for all closures @@ -178,7 +160,7 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc ccx.tn().val_to_string(llfn)); ccx.closure_vals().borrow_mut().insert(mono_id, llfn); - Some(Datum::new(llfn, function_type, Rvalue::new(ByValue))) + llfn } pub enum Dest<'a, 'tcx: 'a> { @@ -190,9 +172,11 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, decl: &ast::FnDecl, body: &ast::Block, id: ast::NodeId, - param_substs: &'tcx Substs<'tcx>) + closure_substs: &'tcx ty::ClosureSubsts<'tcx>) -> Option> { + let param_substs = closure_substs.func_substs; + let ccx = match dest { Dest::SaveIn(bcx, _) => bcx.ccx(), Dest::Ignore(ccx) => ccx @@ -203,10 +187,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, debug!("trans_closure_expr()"); let closure_id = ast_util::local_def(id); - let llfn = get_or_create_declaration_if_closure( - ccx, - closure_id, - param_substs).unwrap(); + let llfn = get_or_create_closure_declaration(ccx, closure_id, closure_substs); // Get the type of this closure. Use the current `param_substs` as // the closure substitutions. This makes sense because the closure @@ -215,7 +196,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, // of the closure expression. let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); - let function_type = infcx.closure_type(closure_id, param_substs); + let function_type = infcx.closure_type(closure_id, closure_substs); let freevars: Vec = tcx.with_freevars(id, |fv| fv.iter().cloned().collect()); @@ -225,7 +206,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, trans_closure(ccx, decl, body, - llfn.val, + llfn, param_substs, id, &[], @@ -268,19 +249,12 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, closure_def_id: ast::DefId, - substs: Substs<'tcx>, - node: ExprOrMethodCall, - param_substs: &'tcx Substs<'tcx>, + substs: ty::ClosureSubsts<'tcx>, trait_closure_kind: ty::ClosureKind) -> ValueRef { - // The substitutions should have no type parameters remaining - // after passing through fulfill_obligation - let llfn = callee::trans_fn_ref_with_substs(ccx, - closure_def_id, - node, - param_substs, - substs.clone()).val; + // If this is a closure, redirect to it. + let llfn = get_or_create_closure_declaration(ccx, closure_def_id, &substs); // If the closure is a Fn closure, but a FnOnce is needed (etc), // then adapt the self type @@ -296,7 +270,7 @@ pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, fn trans_closure_adapter_shim<'a, 'tcx>( ccx: &'a CrateContext<'a, 'tcx>, closure_def_id: ast::DefId, - substs: Substs<'tcx>, + substs: ty::ClosureSubsts<'tcx>, llfn_closure_kind: ty::ClosureKind, trait_closure_kind: ty::ClosureKind, llfn: ValueRef) @@ -348,7 +322,7 @@ fn trans_closure_adapter_shim<'a, 'tcx>( fn trans_fn_once_adapter_shim<'a, 'tcx>( ccx: &'a CrateContext<'a, 'tcx>, closure_def_id: ast::DefId, - substs: Substs<'tcx>, + substs: ty::ClosureSubsts<'tcx>, llreffn: ValueRef) -> ValueRef { @@ -362,12 +336,11 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // Find a version of the closure type. Substitute static for the // region since it doesn't really matter. - let substs = tcx.mk_substs(substs); - let closure_ty = tcx.mk_closure(closure_def_id, substs); + let closure_ty = tcx.mk_closure_from_closure_substs(closure_def_id, Box::new(substs.clone())); let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), closure_ty); // Make a version with the type of by-ref closure. - let ty::ClosureTy { unsafety, abi, mut sig } = infcx.closure_type(closure_def_id, substs); + let ty::ClosureTy { unsafety, abi, mut sig } = infcx.closure_type(closure_def_id, &substs); sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet let llref_bare_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { unsafety: unsafety, abi: abi, @@ -397,7 +370,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( ast::DUMMY_NODE_ID, false, sig.output, - substs, + substs.func_substs, None, &block_arena); let mut bcx = init_function(&fcx, false, sig.output); diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 302ef68bddc7d..deab11332c809 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -882,11 +882,16 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } }, ast::ExprClosure(_, ref decl, ref body) => { - closure::trans_closure_expr(closure::Dest::Ignore(cx), - decl, - body, - e.id, - param_substs); + match ety.sty { + ty::TyClosure(_, ref substs) => { + closure::trans_closure_expr(closure::Dest::Ignore(cx), decl, + body, e.id, substs); + } + _ => + cx.sess().span_bug( + e.span, + &format!("bad type for closure expr: {:?}", ety)) + } C_null(type_of::type_of(cx, ety)) }, _ => cx.sess().span_bug(e.span, diff --git a/src/librustc_trans/trans/debuginfo/metadata.rs b/src/librustc_trans/trans/debuginfo/metadata.rs index 5f17197a4b9a7..09495dea28686 100644 --- a/src/librustc_trans/trans/debuginfo/metadata.rs +++ b/src/librustc_trans/trans/debuginfo/metadata.rs @@ -287,7 +287,7 @@ impl<'tcx> TypeMap<'tcx> { } } }, - ty::TyClosure(def_id, substs) => { + ty::TyClosure(def_id, ref substs) => { let infcx = infer::normalizing_infer_ctxt(cx.tcx(), &cx.tcx().tables); let closure_ty = infcx.closure_type(def_id, substs); self.get_unique_type_id_of_closure_type(cx, @@ -811,14 +811,10 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) } - ty::TyClosure(def_id, substs) => { - let infcx = infer::normalizing_infer_ctxt(cx.tcx(), &cx.tcx().tables); - let upvars = infcx.closure_upvars(def_id, substs).unwrap(); - let upvar_types = upvars.iter().map(|u| u.ty).collect::>(); - + ty::TyClosure(_, ref substs) => { prepare_tuple_metadata(cx, t, - &upvar_types[..], + &substs.upvar_tys, unique_type_id, usage_site_span).finalize(cx) } diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs index 9ce5c457bff33..8e4e0aaa75fec 100644 --- a/src/librustc_trans/trans/debuginfo/mod.rs +++ b/src/librustc_trans/trans/debuginfo/mod.rs @@ -416,7 +416,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty::TyBareFn(_, ref barefnty) => { (cx.tcx().erase_late_bound_regions(&barefnty.sig), barefnty.abi) } - ty::TyClosure(def_id, substs) => { + ty::TyClosure(def_id, ref substs) => { let closure_type = cx.tcx().closure_type(def_id, substs); (cx.tcx().erase_late_bound_regions(&closure_type.sig), closure_type.abi) } diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index c802de91e38b3..0c77e74be38aa 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -116,7 +116,7 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, ty::TyBareFn(_, ref f) => { (&f.sig, f.abi, None) } - ty::TyClosure(closure_did, substs) => { + ty::TyClosure(closure_did, ref substs) => { let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); function_type = infcx.closure_type(closure_did, substs); let self_type = base::self_type_for_closure(ccx, closure_did, fn_type); diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index f7ace78512013..962803932b88b 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1146,8 +1146,14 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, SaveIn(lldest) => closure::Dest::SaveIn(bcx, lldest), Ignore => closure::Dest::Ignore(bcx.ccx()) }; - closure::trans_closure_expr(dest, decl, body, expr.id, bcx.fcx.param_substs) - .unwrap_or(bcx) + let substs = match expr_ty(bcx, expr).sty { + ty::TyClosure(_, ref substs) => substs, + ref t => + bcx.tcx().sess.span_bug( + expr.span, + &format!("closure expr without closure type: {:?}", t)), + }; + closure::trans_closure_expr(dest, decl, body, expr.id, substs).unwrap_or(bcx) } ast::ExprCall(ref f, ref args) => { if bcx.tcx().is_method_call(expr.id) { diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 8901361b27976..4a549d9c24cda 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -341,8 +341,6 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let llfn = closure::trans_closure_method(bcx.ccx(), vtable_closure.closure_def_id, vtable_closure.substs, - MethodCallKey(method_call), - bcx.fcx.param_substs, trait_closure_kind); Callee { bcx: bcx, @@ -646,8 +644,6 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let llfn = closure::trans_closure_method(ccx, closure_def_id, substs, - ExprId(0), - param_substs, trait_closure_kind); vec![llfn].into_iter() } diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index f32a4fe43d696..4cca3b7582bfb 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -131,7 +131,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, return Some(CallStep::Builtin); } - ty::TyClosure(def_id, substs) => { + ty::TyClosure(def_id, ref substs) => { assert_eq!(def_id.krate, ast::LOCAL_CRATE); // Check whether this is a call to a closure where we diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 6d1e9dfacf281..cb5875ec8bcea 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -53,15 +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 closure_type = fcx.ccx.tcx.mk_closure(expr_def_id, - fcx.ccx.tcx.mk_substs(fcx.inh.infcx.parameter_environment.free_substs.clone())); + 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()), + upvar_tys); fcx.write_ty(expr.id, closure_type); diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 7d911cf8b03bc..38207354792ae 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -41,8 +41,7 @@ pub fn check_drop_impl(tcx: &ty::ctxt, drop_impl_did: ast::DefId) -> Result<(), let dtor_predicates = tcx.lookup_predicates(drop_impl_did); match dtor_self_type.sty { ty::TyEnum(self_type_did, self_to_impl_substs) | - ty::TyStruct(self_type_did, self_to_impl_substs) | - ty::TyClosure(self_type_did, self_to_impl_substs) => { + ty::TyStruct(self_type_did, self_to_impl_substs) => { try!(ensure_drop_params_and_item_params_correspond(tcx, drop_impl_did, dtor_generics, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 1c21813fc63bd..c6f543210ad14 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -89,7 +89,6 @@ use middle::free_region::FreeRegionMap; use middle::implicator; use middle::mem_categorization as mc; use middle::region::CodeExtent; -use middle::subst::Substs; use middle::traits; use middle::ty::{self, ReScope, Ty, MethodCall, HasTypeFlags}; use middle::infer::{self, GenericKind}; @@ -383,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 @@ -1426,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, @@ -1440,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 Substs<'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 c7f084e27cda0..0e3fa654efaad 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,55 +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. */ - self.visit_block(body); + debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id); - debug!("analyzing closure `{}` with fn 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 = @@ -237,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) @@ -268,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) => { @@ -485,8 +520,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> { // ignore nested fn items } visit::FkFnBlock => { - self.analyze_closure(id, decl, body); visit::walk_fn(self, fn_kind, decl, body, span); + self.analyze_closure(id, span, decl, body); } } } diff --git a/src/test/compile-fail/closure-referencing-itself-issue-25954.rs b/src/test/compile-fail/closure-referencing-itself-issue-25954.rs new file mode 100644 index 0000000000000..9357d0e761571 --- /dev/null +++ b/src/test/compile-fail/closure-referencing-itself-issue-25954.rs @@ -0,0 +1,28 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #25954: detect and reject a closure type that +// references itself. + +use std::cell::{Cell, RefCell}; + +struct A { + x: RefCell>, + b: Cell, +} + +fn main() { + let mut p = A{x: RefCell::new(None), b: Cell::new(4i32)}; + + // This is an error about types of infinite size: + let q = || p.b.set(5i32); //~ ERROR mismatched types + + *(p.x.borrow_mut()) = Some(q); +} diff --git a/src/test/compile-fail/issue-25368.rs b/src/test/compile-fail/issue-25368.rs new file mode 100644 index 0000000000000..e70c00502210f --- /dev/null +++ b/src/test/compile-fail/issue-25368.rs @@ -0,0 +1,23 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::sync::mpsc::channel; +use std::thread::spawn; +use std::marker::PhantomData; + +struct Foo {foo: PhantomData} + +fn main() { + let (tx, rx) = channel(); + + spawn(move || { + tx.send(Foo{ foo: PhantomData }); //~ ERROR E0282 + }); +} diff --git a/src/test/compile-fail/regions-proc-bound-capture.rs b/src/test/compile-fail/regions-proc-bound-capture.rs index 3c137133c9867..48b6e8b773f3b 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() { }