Auto merge of #27087 - nikomatsakis:closure-exploration, r=nrc

Refactors the "desugaring" of closures to expose the types of the upvars. This is necessary to be faithful with how actual structs work. The reasoning of the particular desugaring that I chose is explained in a fairly detailed comment.

As a side-effect, recursive closure types are prohibited unless a trait object intermediary is used. This fixes #25954 and also eliminates concerns about unrepresentable closure types that have infinite size, I believe. I don't believe this can cause regressions because of #25954.

(As for motivation, besides #25954 etc, this work is also intended as refactoring in support of incremental compilation, since closures are one of the thornier cases encountered when attempting to split node-ids into item-ids and within-item-ids. The goal is to eliminate the "internal def-id" distinction in astdecoding. However, I have to do more work on trans to really make progress there.)

r? @nrc
This commit is contained in:
bors 2015-07-24 16:36:32 +00:00
commit 5e6b534362
35 changed files with 536 additions and 371 deletions

View file

@ -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), '[');

View file

@ -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) => {

View file

@ -256,7 +256,10 @@ macro_rules! return_if_err {
($inp: expr) => (
match $inp {
Ok(v) => v,
Err(()) => return
Err(()) => {
debug!("mc reported err");
return
}
}
)
}

View file

@ -40,7 +40,6 @@ impl FreeRegionMap {
self.relate_free_regions(free_a, free_b);
}
Implication::RegionSubRegion(..) |
Implication::RegionSubClosure(..) |
Implication::RegionSubGeneric(..) |
Implication::Predicate(..) => {
}

View file

@ -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 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<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 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 (&region, &variance) in substs.regions().iter().zip(&variances.regions) {
match variance {

View file

@ -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<Ty<'tcx>> {
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<T:TypeFoldable<'tcx>>(&self, value: &T) -> FixupResult<T> {
@ -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<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> {

View file

@ -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()
}

View file

@ -367,7 +367,13 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
}
fn expr_ty(&self, expr: &ast::Expr) -> McResult<Ty<'tcx>> {
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<Ty<'tcx>> {

View file

@ -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<N>
@ -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(),
})
}
}

View file

@ -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)) =

View file

@ -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<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,55 +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, substs) => {
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<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()
}
}
}
@ -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<VtableClosureData<'tcx, PredicateObligation<'tcx>>,
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(

View file

@ -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<ClosureSubsts<'tcx>>),
/// A tuple type. For example, `(i32, bool)`.
TyTuple(Vec<Ty<'tcx>>),
@ -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<Ty<'tcx>>
}
#[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>>)
-> 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<ClosureSubsts<'tcx>>)
-> 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<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);
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<T:RegionEscape> RegionEscape for Vec<T> {
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({},{:?},{})",

View file

@ -296,6 +296,16 @@ impl<'tcx> TypeFoldable<'tcx> for subst::Substs<'tcx> {
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> {
fn fold_with<F: TypeFolder<'tcx>>(&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<F: TypeFolder<'tcx>>(&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))

View file

@ -48,6 +48,12 @@ pub trait TypeRelation<'a,'tcx> : Sized {
Relate::relate(self, a, b)
}
/// Relete elements of two slices pairwise.
fn relate_zip<T:Relate<'a,'tcx>>(&mut self, a: &[T], b: &[T]) -> RelateResult<'tcx, Vec<T>> {
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<T:Relate<'a,'tcx>>(&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<R>(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<R>(relation: &mut R,
a: &ty::Region,

View file

@ -88,10 +88,13 @@ fn push_subtypes<'tcx>(stack: &mut Vec<Ty<'tcx>>, parent_ty: Ty<'tcx>) {
}).collect::<Vec<_>>());
}
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);
}

View file

@ -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),

View file

@ -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::<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, 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::<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);

View file

@ -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);

View file

@ -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) => {

View file

@ -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);

View file

@ -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<Datum<'tcx, Rvalue>> {
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 theres 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<Block<'a, 'tcx>>
{
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<ty::Freevar> =
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);

View file

@ -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,

View file

@ -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::<Vec<_>>();
ty::TyClosure(_, ref substs) => {
prepare_tuple_metadata(cx,
t,
&upvar_types[..],
&substs.upvar_tys,
unique_type_id,
usage_site_span).finalize(cx)
}

View file

@ -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)
}

View file

@ -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);

View file

@ -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) {

View file

@ -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()
}

View file

@ -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

View file

@ -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 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()));
// 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);

View file

@ -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,

View file

@ -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,

View file

@ -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);
// 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 =
@ -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<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)
@ -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);
}
}
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Regression test for #25954: detect and reject a closure type that
// references itself.
use std::cell::{Cell, RefCell};
struct A<T: Fn()> {
x: RefCell<Option<T>>,
b: Cell<i32>,
}
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);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::sync::mpsc::channel;
use std::thread::spawn;
use std::marker::PhantomData;
struct Foo<T> {foo: PhantomData<T>}
fn main() {
let (tx, rx) = channel();
spawn(move || {
tx.send(Foo{ foo: PhantomData }); //~ ERROR E0282
});
}

View file

@ -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() { }