Skip to content

Commit b2d11dc

Browse files
committed
Distinguish early- from late-bound lifetime parameters on the basis of whether they appear in a trait bound. Fixes rust-lang#5121.
1 parent 37d6ca9 commit b2d11dc

11 files changed

+446
-93
lines changed

src/librustc/middle/resolve_lifetime.rs

Lines changed: 167 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use driver::session;
2121
use std::hashmap::HashMap;
2222
use syntax::ast;
2323
use syntax::codemap::Span;
24+
use syntax::opt_vec;
2425
use syntax::opt_vec::OptVec;
2526
use syntax::parse::token::special_idents;
2627
use syntax::print::pprust::{lifetime_to_str};
@@ -37,12 +38,14 @@ struct LifetimeContext {
3738
}
3839

3940
enum ScopeChain<'self> {
40-
ItemScope(&'self OptVec<ast::Lifetime>),
41-
FnScope(ast::NodeId, &'self OptVec<ast::Lifetime>, &'self ScopeChain<'self>),
42-
BlockScope(ast::NodeId, &'self ScopeChain<'self>),
41+
EarlyScope(uint, &'self OptVec<ast::Lifetime>, Scope<'self>),
42+
LateScope(ast::NodeId, &'self OptVec<ast::Lifetime>, Scope<'self>),
43+
BlockScope(ast::NodeId, Scope<'self>),
4344
RootScope
4445
}
4546

47+
type Scope<'self> = &'self ScopeChain<'self>;
48+
4649
pub fn crate(sess: session::Session,
4750
crate: &ast::Crate)
4851
-> @mut NamedRegionMap {
@@ -55,10 +58,10 @@ pub fn crate(sess: session::Session,
5558
ctxt.named_region_map
5659
}
5760

58-
impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
61+
impl<'self> Visitor<Scope<'self>> for LifetimeContext {
5962
fn visit_item(&mut self,
6063
item: @ast::item,
61-
_: &'self ScopeChain<'self>) {
64+
_: Scope<'self>) {
6265
let scope = match item.node {
6366
ast::item_fn(*) | // fn lifetimes get added in visit_fn below
6467
ast::item_mod(*) |
@@ -73,7 +76,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
7376
ast::item_impl(ref generics, _, _, _) |
7477
ast::item_trait(ref generics, _, _) => {
7578
self.check_lifetime_names(&generics.lifetimes);
76-
ItemScope(&generics.lifetimes)
79+
EarlyScope(0, &generics.lifetimes, &RootScope)
7780
}
7881
};
7982
debug!("entering scope {:?}", scope);
@@ -87,33 +90,32 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
8790
b: &ast::Block,
8891
s: Span,
8992
n: ast::NodeId,
90-
scope: &'self ScopeChain<'self>) {
93+
scope: Scope<'self>) {
9194
match *fk {
9295
visit::fk_item_fn(_, generics, _, _) |
9396
visit::fk_method(_, generics, _) => {
94-
let scope1 = FnScope(n, &generics.lifetimes, scope);
95-
self.check_lifetime_names(&generics.lifetimes);
96-
debug!("pushing fn scope id={} due to item/method", n);
97-
visit::walk_fn(self, fk, fd, b, s, n, &scope1);
98-
debug!("popping fn scope id={} due to item/method", n);
97+
self.visit_fn_decl(
98+
n, generics, scope,
99+
|this, scope1| visit::walk_fn(this, fk, fd, b,
100+
s, n, scope1))
99101
}
100102
visit::fk_anon(*) | visit::fk_fn_block(*) => {
101-
visit::walk_fn(self, fk, fd, b, s, n, scope);
103+
visit::walk_fn(self, fk, fd, b, s, n, scope)
102104
}
103105
}
104106
}
105107

106108
fn visit_ty(&mut self,
107109
ty: &ast::Ty,
108-
scope: &'self ScopeChain<'self>) {
110+
scope: Scope<'self>) {
109111
match ty.node {
110112
ast::ty_closure(@ast::TyClosure { lifetimes: ref lifetimes, _ }) |
111113
ast::ty_bare_fn(@ast::TyBareFn { lifetimes: ref lifetimes, _ }) => {
112-
let scope1 = FnScope(ty.id, lifetimes, scope);
114+
let scope1 = LateScope(ty.id, lifetimes, scope);
113115
self.check_lifetime_names(lifetimes);
114-
debug!("pushing fn scope id={} due to type", ty.id);
116+
debug!("pushing fn ty scope id={} due to type", ty.id);
115117
visit::walk_ty(self, ty, &scope1);
116-
debug!("popping fn scope id={} due to type", ty.id);
118+
debug!("popping fn ty scope id={} due to type", ty.id);
117119
}
118120
_ => {
119121
visit::walk_ty(self, ty, scope);
@@ -123,17 +125,15 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
123125

124126
fn visit_ty_method(&mut self,
125127
m: &ast::TypeMethod,
126-
scope: &'self ScopeChain<'self>) {
127-
let scope1 = FnScope(m.id, &m.generics.lifetimes, scope);
128-
self.check_lifetime_names(&m.generics.lifetimes);
129-
debug!("pushing fn scope id={} due to ty_method", m.id);
130-
visit::walk_ty_method(self, m, &scope1);
131-
debug!("popping fn scope id={} due to ty_method", m.id);
128+
scope: Scope<'self>) {
129+
self.visit_fn_decl(
130+
m.id, &m.generics, scope,
131+
|this, scope1| visit::walk_ty_method(this, m, scope1))
132132
}
133133

134134
fn visit_block(&mut self,
135135
b: &ast::Block,
136-
scope: &'self ScopeChain<'self>) {
136+
scope: Scope<'self>) {
137137
let scope1 = BlockScope(b.id, scope);
138138
debug!("pushing block scope {}", b.id);
139139
visit::walk_block(self, b, &scope1);
@@ -142,7 +142,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
142142

143143
fn visit_lifetime_ref(&mut self,
144144
lifetime_ref: &ast::Lifetime,
145-
scope: &'self ScopeChain<'self>) {
145+
scope: Scope<'self>) {
146146
if lifetime_ref.ident == special_idents::statik {
147147
self.insert_lifetime(lifetime_ref, ast::DefStaticRegion);
148148
return;
@@ -151,7 +151,93 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
151151
}
152152
}
153153

154+
impl<'self> ScopeChain<'self> {
155+
fn count_early_params(&self) -> uint {
156+
/*!
157+
* Counts the number of early parameters that are in scope.
158+
* Used when checking methods so that we assign the early
159+
* lifetime parameters declared on the method indices that
160+
* come after those from the type (e.g., if there is something
161+
* like `impl<'a> Foo { ... fn bar<'b>(...) }` then `'a` would
162+
* have index 0 and `'b` would have index 1.
163+
*/
164+
165+
match *self {
166+
RootScope => 0,
167+
EarlyScope(base, lifetimes, _) => base + lifetimes.len(),
168+
LateScope(_, _, s) => s.count_early_params(),
169+
BlockScope(_, _) => 0,
170+
}
171+
}
172+
}
173+
154174
impl LifetimeContext {
175+
fn visit_fn_decl(&mut self,
176+
n: ast::NodeId,
177+
generics: &ast::Generics,
178+
scope: Scope,
179+
walk: |&mut LifetimeContext, Scope|) {
180+
/*!
181+
* Handles visiting fns and methods. These are a bit
182+
* complicated because we must distinguish early- vs late-bound
183+
* lifetime parameters. We do this by checking which lifetimes
184+
* appear within type bounds; those are early bound lifetimes,
185+
* and the rest are late bound.
186+
*
187+
* For example:
188+
*
189+
* fn foo<'a,'b,'c,T:Trait<'b>>(...)
190+
*
191+
* Here `'a` and `'c` are late bound but `'b` is early
192+
* bound. Note that early- and late-bound lifetimes may be
193+
* interspersed together.
194+
*
195+
* If early bound lifetimes are present, we separate them into
196+
* their own list (and likewise for late bound). They will be
197+
* numbered sequentially, starting from the lowest index that
198+
* is already in scope (for a fn item, that will be 0, but for
199+
* a method it might not be). Late bound lifetimes are
200+
* resolved by name and associated with a binder id (`n`), so
201+
* the ordering is not important there.
202+
*/
203+
204+
self.check_lifetime_names(&generics.lifetimes);
205+
206+
let early_count = scope.count_early_params();
207+
let referenced_idents =
208+
free_lifetimes(&generics.ty_params);
209+
debug!("pushing fn scope id={} due to item/method \
210+
referenced_idents={:?} \
211+
early_count={}",
212+
n,
213+
referenced_idents.map(|&i| self.sess.str_of(i)),
214+
early_count);
215+
if referenced_idents.is_empty() {
216+
let scope1 = LateScope(n, &generics.lifetimes, scope);
217+
218+
walk(self, &scope1)
219+
} else {
220+
let early: OptVec<ast::Lifetime> =
221+
generics.lifetimes.iter()
222+
.filter(|l| referenced_idents.iter().any(|i| i == &l.ident))
223+
.map(|l| *l)
224+
.collect();
225+
let scope1 = EarlyScope(early_count, &early, scope);
226+
debug!("early names = {:?}",
227+
early.map(|l| self.sess.str_of(l.ident)));
228+
229+
let late: OptVec<ast::Lifetime> =
230+
generics.lifetimes.iter()
231+
.filter(|l| !referenced_idents.iter().any(|i| i == &l.ident))
232+
.map(|l| *l)
233+
.collect();
234+
let scope2 = LateScope(n, &late, &scope1);
235+
236+
walk(self, &scope2)
237+
}
238+
debug!("popping fn scope id={} due to item/method", n);
239+
}
240+
155241
fn resolve_lifetime_ref(&self,
156242
lifetime_ref: &ast::Lifetime,
157243
scope: &ScopeChain) {
@@ -173,23 +259,25 @@ impl LifetimeContext {
173259
break;
174260
}
175261

176-
ItemScope(lifetimes) => {
262+
EarlyScope(base, lifetimes, s) => {
177263
match search_lifetimes(lifetimes, lifetime_ref) {
178-
Some((index, decl_id)) => {
264+
Some((offset, decl_id)) => {
265+
let index = base + offset;
179266
let def = ast::DefEarlyBoundRegion(index, decl_id);
180267
self.insert_lifetime(lifetime_ref, def);
181268
return;
182269
}
183270
None => {
184-
break;
271+
depth += 1;
272+
scope = s;
185273
}
186274
}
187275
}
188276

189-
FnScope(id, lifetimes, s) => {
277+
LateScope(binder_id, lifetimes, s) => {
190278
match search_lifetimes(lifetimes, lifetime_ref) {
191279
Some((_index, decl_id)) => {
192-
let def = ast::DefLateBoundRegion(id, depth, decl_id);
280+
let def = ast::DefLateBoundRegion(binder_id, depth, decl_id);
193281
self.insert_lifetime(lifetime_ref, def);
194282
return;
195283
}
@@ -227,12 +315,7 @@ impl LifetimeContext {
227315
break;
228316
}
229317

230-
ItemScope(lifetimes) => {
231-
search_result = search_lifetimes(lifetimes, lifetime_ref);
232-
break;
233-
}
234-
235-
FnScope(_, lifetimes, s) => {
318+
EarlyScope(_, lifetimes, s) | LateScope(_, lifetimes, s) => {
236319
search_result = search_lifetimes(lifetimes, lifetime_ref);
237320
if search_result.is_some() {
238321
break;
@@ -319,3 +402,51 @@ fn search_lifetimes(lifetimes: &OptVec<ast::Lifetime>,
319402
}
320403
return None;
321404
}
405+
406+
///////////////////////////////////////////////////////////////////////////
407+
408+
pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> OptVec<ast::Lifetime> {
409+
let referenced_idents = free_lifetimes(&generics.ty_params);
410+
if referenced_idents.is_empty() {
411+
return opt_vec::Empty;
412+
}
413+
414+
generics.lifetimes.iter()
415+
.filter(|l| referenced_idents.iter().any(|i| i == &l.ident))
416+
.map(|l| *l)
417+
.collect()
418+
}
419+
420+
pub fn free_lifetimes(ty_params: &OptVec<ast::TyParam>) -> OptVec<ast::Ident> {
421+
/*!
422+
* Gathers up and returns the names of any lifetimes that appear
423+
* free in `ty_params`. Of course, right now, all lifetimes appear
424+
* free, since we don't have any binders in type parameter
425+
* declarations, but I just to be forwards compatible for future
426+
* extensions with my terminology. =)
427+
*/
428+
429+
let mut collector = FreeLifetimeCollector { names: opt_vec::Empty };
430+
for ty_param in ty_params.iter() {
431+
visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds, ());
432+
}
433+
return collector.names;
434+
435+
struct FreeLifetimeCollector {
436+
names: OptVec<ast::Ident>,
437+
}
438+
439+
impl Visitor<()> for FreeLifetimeCollector {
440+
fn visit_ty(&mut self, t:&ast::Ty, _:()) {
441+
// for some weird reason visitor doesn't descend into
442+
// types by default
443+
visit::walk_ty(self, t, ());
444+
}
445+
446+
fn visit_lifetime_ref(&mut self,
447+
lifetime_ref: &ast::Lifetime,
448+
_: ()) {
449+
self.names.push(lifetime_ref.ident);
450+
}
451+
}
452+
}

src/librustc/middle/ty.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,8 @@ pub struct Generics {
882882
/// List of type parameters declared on the item.
883883
type_param_defs: @~[TypeParameterDef],
884884

885-
/// List of region parameters declared on the item.
885+
/// List of region parameters declared on the item. In the
886+
/// case of a fn or method, only includes *early-bound* lifetimes.
886887
region_param_defs: @[RegionParameterDef],
887888
}
888889

@@ -4739,6 +4740,7 @@ pub fn construct_parameter_environment(
47394740
item_type_params: &[TypeParameterDef],
47404741
method_type_params: &[TypeParameterDef],
47414742
item_region_params: &[RegionParameterDef],
4743+
method_region_params: &[RegionParameterDef],
47424744
free_id: ast::NodeId)
47434745
-> ParameterEnvironment
47444746
{
@@ -4766,11 +4768,23 @@ pub fn construct_parameter_environment(
47664768
});
47674769

47684770
// map bound 'a => free 'a
4769-
let region_params = item_region_params.iter().
4770-
map(|r| ty::ReFree(ty::FreeRegion {
4771-
scope_id: free_id,
4772-
bound_region: ty::BrNamed(r.def_id, r.ident)})).
4773-
collect();
4771+
let region_params = {
4772+
fn push_region_params(accum: OptVec<ty::Region>,
4773+
free_id: ast::NodeId,
4774+
region_params: &[RegionParameterDef])
4775+
-> OptVec<ty::Region> {
4776+
let mut accum = accum;
4777+
for r in region_params.iter() {
4778+
accum.push(
4779+
ty::ReFree(ty::FreeRegion {
4780+
scope_id: free_id,
4781+
bound_region: ty::BrNamed(r.def_id, r.ident)}));
4782+
}
4783+
accum
4784+
}
4785+
let t = push_region_params(opt_vec::Empty, free_id, item_region_params);
4786+
push_region_params(t, free_id, method_region_params)
4787+
};
47744788

47754789
let free_substs = substs {
47764790
self_ty: self_ty,
@@ -4792,6 +4806,15 @@ pub fn construct_parameter_environment(
47924806
}
47934807
});
47944808

4809+
debug!("construct_parameter_environment: free_id={} \
4810+
free_substs={} \
4811+
self_param_bound={} \
4812+
type_param_bounds={}",
4813+
free_id,
4814+
free_substs.repr(tcx),
4815+
self_bound_substd.repr(tcx),
4816+
type_param_bounds_substd.repr(tcx));
4817+
47954818
ty::ParameterEnvironment {
47964819
free_substs: free_substs,
47974820
self_param_bound: self_bound_substd,

0 commit comments

Comments
 (0)