Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elaborate predicates on associated types' associated types (and so on) #30704

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 => {
Expand Down
80 changes: 43 additions & 37 deletions src/librustc/middle/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

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

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
79 changes: 65 additions & 14 deletions src/librustc/middle/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -80,35 +86,61 @@ pub struct Elaborator<'cx, 'tcx:'cx> {
tcx: &'cx ty::ctxt<'tcx>,
stack: Vec<ty::Predicate<'tcx>>,
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<ty::Predicate<'tcx>>)
-> 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<ty::Predicate<'tcx>>)
predicates: Vec<ty::Predicate<'tcx>>)
-> 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<ty::Predicate<'tcx>>,
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> {
Expand All @@ -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 <... <<<Self::B as A>::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
Expand Down Expand Up @@ -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()
}

///////////////////////////////////////////////////////////////////////////
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ pub struct ctxt<'tcx> {
pub trait_defs: RefCell<DefIdMap<&'tcx ty::TraitDef<'tcx>>>,
pub adt_defs: RefCell<DefIdMap<ty::AdtDefMaster<'tcx>>>,

pub collection_finished: RefCell<bool>, // cruft

/// Maps from the def-id of an item (trait/struct/enum/fn) to its
/// associated predicates.
pub predicates: RefCell<DefIdMap<GenericPredicates<'tcx>>>,
Expand Down Expand Up @@ -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()),
Expand Down
13 changes: 13 additions & 0 deletions src/librustc/middle/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ty::TraitRef<'tcx>> {
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)]
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..) |
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

///////////////////////////////////////////////////////////////////////////
Expand Down
39 changes: 39 additions & 0 deletions src/test/run-pass/elaborate_self_projection_projection.rs
Original file line number Diff line number Diff line change
@@ -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 <Self::B as B>::C: C, <<Self::B as B>::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<T: A>(a: T) {
let _ = a.b().c();
}
fn accept_qq<T: QQ>(a: T) {
let _ = a.b().c();
}

pub fn main() {
accept_a(0);
accept_qq(0);
}