Skip to content

Commit 6fb68f1

Browse files
committed
Introduce machinery for higher-ranked TraitRefs
1 parent ecdb741 commit 6fb68f1

File tree

15 files changed

+334
-167
lines changed

15 files changed

+334
-167
lines changed

src/librustc/middle/resolve_lifetime.rs

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ pub use self::DefRegion::*;
2121
use self::ScopeChain::*;
2222

2323
use session::Session;
24+
use middle::def;
25+
use middle::resolve::DefMap;
2426
use middle::subst;
27+
use middle::ty;
2528
use std::fmt;
2629
use syntax::ast;
2730
use syntax::codemap::Span;
28-
use syntax::owned_slice::OwnedSlice;
2931
use syntax::parse::token::special_idents;
3032
use syntax::parse::token;
3133
use syntax::print::pprust::{lifetime_to_string};
@@ -52,7 +54,8 @@ pub type NamedRegionMap = NodeMap<DefRegion>;
5254
struct LifetimeContext<'a> {
5355
sess: &'a Session,
5456
named_region_map: &'a mut NamedRegionMap,
55-
scope: Scope<'a>
57+
scope: Scope<'a>,
58+
def_map: &'a DefMap,
5659
}
5760

5861
enum ScopeChain<'a> {
@@ -72,12 +75,13 @@ type Scope<'a> = &'a ScopeChain<'a>;
7275

7376
static ROOT_SCOPE: ScopeChain<'static> = RootScope;
7477

75-
pub fn krate(sess: &Session, krate: &ast::Crate) -> NamedRegionMap {
78+
pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegionMap {
7679
let mut named_region_map = NodeMap::new();
7780
visit::walk_crate(&mut LifetimeContext {
7881
sess: sess,
7982
named_region_map: &mut named_region_map,
80-
scope: &ROOT_SCOPE
83+
scope: &ROOT_SCOPE,
84+
def_map: def_map,
8185
}, krate);
8286
sess.abort_if_errors();
8387
named_region_map
@@ -151,6 +155,27 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
151155
visit::walk_ty(this, ty);
152156
});
153157
}
158+
ast::TyPath(ref path, ref opt_bounds, id) => {
159+
// if this path references a trait, then this will resolve to
160+
// a trait ref, which introduces a binding scope.
161+
match self.def_map.borrow().get(&id) {
162+
Some(&def::DefTrait(..)) => {
163+
self.with(LateScope(&Vec::new(), self.scope), |this| {
164+
this.visit_path(path, id);
165+
});
166+
167+
match *opt_bounds {
168+
Some(ref bounds) => {
169+
visit::walk_ty_param_bounds_helper(self, bounds);
170+
}
171+
None => { }
172+
}
173+
}
174+
_ => {
175+
visit::walk_ty(self, ty);
176+
}
177+
}
178+
}
154179
_ => {
155180
visit::walk_ty(self, ty)
156181
}
@@ -177,49 +202,22 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
177202

