Skip to content

Commit

Permalink
Distinguish elaboration vs supertrait expansion. Elaboration considers
Browse files Browse the repository at this point in the history
all where-clauses, but supertrait considers a more limited
set. Supertrait expansion is suitable for virtual tables and associated
type inclusion, elaboration for more general reasoning.

Fixes rust-lang#20671.
  • Loading branch information
nikomatsakis committed Mar 6, 2015
1 parent 1fe8f22 commit 9990b3c
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 40 deletions.
3 changes: 3 additions & 0 deletions src/librustc/middle/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
* type inference.
*/

debug!("process_predicate(obligation={})", obligation.repr(selcx.tcx()));
let tcx = selcx.tcx();
match obligation.predicate {
ty::Predicate::Trait(ref data) => {
Expand All @@ -328,6 +329,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
false
}
Ok(Some(s)) => {
debug!("process_predicate: new_obligations={}", s.repr(selcx.tcx()));
s.map_move_nested(|p| new_obligations.push(p));
true
}
Expand Down Expand Up @@ -398,6 +400,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
result.repr(tcx));
match result {
Ok(Some(obligations)) => {
debug!("process_predicate: new_obligations={}", obligations.repr(selcx.tcx()));
new_obligations.extend(obligations.into_iter());
true
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ 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_trait_refs;
pub use self::util::get_vtable_index_of_object_method;
pub use self::util::trait_ref_for_builtin_bound;
pub use self::util::superpredicates;
pub use self::util::supertraits;
pub use self::util::Supertraits;
pub use self::util::transitive_bounds;
pub use self::util::supertraits_many;
pub use self::util::upcast;

mod coherence;
Expand Down
26 changes: 20 additions & 6 deletions src/librustc/middle/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,12 +1000,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

let caller_trait_refs: Vec<_> =
self.param_env().caller_bounds.iter()
.filter_map(|o| o.to_opt_poly_trait_ref())
.collect();
.filter_map(|o| o.to_opt_poly_trait_ref())
.collect();

let all_bounds =
util::transitive_bounds(
self.tcx(), &caller_trait_refs[..]);
util::elaborate_trait_refs(self.tcx(), &caller_trait_refs)
.filter_to_traits();

let matching_bounds =
all_bounds.filter(
Expand Down Expand Up @@ -1323,8 +1323,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(&ImplCandidate(..), &ParamCandidate(..)) |
(&ClosureCandidate(..), &ParamCandidate(..)) |
(&FnPointerCandidate(..), &ParamCandidate(..)) |
(&BuiltinObjectCandidate(..), &ParamCandidate(_)) |
(&BuiltinCandidate(..), &ParamCandidate(..)) => {
(&BuiltinObjectCandidate(..), &ParamCandidate(_)) => {
// We basically prefer always prefer to use a
// where-clause over another option. Where clauses
// impose the burden of finding the exact match onto
Expand All @@ -1333,6 +1332,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// #18453.
true
}
(&ParamCandidate(..), &BuiltinCandidate(..)) => {
// Builtin candidates only trigger for things like
// `Sized` and `Copy` applied to structural types; in
// that case, the param candidate cannot be any
// different from the builtin rules, so prefer the
// builtin rules. It is important to do this because
// otherwise if you have
//
// where (A, B) : Sized
//
// in your environment, for example, it will unduly
// influence matches of other tuples, forcing their
// argument types to be A and B.
true
}
(&DefaultImplCandidate(_), _) => {
// Prefer other candidates over default implementations.
self.tcx().sess.bug(
Expand Down
106 changes: 77 additions & 29 deletions src/librustc/middle/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,27 +65,32 @@ impl<'a,'tcx> PredicateSet<'a,'tcx> {

///////////////////////////////////////////////////////////////////////////
// `Elaboration` iterator
///////////////////////////////////////////////////////////////////////////
//
// "Elaboration" is the process of identifying all the predicates that
// are implied by a source predicate. This is done by expanding out
// where clauses declared on a trait. Example:
//
// ```
// trait Foo<T> : Bar<T> where T : Baz { .. }
// trait Bar<T> { .. }
// trait Baz { .. }
// ```
//
// Elaborating `X:Foo<Y>` would yield `[X:Foo<Y>, X:Bar<Y>, Y:Baz,
// Y:Sized]`. If you only want the supertraits (`X:Bar<Y>`, here),
// see the supertrait iterator below.

/// "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
/// example, if we know that `T : Ord`, the elaborator would deduce
/// that `T : PartialOrd` holds as well. Similarly, if we have `trait
/// Foo : 'static`, and we know that `T : Foo`, then we know that `T :
/// 'static`.
/// Elaborates a set of predicates into other predicates that are
/// implied by the initial set.
pub struct Elaborator<'cx, 'tcx:'cx> {
tcx: &'cx ty::ctxt<'tcx>,
stack: Vec<ty::Predicate<'tcx>>,
visited: PredicateSet<'cx,'tcx>,
}

pub fn elaborate_trait_ref<'cx, 'tcx>(
tcx: &'cx ty::ctxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
-> Elaborator<'cx, 'tcx>
{
elaborate_predicates(tcx, vec![trait_ref.as_predicate()])
/// If true, we only elaborate `A:Foo` to `A:Bar` where `Bar` is a
/// supertrait of `Foo`. Otherwise, we consider all where-clauses
/// declared on `Foo`.
supertraits_only: bool,
}

pub fn elaborate_trait_refs<'cx, 'tcx>(
Expand All @@ -101,15 +106,24 @@ pub fn elaborate_trait_refs<'cx, 'tcx>(

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>
{
let mut visited = PredicateSet::new(tcx);
predicates.retain(|pred| visited.insert(pred));
Elaborator { tcx: tcx, stack: predicates, visited: visited }
Elaborator::new(tcx, predicates, false)
}

impl<'cx, 'tcx> Elaborator<'cx, 'tcx> {
fn new(tcx: &'cx ty::ctxt<'tcx>,
mut predicates: Vec<ty::Predicate<'tcx>>,
supertraits_only: bool)
-> Elaborator<'cx, 'tcx>
{
let mut visited = PredicateSet::new(tcx);
predicates.retain(|pred| visited.insert(pred));
Elaborator { tcx: tcx, stack: predicates,
visited: visited, supertraits_only: supertraits_only }
}

pub fn filter_to_traits(self) -> FilterToTraits<Elaborator<'cx, 'tcx>> {
FilterToTraits::new(self)
}
Expand All @@ -118,7 +132,11 @@ impl<'cx, 'tcx> Elaborator<'cx, 'tcx> {
match *predicate {
ty::Predicate::Trait(ref data) => {
// Predicates declared on the trait.
let predicates = ty::lookup_super_predicates(self.tcx, data.def_id());
let predicates = if self.supertraits_only {
ty::lookup_super_predicates(self.tcx, data.def_id())
} else {
ty::lookup_predicates(self.tcx, data.def_id())
};

let mut predicates: Vec<_> =
predicates.predicates
Expand Down Expand Up @@ -191,27 +209,55 @@ impl<'cx, 'tcx> Iterator for Elaborator<'cx, 'tcx> {

///////////////////////////////////////////////////////////////////////////
// Supertrait iterator
///////////////////////////////////////////////////////////////////////////
//
// Expanding out the set of supertraits is a subset of the full elaboration
// described above. The difference can be illustrated easily with this
// example:
//
// ```
// trait Foo<T> : Bar<T> where T : Baz { .. }
// trait Bar<T> { .. }
// trait Baz { .. }
// ```
//
// Given the predicate `X:Foo<Y>`, supertrait expansion would yield
// `[X:Foo<Y>, X:Bar<Y>]`, but the full elaboration would yield
// `[X:Foo<Y>, X:Bar<Y>, Y:Baz, Y:Sized]`.
//
// Supertraits are the correct choice for vtables and objects (which
// only contain supertraits). Another time when you want supertraits
// is when elaborating the set of associated items contained by a
// trait, which includes only those items defined in supertraits, not
// arbitrary random traits.

pub type Supertraits<'cx, 'tcx> = FilterToTraits<Elaborator<'cx, 'tcx>>;

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()
Elaborator::new(tcx, vec![trait_ref.as_predicate()], true).filter_to_traits()
}

pub fn transitive_bounds<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
bounds: &[ty::PolyTraitRef<'tcx>])
-> Supertraits<'cx, 'tcx>
pub fn superpredicates<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
trait_refs: Vec<ty::Predicate<'tcx>>)
-> Elaborator<'cx, 'tcx>
{
elaborate_trait_refs(tcx, bounds).filter_to_traits()
Elaborator::new(tcx, trait_refs, true)
}

pub fn supertraits_many<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
trait_refs: &[ty::PolyTraitRef<'tcx>])
-> Supertraits<'cx, 'tcx>
{
let predicates = trait_refs.iter()
.map(|trait_ref| trait_ref.as_predicate())
.collect();
Elaborator::new(tcx, predicates, true).filter_to_traits()
}

///////////////////////////////////////////////////////////////////////////
// Other
///////////////////////////////////////////////////////////////////////////

/// A filter around an iterator of predicates that makes it yield up
/// just trait references.
Expand Down Expand Up @@ -377,15 +423,17 @@ pub fn upcast<'tcx>(tcx: &ty::ctxt<'tcx>,
pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
object_trait_ref: ty::PolyTraitRef<'tcx>,
trait_def_id: ast::DefId,
method_offset_in_trait: uint) -> uint {
method_offset_in_trait: uint)
-> uint
{
// We need to figure the "real index" of the method in a
// listing of all the methods of an object. We do this by
// iterating down the supertraits of the object's trait until
// we find the trait the method came from, counting up the
// methods from them.
let mut method_count = 0;

for bound_ref in transitive_bounds(tcx, &[object_trait_ref]) {
for bound_ref in supertraits(tcx, object_trait_ref) {
if bound_ref.def_id() == trait_def_id {
break;
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5899,7 +5899,7 @@ pub fn each_bound_trait_and_supertraits<'tcx, F>(tcx: &ctxt<'tcx>,
-> bool where
F: FnMut(PolyTraitRef<'tcx>) -> bool,
{
for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
for bound_trait_ref in traits::supertraits_many(tcx, bounds) {
if !f(bound_trait_ref) {
return false;
}
Expand Down Expand Up @@ -5928,7 +5928,7 @@ pub fn required_region_bounds<'tcx>(tcx: &ctxt<'tcx>,

assert!(!erased_self_ty.has_escaping_regions());

traits::elaborate_predicates(tcx, predicates)
traits::superpredicates(tcx, predicates)
.filter_map(|predicate| {
match predicate {
ty::Predicate::Projection(..) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,7 @@ fn associated_path_def_to_ty<'tcx>(this: &AstConv<'tcx>,
}

let mut suitable_bounds: Vec<_> =
traits::transitive_bounds(tcx, &bounds)
traits::supertraits_many(tcx, &bounds)
.filter(|b| this.trait_defines_associated_type_named(b.def_id(), assoc_name))
.collect();

Expand Down
4 changes: 3 additions & 1 deletion src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
debug!("elaborate_bounds(bounds={})", bounds.repr(self.tcx()));

let tcx = self.tcx();
for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
for bound_trait_ref in
traits::elaborate_trait_refs(tcx, bounds).filter_map(|tr| tr.to_opt_poly_trait_ref())
{
let (pos, method) = match trait_method(tcx,
bound_trait_ref.def_id(),
self.method_name) {
Expand Down
27 changes: 27 additions & 0 deletions src/test/run-pass/trait-infer-from-where-clause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2013 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.

// Tests a case where we need to consider the where-clauses of `Foo`,
// and not just its supetraits, to infer that `A:Foo` holds.
// Issue #20671.

trait Foo<T> {
fn foo(&self) -> &T;
}

trait Bar<A> where A: Foo<Self> {
fn dummy(&self, a: A);
}

fn foobar<A, B: Bar<A>>(a: &A) -> &B {
a.foo()
}

fn main() { }

0 comments on commit 9990b3c

Please sign in to comment.