Skip to content

Commit 9990b3c

Browse files
committed
Distinguish elaboration vs supertrait expansion. Elaboration considers
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.
1 parent 1fe8f22 commit 9990b3c

File tree

8 files changed

+136
-40
lines changed

8 files changed

+136
-40
lines changed

src/librustc/middle/traits/fulfill.rs

+3
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
319319
* type inference.
320320
*/
321321

322+
debug!("process_predicate(obligation={})", obligation.repr(selcx.tcx()));
322323
let tcx = selcx.tcx();
323324
match obligation.predicate {
324325
ty::Predicate::Trait(ref data) => {
@@ -328,6 +329,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
328329
false
329330
}
330331
Ok(Some(s)) => {
332+
debug!("process_predicate: new_obligations={}", s.repr(selcx.tcx()));
331333
s.map_move_nested(|p| new_obligations.push(p));
332334
true
333335
}
@@ -398,6 +400,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
398400
result.repr(tcx));
399401
match result {
400402
Ok(Some(obligations)) => {
403+
debug!("process_predicate: new_obligations={}", obligations.repr(selcx.tcx()));
401404
new_obligations.extend(obligations.into_iter());
402405
true
403406
}

src/librustc/middle/traits/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ pub use self::select::SelectionCache;
4343
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
4444
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
4545
pub use self::util::elaborate_predicates;
46+
pub use self::util::elaborate_trait_refs;
4647
pub use self::util::get_vtable_index_of_object_method;
4748
pub use self::util::trait_ref_for_builtin_bound;
49+
pub use self::util::superpredicates;
4850
pub use self::util::supertraits;
4951
pub use self::util::Supertraits;
50-
pub use self::util::transitive_bounds;
52+
pub use self::util::supertraits_many;
5153
pub use self::util::upcast;
5254

5355
mod coherence;

src/librustc/middle/traits/select.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -1000,12 +1000,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10001000

10011001
let caller_trait_refs: Vec<_> =
10021002
self.param_env().caller_bounds.iter()
1003-
.filter_map(|o| o.to_opt_poly_trait_ref())
1004-
.collect();
1003+
.filter_map(|o| o.to_opt_poly_trait_ref())
1004+
.collect();
10051005

10061006
let all_bounds =
1007-
util::transitive_bounds(
1008-
self.tcx(), &caller_trait_refs[..]);
1007+
util::elaborate_trait_refs(self.tcx(), &caller_trait_refs)
1008+
.filter_to_traits();
10091009

10101010
let matching_bounds =
10111011
all_bounds.filter(
@@ -1323,8 +1323,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13231323
(&ImplCandidate(..), &ParamCandidate(..)) |
13241324
(&ClosureCandidate(..), &ParamCandidate(..)) |
13251325
(&FnPointerCandidate(..), &ParamCandidate(..)) |
1326-
(&BuiltinObjectCandidate(..), &ParamCandidate(_)) |
1327-
(&BuiltinCandidate(..), &ParamCandidate(..)) => {
1326+
(&BuiltinObjectCandidate(..), &ParamCandidate(_)) => {
13281327
// We basically prefer always prefer to use a
13291328
// where-clause over another option. Where clauses
13301329
// impose the burden of finding the exact match onto
@@ -1333,6 +1332,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13331332
// #18453.
13341333
true
13351334
}
1335+
(&ParamCandidate(..), &BuiltinCandidate(..)) => {
1336+
// Builtin candidates only trigger for things like
1337+
// `Sized` and `Copy` applied to structural types; in
1338+
// that case, the param candidate cannot be any
1339+
// different from the builtin rules, so prefer the
1340+
// builtin rules. It is important to do this because
1341+
// otherwise if you have
1342+
//
1343+
// where (A, B) : Sized
1344+
//
1345+
// in your environment, for example, it will unduly
1346+
// influence matches of other tuples, forcing their
1347+
// argument types to be A and B.
1348+
true
1349+
}
13361350
(&DefaultImplCandidate(_), _) => {
13371351
// Prefer other candidates over default implementations.
13381352
self.tcx().sess.bug(

src/librustc/middle/traits/util.rs

+77-29
Original file line numberDiff line numberDiff line change
@@ -65,27 +65,32 @@ impl<'a,'tcx> PredicateSet<'a,'tcx> {
6565

6666
///////////////////////////////////////////////////////////////////////////
6767
// `Elaboration` iterator
68-
///////////////////////////////////////////////////////////////////////////
68+
//
69+
// "Elaboration" is the process of identifying all the predicates that
70+
// are implied by a source predicate. This is done by expanding out
71+
// where clauses declared on a trait. Example:
72+
//
73+
// ```
74+
// trait Foo<T> : Bar<T> where T : Baz { .. }
75+
// trait Bar<T> { .. }
76+
// trait Baz { .. }
77+
// ```
78+
//
79+
// Elaborating `X:Foo<Y>` would yield `[X:Foo<Y>, X:Bar<Y>, Y:Baz,
80+
// Y:Sized]`. If you only want the supertraits (`X:Bar<Y>`, here),
81+
// see the supertrait iterator below.
6982

70-
/// "Elaboration" is the process of identifying all the predicates that
71-
/// are implied by a source predicate. Currently this basically means
72-
/// walking the "supertraits" and other similar assumptions. For
73-
/// example, if we know that `T : Ord`, the elaborator would deduce
74-
/// that `T : PartialOrd` holds as well. Similarly, if we have `trait
75-
/// Foo : 'static`, and we know that `T : Foo`, then we know that `T :
76-
/// 'static`.
83+
/// Elaborates a set of predicates into other predicates that are
84+
/// implied by the initial set.
7785
pub struct Elaborator<'cx, 'tcx:'cx> {
7886
tcx: &'cx ty::ctxt<'tcx>,
7987
stack: Vec<ty::Predicate<'tcx>>,
8088
visited: PredicateSet<'cx,'tcx>,
81-
}
8289

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

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

102107
pub fn elaborate_predicates<'cx, 'tcx>(
103108
tcx: &'cx ty::ctxt<'tcx>,
104-
mut predicates: Vec<ty::Predicate<'tcx>>)
109+
predicates: Vec<ty::Predicate<'tcx>>)
105110
-> Elaborator<'cx, 'tcx>
106111
{
107-
let mut visited = PredicateSet::new(tcx);
108-
predicates.retain(|pred| visited.insert(pred));
109-
Elaborator { tcx: tcx, stack: predicates, visited: visited }
112+
Elaborator::new(tcx, predicates, false)
110113
}
111114

112115
impl<'cx, 'tcx> Elaborator<'cx, 'tcx> {
116+
fn new(tcx: &'cx ty::ctxt<'tcx>,
117+
mut predicates: Vec<ty::Predicate<'tcx>>,
118+
supertraits_only: bool)
119+
-> Elaborator<'cx, 'tcx>
120+
{
121+
let mut visited = PredicateSet::new(tcx);
122+
predicates.retain(|pred| visited.insert(pred));
123+
Elaborator { tcx: tcx, stack: predicates,
124+
visited: visited, supertraits_only: supertraits_only }
125+
}
126+
113127
pub fn filter_to_traits(self) -> FilterToTraits<Elaborator<'cx, 'tcx>> {
114128
FilterToTraits::new(self)
115129
}
@@ -118,7 +132,11 @@ impl<'cx, 'tcx> Elaborator<'cx, 'tcx> {
118132
match *predicate {
119133
ty::Predicate::Trait(ref data) => {
120134
// Predicates declared on the trait.
121-
let predicates = ty::lookup_super_predicates(self.tcx, data.def_id());
135+
let predicates = if self.supertraits_only {
136+
ty::lookup_super_predicates(self.tcx, data.def_id())
137+
} else {
138+
ty::lookup_predicates(self.tcx, data.def_id())
139+
};
122140

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

192210
///////////////////////////////////////////////////////////////////////////
193211
// Supertrait iterator
194-
///////////////////////////////////////////////////////////////////////////
212+
//
213+
// Expanding out the set of supertraits is a subset of the full elaboration
214+
// described above. The difference can be illustrated easily with this
215+
// example:
216+
//
217+
// ```
218+
// trait Foo<T> : Bar<T> where T : Baz { .. }
219+
// trait Bar<T> { .. }
220+
// trait Baz { .. }
221+
// ```
222+
//
223+
// Given the predicate `X:Foo<Y>`, supertrait expansion would yield
224+
// `[X:Foo<Y>, X:Bar<Y>]`, but the full elaboration would yield
225+
// `[X:Foo<Y>, X:Bar<Y>, Y:Baz, Y:Sized]`.
226+
//
227+
// Supertraits are the correct choice for vtables and objects (which
228+
// only contain supertraits). Another time when you want supertraits
229+
// is when elaborating the set of associated items contained by a
230+
// trait, which includes only those items defined in supertraits, not
231+
// arbitrary random traits.
195232

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

198235
pub fn supertraits<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
199236
trait_ref: ty::PolyTraitRef<'tcx>)
200237
-> Supertraits<'cx, 'tcx>
201238
{
202-
elaborate_trait_ref(tcx, trait_ref).filter_to_traits()
239+
Elaborator::new(tcx, vec![trait_ref.as_predicate()], true).filter_to_traits()
203240
}
204241

205-
pub fn transitive_bounds<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
206-
bounds: &[ty::PolyTraitRef<'tcx>])
207-
-> Supertraits<'cx, 'tcx>
242+
pub fn superpredicates<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
243+
trait_refs: Vec<ty::Predicate<'tcx>>)
244+
-> Elaborator<'cx, 'tcx>
208245
{
209-
elaborate_trait_refs(tcx, bounds).filter_to_traits()
246+
Elaborator::new(tcx, trait_refs, true)
247+
}
248+
249+
pub fn supertraits_many<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
250+
trait_refs: &[ty::PolyTraitRef<'tcx>])
251+
-> Supertraits<'cx, 'tcx>
252+
{
253+
let predicates = trait_refs.iter()
254+
.map(|trait_ref| trait_ref.as_predicate())
255+
.collect();
256+
Elaborator::new(tcx, predicates, true).filter_to_traits()
210257
}
211258

212259
///////////////////////////////////////////////////////////////////////////
213260
// Other
214-
///////////////////////////////////////////////////////////////////////////
215261

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

388-
for bound_ref in transitive_bounds(tcx, &[object_trait_ref]) {
436+
for bound_ref in supertraits(tcx, object_trait_ref) {
389437
if bound_ref.def_id() == trait_def_id {
390438
break;
391439
}

src/librustc/middle/ty.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5899,7 +5899,7 @@ pub fn each_bound_trait_and_supertraits<'tcx, F>(tcx: &ctxt<'tcx>,
58995899
-> bool where
59005900
F: FnMut(PolyTraitRef<'tcx>) -> bool,
59015901
{
5902-
for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
5902+
for bound_trait_ref in traits::supertraits_many(tcx, bounds) {
59035903
if !f(bound_trait_ref) {
59045904
return false;
59055905
}
@@ -5928,7 +5928,7 @@ pub fn required_region_bounds<'tcx>(tcx: &ctxt<'tcx>,
59285928

59295929
assert!(!erased_self_ty.has_escaping_regions());
59305930

5931-
traits::elaborate_predicates(tcx, predicates)
5931+
traits::superpredicates(tcx, predicates)
59325932
.filter_map(|predicate| {
59335933
match predicate {
59345934
ty::Predicate::Projection(..) |

src/librustc_typeck/astconv.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,7 @@ fn associated_path_def_to_ty<'tcx>(this: &AstConv<'tcx>,
10591059
}
10601060

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

src/librustc_typeck/check/method/probe.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
456456
debug!("elaborate_bounds(bounds={})", bounds.repr(self.tcx()));
457457

458458
let tcx = self.tcx();
459-
for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
459+
for bound_trait_ref in
460+
traits::elaborate_trait_refs(tcx, bounds).filter_map(|tr| tr.to_opt_poly_trait_ref())
461+
{
460462
let (pos, method) = match trait_method(tcx,
461463
bound_trait_ref.def_id(),
462464
self.method_name) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Tests a case where we need to consider the where-clauses of `Foo`,
12+
// and not just its supetraits, to infer that `A:Foo` holds.
13+
// Issue #20671.
14+
15+
trait Foo<T> {
16+
fn foo(&self) -> &T;
17+
}
18+
19+
trait Bar<A> where A: Foo<Self> {
20+
fn dummy(&self, a: A);
21+
}
22+
23+
fn foobar<A, B: Bar<A>>(a: &A) -> &B {
24+
a.foo()
25+
}
26+
27+
fn main() { }

0 commit comments

Comments
 (0)