diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 255680465ca7f..d369be0c06d42 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -49,7 +49,7 @@ pub use self::select::SelectionContext; pub use self::select::SelectionCache; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants -pub use self::util::elaborate_predicates; +pub use self::util::{elaborate_predicates, elaborate_super_predicates}; pub use self::util::get_vtable_index_of_object_method; pub use self::util::trait_ref_for_builtin_bound; pub use self::util::predicate_for_trait_def; @@ -416,7 +416,7 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi unnormalized_env); let predicates: Vec<_> = - util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.clone()) + util::elaborate_super_predicates(tcx, unnormalized_env.caller_bounds.clone()) .filter(|p| !p.is_global()) // (*) .collect(); diff --git a/src/librustc/middle/traits/object_safety.rs b/src/librustc/middle/traits/object_safety.rs index bd60d0a212252..269ab24afb387 100644 --- a/src/librustc/middle/traits/object_safety.rs +++ b/src/librustc/middle/traits/object_safety.rs @@ -18,7 +18,7 @@ //! - not have generic type parameters use super::supertraits; -use super::elaborate_predicates; +use super::elaborate_super_predicates; use middle::def_id::DefId; use middle::subst::{self, SelfSpace, TypeSpace}; @@ -195,7 +195,7 @@ fn generics_require_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>, let free_substs = tcx.construct_free_substs(generics, tcx.region_maps.node_extent(ast::DUMMY_NODE_ID)); let predicates = predicates.instantiate(tcx, &free_substs).predicates.into_vec(); - elaborate_predicates(tcx, predicates) + elaborate_super_predicates(tcx, predicates) .any(|predicate| { match predicate { ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => { diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index b0215675fca81..1170379e5c045 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1043,7 +1043,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if candidates.vec.is_empty() { try!(self.assemble_candidates_from_default_impls(obligation, &mut candidates)); } - debug!("candidate list size: {}", candidates.vec.len()); + debug!("candidate list: len={}, vec={:?}", candidates.vec.len(), candidates.vec); Ok(candidates) } @@ -1066,6 +1066,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => { return; } }; + debug!("assemble_candidates_from_projected_tys: obligation_type_space={:?}", + obligation.predicate.0.trait_ref.substs.types); + debug!("assemble_candidates_for_projected_tys: trait_def_id={:?}", trait_def_id); @@ -1094,8 +1097,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { skol_trait_predicate, skol_map); - let projection_trait_ref = match skol_trait_predicate.trait_ref.self_ty().sty { - ty::TyProjection(ref data) => &data.trait_ref, + let projection_trait_refs = match skol_trait_predicate.trait_ref.self_ty().sty { + ty::TyProjection(ref data) => data.parent_trait_refs(), _ => { self.tcx().sess.span_bug( obligation.cause.span, @@ -1105,42 +1108,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }; debug!("match_projection_obligation_against_bounds_from_trait: \ - projection_trait_ref={:?}", - projection_trait_ref); - - let trait_predicates = self.tcx().lookup_predicates(projection_trait_ref.def_id); - let bounds = trait_predicates.instantiate(self.tcx(), projection_trait_ref.substs); - debug!("match_projection_obligation_against_bounds_from_trait: \ - bounds={:?}", - bounds); - - let matching_bound = - util::elaborate_predicates(self.tcx(), bounds.predicates.into_vec()) - .filter_to_traits() - .find( - |bound| self.infcx.probe( - |_| self.match_projection(obligation, - bound.clone(), - skol_trait_predicate.trait_ref.clone(), - &skol_map, - snapshot))); - - debug!("match_projection_obligation_against_bounds_from_trait: \ - matching_bound={:?}", - matching_bound); - match matching_bound { - None => false, - Some(bound) => { - // Repeat the successful match, if any, this time outside of a probe. - let result = self.match_projection(obligation, - bound, - skol_trait_predicate.trait_ref.clone(), - &skol_map, - snapshot); - assert!(result); - true + projection_trait_refs={:?}", + projection_trait_refs); + + for projection_trait_ref in projection_trait_refs { + let trait_predicates = self.tcx().lookup_predicates(projection_trait_ref.def_id); + let bounds = trait_predicates.instantiate(self.tcx(), projection_trait_ref.substs); + debug!("match_projection_obligation_against_bounds_from_trait: \ + bounds={:?}", + bounds); + + let matching_bound = + util::elaborate_predicates(self.tcx(), bounds.predicates.into_vec()) + .filter_to_traits() + .find( + |bound| self.infcx.probe( + |_| self.match_projection(obligation, + bound.clone(), + skol_trait_predicate.trait_ref.clone(), + &skol_map, + snapshot))); + + debug!("match_projection_obligation_against_bounds_from_trait: \ + matching_bound={:?}", + matching_bound); + match matching_bound { + None => continue, + Some(bound) => { + // Repeat the successful match, if any, this time outside of a probe. + let result = self.match_projection(obligation, + bound, + skol_trait_predicate.trait_ref.clone(), + &skol_map, + snapshot); + assert!(result); + return true; + } } } + false } fn match_projection(&mut self, diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index c50c9e9765d25..7b00b10af5f5c 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -69,6 +69,12 @@ impl<'a,'tcx> PredicateSet<'a,'tcx> { // `Elaboration` iterator /////////////////////////////////////////////////////////////////////////// +#[derive(Debug, Clone, Copy)] +enum ElaboratorMode { + Supertraits, + All, +} + /// "Elaboration" is the process of identifying all the predicates that /// are implied by a source predicate. Currently this basically means /// walking the "supertraits" and other similar assumptions. For @@ -80,35 +86,61 @@ pub struct Elaborator<'cx, 'tcx:'cx> { tcx: &'cx ty::ctxt<'tcx>, stack: Vec>, visited: PredicateSet<'cx,'tcx>, + mode: ElaboratorMode, + allowed_iterations: usize, } -pub fn elaborate_trait_ref<'cx, 'tcx>( +fn elaborate_trait_ref_with_mode<'cx, 'tcx>( tcx: &'cx ty::ctxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>) + trait_ref: ty::PolyTraitRef<'tcx>, + mode: ElaboratorMode) -> Elaborator<'cx, 'tcx> { - elaborate_predicates(tcx, vec![trait_ref.to_predicate()]) + elaborate_predicates_with_mode(tcx, vec![trait_ref.to_predicate()], mode) } -pub fn elaborate_trait_refs<'cx, 'tcx>( +fn elaborate_trait_refs_with_mode<'cx, 'tcx>( tcx: &'cx ty::ctxt<'tcx>, - trait_refs: &[ty::PolyTraitRef<'tcx>]) + trait_refs: &[ty::PolyTraitRef<'tcx>], + mode: ElaboratorMode) -> Elaborator<'cx, 'tcx> { let predicates = trait_refs.iter() .map(|trait_ref| trait_ref.to_predicate()) .collect(); - elaborate_predicates(tcx, predicates) + elaborate_predicates_with_mode(tcx, predicates, mode) +} + +// FIXME @reviewer: should this return a different type, and should ElaboratorMode above be +// eschewed? +pub fn elaborate_super_predicates<'cx, 'tcx>( + tcx: &'cx ty::ctxt<'tcx>, + predicates: Vec>) + -> Elaborator<'cx, 'tcx> +{ + elaborate_predicates_with_mode(tcx, predicates, ElaboratorMode::Supertraits) } pub fn elaborate_predicates<'cx, 'tcx>( tcx: &'cx ty::ctxt<'tcx>, - mut predicates: Vec>) + predicates: Vec>) -> Elaborator<'cx, 'tcx> { + assert!(*tcx.collection_finished.borrow(), "should not elaborate non-supertrait predicates before collection is finished."); // cruft + elaborate_predicates_with_mode(tcx, predicates, ElaboratorMode::All) +} + +fn elaborate_predicates_with_mode<'cx, 'tcx>( + tcx: &'cx ty::ctxt<'tcx>, + mut predicates: Vec>, + mode: ElaboratorMode) + -> Elaborator<'cx, 'tcx> +{ + debug!("elaborate_predicates_with_mode(predicates={:?}, mode={:?})", predicates, mode); let mut visited = PredicateSet::new(tcx); predicates.retain(|pred| visited.insert(pred)); - Elaborator { tcx: tcx, stack: predicates, visited: visited } + Elaborator { tcx: tcx, stack: predicates, visited: visited, mode: mode, + allowed_iterations: tcx.sess.recursion_limit.get() } } impl<'cx, 'tcx> Elaborator<'cx, 'tcx> { @@ -117,19 +149,38 @@ impl<'cx, 'tcx> Elaborator<'cx, 'tcx> { } fn push(&mut self, predicate: &ty::Predicate<'tcx>) { + // FIXME this is a hack to get around how hard it is to handle streams of associated type + // bounds when elaborating all predicates. The reason why this is hard, you could have a + // trait like this: + // + // trait A { type B: A; } + // + // And there's no way in this iterator to know whether or not there's an obligation of the + // form <... <<::B as A>::B as A>:: ...>::B: SomeOtherTrait that the calling + // code is trying to fulfill. We don't know how deep to go down the rabbit hole from here. + if self.allowed_iterations == 0 { + return; + } else { + self.allowed_iterations -= 1; + } + match *predicate { ty::Predicate::Trait(ref data) => { // Predicates declared on the trait. - let predicates = self.tcx.lookup_super_predicates(data.def_id()); + let (predicates, mode_string) = match self.mode { // mode_string is cruft + ElaboratorMode::All => + (self.tcx.lookup_predicates(data.def_id()), "predicates"), + ElaboratorMode::Supertraits => + (self.tcx.lookup_super_predicates(data.def_id()), "super_predicates") + }; let mut predicates: Vec<_> = predicates.predicates .iter() .map(|p| p.subst_supertrait(self.tcx, &data.to_poly_trait_ref())) .collect(); - - debug!("super_predicates: data={:?} predicates={:?}", - data, predicates); + debug!("{}: data={:?} predicates={:?}", + mode_string, data, predicates); // Only keep those bounds that we haven't already // seen. This is necessary to prevent infinite @@ -209,14 +260,14 @@ pub fn supertraits<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Supertraits<'cx, 'tcx> { - elaborate_trait_ref(tcx, trait_ref).filter_to_traits() + elaborate_trait_ref_with_mode(tcx, trait_ref, ElaboratorMode::Supertraits).filter_to_traits() } pub fn transitive_bounds<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>, bounds: &[ty::PolyTraitRef<'tcx>]) -> Supertraits<'cx, 'tcx> { - elaborate_trait_refs(tcx, bounds).filter_to_traits() + elaborate_trait_refs_with_mode(tcx, bounds, ElaboratorMode::Supertraits).filter_to_traits() } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index cee651743ca86..86cab1972e98a 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -257,6 +257,8 @@ pub struct ctxt<'tcx> { pub trait_defs: RefCell>>, pub adt_defs: RefCell>>, + pub collection_finished: RefCell, // cruft + /// Maps from the def-id of an item (trait/struct/enum/fn) to its /// associated predicates. pub predicates: RefCell>>, @@ -485,6 +487,7 @@ impl<'tcx> ctxt<'tcx> { let common_types = CommonTypes::new(&arenas.type_, &interner); tls::enter(ctxt { + collection_finished: RefCell::new(false), // cruft arenas: arenas, interner: interner, substs_interner: RefCell::new(FnvHashMap()), diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs index 6f72d3df07e37..80322a05cd36d 100644 --- a/src/librustc/middle/ty/sty.rs +++ b/src/librustc/middle/ty/sty.rs @@ -438,6 +438,19 @@ impl<'tcx> ProjectionTy<'tcx> { pub fn sort_key(&self) -> (DefId, Name) { (self.trait_ref.def_id, self.item_name) } + // FIXME @reviewer: This feels weird. I have no idea if it should feel weird or not, though. + pub fn parent_trait_refs(&self) -> Vec> { + let mut trait_refs = Vec::new(); + let mut current = self.trait_ref; + loop { + trait_refs.push(current); + match current.self_ty().sty { + TyProjection(ref data) => { current = data.trait_ref }, + _ => break + } + } + trait_refs + } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index 0517769356f75..b2db2da5e2378 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -392,7 +392,7 @@ impl<'tcx> ty::ctxt<'tcx> { assert!(!erased_self_ty.has_escaping_regions()); - traits::elaborate_predicates(self, predicates) + traits::elaborate_super_predicates(self, predicates) .filter_map(|predicate| { match predicate { ty::Predicate::Projection(..) | diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b7ce91d331723..920e54cd14b15 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -109,6 +109,8 @@ pub fn collect_item_types(tcx: &ty::ctxt) { let mut visitor = CollectItemTypesVisitor{ ccx: ccx }; ccx.tcx.map.krate().visit_all_items(&mut visitor); + + *tcx.collection_finished.borrow_mut() = true; } /////////////////////////////////////////////////////////////////////////// diff --git a/src/test/run-pass/elaborate_self_projection_projection.rs b/src/test/run-pass/elaborate_self_projection_projection.rs new file mode 100644 index 0000000000000..3f758b71484a0 --- /dev/null +++ b/src/test/run-pass/elaborate_self_projection_projection.rs @@ -0,0 +1,39 @@ +trait D {} + +trait C { + type D; +} + +trait B { + type C: Default; + fn c(&self) -> Self::C { Default::default() } +} + +trait A + where ::C: C, <::C as C>::D: D +{ + type B: B + Default; + fn b(&self) -> Self::B { Default::default() } +} + + +trait QQ: A {} + + +impl D for () {} +impl C for i8 { type D = (); } +impl B for i16 { type C = i8; } +impl A for i32 { type B = i16; } +impl QQ for i32 {} + +fn accept_a(a: T) { + let _ = a.b().c(); +} +fn accept_qq(a: T) { + let _ = a.b().c(); +} + +pub fn main() { + accept_a(0); + accept_qq(0); +}