178203
fn visit_generics(&mut self, generics: &ast::Generics) {
179204
for ty_param in generics.ty_params.iter() {
180-
self.visit_ty_param_bounds(&ty_param.bounds);
205+
visit::walk_ty_param_bounds_helper(self, &ty_param.bounds);
181206
match ty_param.default {
182207
Some(ref ty) => self.visit_ty(&**ty),
183208
None => {}
184209
}
185210
}
186211
for predicate in generics.where_clause.predicates.iter() {
187212
self.visit_ident(predicate.span, predicate.ident);
188-
self.visit_ty_param_bounds(&predicate.bounds);
189-
}
190-
}
191-
}
192-
193-
impl<'a> LifetimeContext<'a> {
194-
fn with(&mut self, wrap_scope: ScopeChain, f: |&mut LifetimeContext|) {
195-
let LifetimeContext {sess, ref mut named_region_map, ..} = *self;
196-
let mut this = LifetimeContext {
197-
sess: sess,
198-
named_region_map: *named_region_map,
199-
scope: &wrap_scope
200-
};
201-
debug!("entering scope {}", this.scope);
202-
f(&mut this);
203-
debug!("exiting scope {}", this.scope);
204-
}
205-
206-
fn visit_ty_param_bounds(&mut self,
207-
bounds: &OwnedSlice<ast::TyParamBound>) {
208-
for bound in bounds.iter() {
209-
match *bound {
210-
ast::TraitTyParamBound(ref trait_ref) => {
211-
self.visit_poly_trait_ref(trait_ref);
212-
}
213-
ast::RegionTyParamBound(ref lifetime) => {
214-
self.visit_lifetime_ref(lifetime);
215-
}
216-
}
213+
visit::walk_ty_param_bounds_helper(self, &predicate.bounds);
217214
}
218215
}
219216

220217
fn visit_poly_trait_ref(&mut self, trait_ref: &ast::PolyTraitRef) {
221-
let ref_id = trait_ref.trait_ref.ref_id;
222-
self.with(LateScope(ref_id, &trait_ref.bound_lifetimes, self.scope), |this| {
218+
debug!("visit_poly_trait_ref trait_ref={}", trait_ref);
219+
220+
self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |this| {
223221
this.check_lifetime_defs(&trait_ref.bound_lifetimes);
224222
for lifetime in trait_ref.bound_lifetimes.iter() {
225223
this.visit_lifetime_decl(lifetime);

src/librustc/middle/traits/fulfill.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl FulfillmentContext {
5555
obligation: Obligation)
5656
{
5757
debug!("register_obligation({})", obligation.repr(tcx));
58+
assert!(!obligation.trait_ref.has_escaping_regions());
5859
self.trait_obligations.push(obligation);
5960
}
6061

src/librustc/middle/traits/select.rs

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ use middle::fast_reject;
3131
use middle::mem_categorization::Typer;
3232
use middle::subst::{Subst, Substs, VecPerParamSpace};
3333
use middle::ty;
34-
use middle::typeck::check::regionmanip;
3534
use middle::typeck::infer;
36-
use middle::typeck::infer::LateBoundRegionConversionTime::*;
3735
use middle::typeck::infer::{InferCtxt, TypeSkolemizer};
3836
use middle::ty_fold::TypeFoldable;
3937
use std::cell::RefCell;
@@ -211,6 +209,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
211209
*/
212210

213211
debug!("select({})", obligation.repr(self.tcx()));
212+
assert!(!obligation.trait_ref.has_escaping_regions());
214213

215214
let stack = self.push_stack(None, obligation);
216215
match try!(self.candidate_from_obligation(&stack)) {
@@ -263,6 +262,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
263262

264263
debug!("evaluate_obligation({})",
265264
obligation.repr(self.tcx()));
265+
assert!(!obligation.trait_ref.has_escaping_regions());
266266

267267
let stack = self.push_stack(None, obligation);
268268
self.evaluate_stack(&stack).may_apply()
@@ -747,6 +747,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
747747
debug!("candidate_from_obligation(cache_skol_trait_ref={}, obligation={})",
748748
cache_skol_trait_ref.repr(self.tcx()),
749749
stack.repr(self.tcx()));
750+
assert!(!stack.obligation.trait_ref.has_escaping_regions());
750751

751752
match self.check_candidate_cache(cache_skol_trait_ref.clone()) {
752753
Some(c) => {
@@ -1707,27 +1708,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
17071708
}
17081709
};
17091710

1710-
// FIXME(pcwalton): This is a bogus thing to do, but
1711-
// it'll do for now until we get the new trait-bound
1712-
// region skolemization working.
1713-
let (_, new_signature) =
1714-
regionmanip::replace_late_bound_regions(
1715-
self.tcx(),
1716-
closure_type.sig.binder_id,
1717-
&closure_type.sig,
1718-
|br| self.infcx.next_region_var(
1719-
infer::LateBoundRegion(obligation.cause.span, br,
1720-
infer::FnCall)));
1721-
1722-
let arguments_tuple = new_signature.inputs[0];
1723-
let trait_ref = Rc::new(ty::TraitRef {
1724-
def_id: obligation.trait_ref.def_id,
1725-
substs: Substs::new_trait(
1711+
let closure_sig = &closure_type.sig;
1712+
let arguments_tuple = closure_sig.inputs[0];
1713+
let substs =
1714+
Substs::new_trait(
17261715
vec![arguments_tuple.subst(self.tcx(), substs),
1727-
new_signature.output.unwrap().subst(self.tcx(), substs)],
1716+
closure_sig.output.unwrap().subst(self.tcx(), substs)],
17281717
vec![],
17291718
vec![],
1730-
obligation.self_ty())
1719+
obligation.self_ty());
1720+
let trait_ref = Rc::new(ty::TraitRef {
1721+
def_id: obligation.trait_ref.def_id,
1722+
substs: substs,
17311723
});
17321724

17331725
self.confirm(obligation.cause,

src/librustc/middle/ty.rs

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,23 @@ pub struct TyTrait {
11051105
pub bounds: ExistentialBounds
11061106
}
11071107

1108+
/**
1109+
* A complete reference to a trait. These take numerous guises in syntax,
1110+
* but perhaps the most recognizable form is in a where clause:
1111+
*
1112+
* T : Foo<U>
1113+
*
1114+
* This would be represented by a trait-reference where the def-id is the
1115+
* def-id for the trait `Foo` and the substs defines `T` as parameter 0 in the
1116+
* `SelfSpace` and `U` as parameter 0 in the `TypeSpace`.
1117+
*
1118+
* Trait references also appear in object types like `Foo<U>`, but in
1119+
* that case the `Self` parameter is absent from the substitutions.
1120+
*
1121+
* Note that a `TraitRef` introduces a level of region binding, to
1122+
* account for higher-ranked trait bounds like `T : for<'a> Foo<&'a
1123+
* U>` or higher-ranked object types.
1124+
*/
11081125
#[deriving(Clone, PartialEq, Eq, Hash, Show)]
11091126
pub struct TraitRef {
11101127
pub def_id: DefId,
@@ -1410,6 +1427,14 @@ impl TraitRef {
14101427
// associated types.
14111428
self.substs.types.as_slice()
14121429
}
1430+
1431+
pub fn has_escaping_regions(&self) -> bool {
1432+
self.substs.has_regions_escaping_depth(1)
1433+
}
1434+
1435+
pub fn has_bound_regions(&self) -> bool {
1436+
self.substs.has_regions_escaping_depth(0)
1437+
}
14131438
}
14141439

14151440
/// When type checking, we use the `ParameterEnvironment` to track
@@ -1826,7 +1851,10 @@ impl FlagComputation {
18261851
}
18271852

18281853
&ty_trait(box TyTrait { ref principal, ref bounds }) => {
1829-
self.add_substs(&principal.substs);
1854+
let mut computation = FlagComputation::new();
1855+
computation.add_substs(&principal.substs);
1856+
self.add_bound_computation(&computation);
1857+
18301858
self.add_bounds(bounds);
18311859
}
18321860

@@ -4708,9 +4736,99 @@ pub fn bounds_for_trait_ref(tcx: &ctxt,
47084736
-> ty::ParamBounds
47094737
{
47104738
let trait_def = lookup_trait_def(tcx, trait_ref.def_id);
4739+
47114740
debug!("bounds_for_trait_ref(trait_def={}, trait_ref={})",
47124741
trait_def.repr(tcx), trait_ref.repr(tcx));
4713-
trait_def.bounds.subst(tcx, &trait_ref.substs)
4742+
4743+
// The interaction between HRTB and supertraits is not entirely
4744+
// obvious. Let me walk you (and myself) through an example.
4745+
//
4746+
// Let's start with an easy case. Consider two traits:
4747+
//
4748+
// trait Foo<'a> : Bar<'a,'a> { }
4749+
// trait Bar<'b,'c> { }
4750+
//
4751+
// Now, if we have a trait reference `for<'x> T : Foo<'x>`, then
4752+
// we can deduce that `for<'x> T : Bar<'x,'x>`. Basically, if we
4753+
// knew that `Foo<'x>` (for any 'x) then we also know that
4754+
// `Bar<'x,'x>` (for any 'x). This more-or-less falls out from
4755+
// normal substitution.
4756+
//
4757+
// In terms of why this is sound, the idea is that whenever there
4758+
// is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>`
4759+
// holds. So if there is an impl of `T:Foo<'a>` that applies to
4760+
// all `'a`, then we must know that `T:Bar<'a,'a>` holds for all
4761+
// `'a`.
4762+
//
4763+
// Another example to be careful of is this:
4764+
//
4765+
// trait Foo1<'a> : for<'b> Bar1<'a,'b> { }
4766+
// trait Bar1<'b,'c> { }
4767+
//
4768+
// Here, if we have `for<'x> T : Foo1<'x>`, then what do we know?
4769+
// The answer is that we know `for<'x,'b> T : Bar1<'x,'b>`. The
4770+
// reason is similar to the previous example: any impl of
4771+
// `T:Foo1<'x>` must show that `for<'b> T : Bar1<'x, 'b>`. So
4772+
// basically we would want to collapse the bound lifetimes from
4773+
// the input (`trait_ref`) and the supertraits.
4774+
//
4775+
// To achieve this in practice is fairly straightforward. Let's
4776+
// consider the more complicated scenario:
4777+
//
4778+
// - We start out with `for<'x> T : Foo1<'x>`. In this case, `'x`
4779+
// has a De Bruijn index of 1. We want to produce `for<'x,'b> T : Bar1<'x,'b>`,
4780+
// where both `'x` and `'b` would have a DB index of 1.
4781+
// The substitution from the input trait-ref is therefore going to be
4782+
// `'a => 'x` (where `'x` has a DB index of 1).
4783+
// - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an
4784+
// early-bound parameter and `'b' is a late-bound parameter with a
4785+
// DB index of 1.
4786+
// - If we replace `'a` with `'x` from the input, it too will have
4787+
// a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>`
4788+
// just as we wanted.
4789+
//
4790+
// There is only one catch. If we just apply the substitution `'a
4791+
// => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will
4792+
// adjust the DB index because we substituting into a binder (it
4793+
// tries to be so smart...) resulting in `for<'x> for<'b>
4794+
// Bar1<'x,'b>` (we have no syntax for this, so use your
4795+
// imagination). Basically the 'x will have DB index of 2 and 'b
4796+
// will have DB index of 1. Not quite what we want. So we apply
4797+
// the substitution to the *contents* of the trait reference,
4798+
// rather than the trait reference itself (put another way, the
4799+
// substitution code expects equal binding levels in the values
4800+
// from the substitution and the value being substituted into, and
4801+
// this trick achieves that).
4802+
4803+
// Carefully avoid the binder introduced by each trait-ref by
4804+
// substituting over the substs, not the trait-refs themselves,
4805+
// thus achieving the "collapse" described in the big comment
4806+
// above.
4807+
let trait_bounds: Vec<_> =
4808+
trait_def.bounds.trait_bounds
4809+
.iter()
4810+
.map(|bound_trait_ref| {
4811+
ty::TraitRef::new(bound_trait_ref.def_id,
4812+
bound_trait_ref.substs.subst(tcx, &trait_ref.substs))
4813+
})
4814+
.map(|bound_trait_ref| Rc::new(bound_trait_ref))
4815+
.collect();
4816+
4817+
debug!("bounds_for_trait_ref: trait_bounds={}",
4818+
trait_bounds.repr(tcx));
4819+
4820+
// The region bounds and builtin bounds do not currently introduce
4821+
// binders so we can just substitute in a straightforward way here.
4822+
let region_bounds =
4823+
trait_def.bounds.region_bounds.subst(tcx, &trait_ref.substs);
4824+
let builtin_bounds =
4825+
trait_def.bounds.builtin_bounds.subst(tcx, &trait_ref.substs);
4826+
4827+
ty::ParamBounds {
4828+
trait_bounds: trait_bounds,
4829+
region_bounds: region_bounds,
4830+
builtin_bounds: builtin_bounds,
4831+
}
47144832
}
47154833

47164834
/// Iterate over attributes of a definition.

0 commit comments

Comments
 (0)