diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index fb6bac9ea06ae..b4b53c003ea2f 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -245,7 +245,7 @@ pub enum Vtable<'tcx, N> { /// Vtable automatically generated for a closure. The def ID is the ID /// of the closure expression. This is a `VtableImpl` in spirit, but the /// impl is generated by the compiler and does not appear in the source. - VtableClosure(ast::DefId, subst::Substs<'tcx>), + VtableClosure(VtableClosureData<'tcx, N>), /// Same as above, but for a fn pointer type with the given signature. VtableFnPointer(ty::Ty<'tcx>), @@ -268,7 +268,16 @@ pub struct VtableImplData<'tcx, N> { pub nested: Vec } -#[derive(Debug,Clone)] +#[derive(Clone, PartialEq, Eq)] +pub struct VtableClosureData<'tcx, N> { + pub closure_def_id: ast::DefId, + pub substs: subst::Substs<'tcx>, + /// Nested obligations. This can be non-empty if the closure + /// signature contains associated types. + pub nested: Vec +} + +#[derive(Debug, Clone)] pub struct VtableDefaultImplData { pub trait_def_id: ast::DefId, pub nested: Vec @@ -502,8 +511,8 @@ impl<'tcx, N> Vtable<'tcx, N> { VtableParam(n) => n, VtableBuiltin(i) => i.nested, VtableDefaultImpl(d) => d.nested, - VtableObject(_) | VtableFnPointer(..) | - VtableClosure(..) => vec![] + VtableClosure(c) => c.nested, + VtableObject(_) | VtableFnPointer(..) => vec![] } } @@ -524,7 +533,11 @@ impl<'tcx, N> Vtable<'tcx, N> { nested: d.nested.into_iter().map(f).collect() }), VtableFnPointer(f) => VtableFnPointer(f), - VtableClosure(d, s) => VtableClosure(d, s), + VtableClosure(c) => VtableClosure(VtableClosureData { + closure_def_id: c.closure_def_id, + substs: c.substs, + 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 7915f9a8bdf31..71946aa79ce3a 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -17,15 +17,15 @@ use super::ObligationCause; use super::PredicateObligation; use super::SelectionContext; use super::SelectionError; +use super::VtableClosureData; use super::VtableImplData; use super::util; use middle::infer; -use middle::subst::{Subst, Substs}; +use middle::subst::Subst; use middle::ty::{self, AsPredicate, ReferencesError, RegionEscape, HasProjectionTypes, ToPolyTraitRef, Ty}; use middle::ty_fold::{self, TypeFoldable, TypeFolder}; -use syntax::ast; use syntax::parse::token; use util::common::FN_OUTPUT_NAME; use util::ppaux::Repr; @@ -57,7 +57,7 @@ pub struct MismatchedProjectionTypes<'tcx> { enum ProjectionTyCandidate<'tcx> { ParamEnv(ty::PolyProjectionPredicate<'tcx>), Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>), - Closure(ast::DefId, Substs<'tcx>), + Closure(VtableClosureData<'tcx, PredicateObligation<'tcx>>), FnPointer(Ty<'tcx>), } @@ -162,11 +162,16 @@ fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext self_ty, &closure_type.sig, util::TupleArgumentsFlag::No); + // We don't have to normalize the return type here - this is only + // reached for TyClosure: Fn inputs where the closure kind is + // still unknown, which should only occur in typeck where the + // closure type is already normalized. let (ret_type, _) = infcx.replace_late_bound_regions_with_fresh_var( obligation.cause.span, infer::AssocTypeProjection(obligation.predicate.projection_ty.item_name), &ty::Binder(ret_type)); + debug!("consider_unification_despite_ambiguity: ret_type={:?}", ret_type.repr(selcx.tcx())); let origin = infer::RelateOutputImplTypes(obligation.cause.span); @@ -686,9 +691,9 @@ fn assemble_candidates_from_impls<'cx,'tcx>( selcx, obligation, obligation_trait_ref, candidate_set, data.object_ty); } - super::VtableClosure(closure_def_id, substs) => { + super::VtableClosure(data) => { candidate_set.vec.push( - ProjectionTyCandidate::Closure(closure_def_id, substs)); + ProjectionTyCandidate::Closure(data)); } super::VtableFnPointer(fn_type) => { candidate_set.vec.push( @@ -755,8 +760,8 @@ fn confirm_candidate<'cx,'tcx>( confirm_impl_candidate(selcx, obligation, impl_vtable) } - ProjectionTyCandidate::Closure(def_id, substs) => { - confirm_closure_candidate(selcx, obligation, def_id, &substs) + ProjectionTyCandidate::Closure(closure_vtable) => { + confirm_closure_candidate(selcx, obligation, closure_vtable) } ProjectionTyCandidate::FnPointer(fn_type) => { @@ -779,13 +784,24 @@ fn confirm_fn_pointer_candidate<'cx,'tcx>( fn confirm_closure_candidate<'cx,'tcx>( selcx: &mut SelectionContext<'cx,'tcx>, obligation: &ProjectionTyObligation<'tcx>, - closure_def_id: ast::DefId, - substs: &Substs<'tcx>) + vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>) -> (Ty<'tcx>, Vec>) { let closure_typer = selcx.closure_typer(); - let closure_type = closure_typer.closure_type(closure_def_id, substs); - confirm_callable_candidate(selcx, obligation, &closure_type.sig, util::TupleArgumentsFlag::No) + let closure_type = closure_typer.closure_type(vtable.closure_def_id, &vtable.substs); + let Normalized { + value: closure_type, + mut obligations + } = normalize_with_depth(selcx, + obligation.cause.clone(), + obligation.recursion_depth+1, + &closure_type); + let (ty, mut cc_obligations) = confirm_callable_candidate(selcx, + obligation, + &closure_type.sig, + util::TupleArgumentsFlag::No); + obligations.append(&mut cc_obligations); + (ty, obligations) } fn confirm_callable_candidate<'cx,'tcx>( @@ -797,7 +813,7 @@ fn confirm_callable_candidate<'cx,'tcx>( { let tcx = selcx.tcx(); - debug!("confirm_closure_candidate({},{})", + debug!("confirm_callable_candidate({},{})", obligation.repr(tcx), fn_sig.repr(tcx)); @@ -921,8 +937,8 @@ impl<'tcx> Repr<'tcx> for ProjectionTyCandidate<'tcx> { format!("ParamEnv({})", data.repr(tcx)), ProjectionTyCandidate::Impl(ref data) => format!("Impl({})", data.repr(tcx)), - ProjectionTyCandidate::Closure(ref a, ref b) => - format!("Closure(({},{}))", a.repr(tcx), b.repr(tcx)), + ProjectionTyCandidate::Closure(ref data) => + format!("Closure({})", data.repr(tcx)), ProjectionTyCandidate::FnPointer(a) => format!("FnPointer(({}))", a.repr(tcx)), } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 211839fbd25f9..8e2a90aa80884 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -31,7 +31,8 @@ use super::Selection; use super::SelectionResult; use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure, VtableFnPointer, VtableObject, VtableDefaultImpl}; -use super::{VtableImplData, VtableObjectData, VtableBuiltinData, VtableDefaultImplData}; +use super::{VtableImplData, VtableObjectData, VtableBuiltinData, + VtableClosureData, VtableDefaultImplData}; use super::object_safety; use super::util; @@ -355,7 +356,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; assert!(!substs.has_escaping_regions()); - let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs); + // It is OK to call the unnormalized variant here - this is only + // reached for TyClosure: Fn inputs where the closure kind is + // still unknown, which should only occur in typeck where the + // closure type is already normalized. + let closure_trait_ref = self.closure_trait_ref_unnormalized(obligation, + closure_def_id, + substs); + match self.confirm_poly_trait_refs(obligation.cause.clone(), obligation.predicate.to_poly_trait_ref(), closure_trait_ref) { @@ -2001,8 +2009,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ClosureCandidate(closure_def_id, substs) => { - try!(self.confirm_closure_candidate(obligation, closure_def_id, &substs)); - Ok(VtableClosure(closure_def_id, substs)) + let vtable_closure = + try!(self.confirm_closure_candidate(obligation, closure_def_id, &substs)); + Ok(VtableClosure(vtable_closure)) } BuiltinObjectCandidate => { @@ -2343,24 +2352,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, closure_def_id: ast::DefId, substs: &Substs<'tcx>) - -> Result<(),SelectionError<'tcx>> + -> Result>, + SelectionError<'tcx>> { debug!("confirm_closure_candidate({},{},{})", obligation.repr(self.tcx()), closure_def_id.repr(self.tcx()), substs.repr(self.tcx())); - let trait_ref = self.closure_trait_ref(obligation, - closure_def_id, - substs); + let Normalized { + value: trait_ref, + obligations + } = self.closure_trait_ref(obligation, closure_def_id, substs); - debug!("confirm_closure_candidate(closure_def_id={}, trait_ref={})", + debug!("confirm_closure_candidate(closure_def_id={}, trait_ref={}, obligations={})", closure_def_id.repr(self.tcx()), - trait_ref.repr(self.tcx())); + trait_ref.repr(self.tcx()), + obligations.repr(self.tcx())); + + try!(self.confirm_poly_trait_refs(obligation.cause.clone(), + obligation.predicate.to_poly_trait_ref(), + trait_ref)); - self.confirm_poly_trait_refs(obligation.cause.clone(), - obligation.predicate.to_poly_trait_ref(), - trait_ref) + Ok(VtableClosureData { + closure_def_id: closure_def_id, + substs: substs.clone(), + nested: obligations + }) } /// In the case of closure types and fn pointers, @@ -2819,11 +2837,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn closure_trait_ref(&self, - obligation: &TraitObligation<'tcx>, - closure_def_id: ast::DefId, - substs: &Substs<'tcx>) - -> ty::PolyTraitRef<'tcx> + fn closure_trait_ref_unnormalized(&mut self, + obligation: &TraitObligation<'tcx>, + closure_def_id: ast::DefId, + substs: &Substs<'tcx>) + -> ty::PolyTraitRef<'tcx> { let closure_type = self.closure_typer.closure_type(closure_def_id, substs); let ty::Binder((trait_ref, _)) = @@ -2832,7 +2850,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.predicate.0.self_ty(), // (1) &closure_type.sig, util::TupleArgumentsFlag::No); - // (1) Feels icky to skip the binder here, but OTOH we know // that the self-type is an unboxed closure type and hence is // in fact unparameterized (or at least does not reference any @@ -2842,6 +2859,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Binder(trait_ref) } + fn closure_trait_ref(&mut self, + obligation: &TraitObligation<'tcx>, + closure_def_id: ast::DefId, + substs: &Substs<'tcx>) + -> Normalized<'tcx, ty::PolyTraitRef<'tcx>> + { + let trait_ref = self.closure_trait_ref_unnormalized( + obligation, closure_def_id, substs); + + // A closure signature can contain associated types which + // must be normalized. + normalize_with_depth(self, + obligation.cause.clone(), + obligation.recursion_depth+1, + &trait_ref) + } + /// Returns the obligations that are implied by instantiating an /// impl or trait. The obligations are substituted and fully /// normalized. This is used when confirming an impl or default diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index a864ba4498fda..c1205d4a46d81 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -308,6 +308,12 @@ impl<'tcx, N> fmt::Debug for VtableImplData<'tcx, N> { } } +impl<'tcx, N> fmt::Debug for super::VtableClosureData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VtableClosure({:?})", self.closure_def_id) + } +} + impl<'tcx> fmt::Debug for super::VtableObjectData<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "VtableObject(...)") @@ -497,10 +503,8 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::Vtable<'tcx, N> { super::VtableDefaultImpl(ref t) => t.repr(tcx), - super::VtableClosure(ref d, ref s) => - format!("VtableClosure({},{})", - d.repr(tcx), - s.repr(tcx)), + super::VtableClosure(ref d) => + d.repr(tcx), super::VtableFnPointer(ref d) => format!("VtableFnPointer({})", @@ -529,6 +533,15 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::VtableImplData<'tcx, N> { } } +impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::VtableClosureData<'tcx, N> { + fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { + format!("VtableClosure(closure_def_id={}, substs={}, nested={})", + self.closure_def_id.repr(tcx), + self.substs.repr(tcx), + self.nested.repr(tcx)) + } +} + impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::VtableBuiltinData { fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { format!("VtableBuiltin(nested={})", diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 38391438298ee..cc91ccbfbd41e 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -479,6 +479,16 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData< } } +impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> { + fn fold_with>(&self, folder: &mut F) -> traits::VtableClosureData<'tcx, N> { + traits::VtableClosureData { + closure_def_id: self.closure_def_id, + substs: self.substs.fold_with(folder), + nested: self.nested.fold_with(folder), + } + } +} + impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableDefaultImplData { fn fold_with>(&self, folder: &mut F) -> traits::VtableDefaultImplData { traits::VtableDefaultImplData { @@ -501,8 +511,8 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> match *self { traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)), traits::VtableDefaultImpl(ref t) => traits::VtableDefaultImpl(t.fold_with(folder)), - traits::VtableClosure(d, ref s) => { - traits::VtableClosure(d, s.fold_with(folder)) + traits::VtableClosure(ref d) => { + traits::VtableClosure(d.fold_with(folder)) } traits::VtableFnPointer(ref d) => { traits::VtableFnPointer(d.fold_with(folder)) diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 0dc1cca85fb2c..7827771994f98 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -359,13 +359,13 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, Callee { bcx: bcx, data: Fn(llfn) } } - traits::VtableClosure(closure_def_id, substs) => { + traits::VtableClosure(vtable_closure) => { // The substitutions should have no type parameters remaining // after passing through fulfill_obligation let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap(); let llfn = closure::trans_closure_method(bcx.ccx(), - closure_def_id, - substs, + vtable_closure.closure_def_id, + vtable_closure.substs, MethodCallKey(method_call), bcx.fcx.param_substs, trait_closure_kind); @@ -716,7 +716,11 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, nested: _ }) => { emit_vtable_methods(ccx, id, substs, param_substs).into_iter() } - traits::VtableClosure(closure_def_id, substs) => { + traits::VtableClosure( + traits::VtableClosureData { + closure_def_id, + substs, + nested: _ }) => { let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap(); let llfn = closure::trans_closure_method(ccx, closure_def_id, diff --git a/src/test/run-pass/issue-25700-2.rs b/src/test/run-pass/issue-25700-2.rs new file mode 100644 index 0000000000000..3117e6f3681de --- /dev/null +++ b/src/test/run-pass/issue-25700-2.rs @@ -0,0 +1,31 @@ +// Copyright 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. + +pub trait Parser { + type Input; +} + +pub struct Iter(P, P::Input); + +pub struct Map(P, F); +impl Parser for Map where F: FnMut(P) { + type Input = u8; +} + +trait AstId { type Untyped; } +impl AstId for u32 { type Untyped = u32; } + +fn record_type(i: Id::Untyped) -> u8 { + Iter(Map(i, |_: Id::Untyped| {}), 42).1 +} + +pub fn main() { + assert_eq!(record_type::(3), 42); +}