Skip to content

Commit 742e458

Browse files
committed
Add proper support for early/late distinction for lifetime bindings.
Uses newly added Vec::partition method to simplify resolve_lifetime.
1 parent 586b619 commit 742e458

20 files changed

+566
-200
lines changed

src/librustc/middle/resolve_lifetime.rs

+164-44
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use std::vec_ng::Vec;
2323
use util::nodemap::NodeMap;
2424
use syntax::ast;
2525
use syntax::codemap::Span;
26+
use syntax::opt_vec;
27+
use syntax::opt_vec::OptVec;
2628
use syntax::parse::token::special_idents;
2729
use syntax::parse::token;
2830
use syntax::print::pprust::{lifetime_to_str};
@@ -33,14 +35,25 @@ use syntax::visit::Visitor;
3335
// that it corresponds to
3436
pub type NamedRegionMap = NodeMap<ast::DefRegion>;
3537

38+
// Returns an instance of some type that implements std::fmt::Show
39+
fn lifetime_show(lt_name: &ast::Name) -> token::InternedString {
40+
token::get_name(*lt_name)
41+
}
42+
3643
struct LifetimeContext {
3744
sess: session::Session,
3845
named_region_map: @RefCell<NamedRegionMap>,
3946
}
4047

4148
enum ScopeChain<'a> {
42-
ItemScope(&'a Vec<ast::Lifetime>),
43-
FnScope(ast::NodeId, &'a Vec<ast::Lifetime>, Scope<'a>),
49+
/// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
50+
/// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
51+
EarlyScope(uint, &'a Vec<ast::Lifetime>, Scope<'a>),
52+
/// LateScope(binder_id, ['a, 'b, ...], s) extends s with late-bound
53+
/// lifetimes introduced by the declaration binder_id.
54+
LateScope(ast::NodeId, &'a Vec<ast::Lifetime>, Scope<'a>),
55+
/// lifetimes introduced by items within a code block are scoped
56+
/// to that block.
4457
BlockScope(ast::NodeId, Scope<'a>),
4558
RootScope
4659
}
@@ -62,6 +75,7 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
6275
fn visit_item(&mut self,
6376
item: &ast::Item,
6477
_: Scope<'a>) {
78+
let root = RootScope;
6579
let scope = match item.node {
6680
ast::ItemFn(..) | // fn lifetimes get added in visit_fn below
6781
ast::ItemMod(..) |
@@ -76,7 +90,7 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
7690
ast::ItemImpl(ref generics, _, _, _) |
7791
ast::ItemTrait(ref generics, _, _) => {
7892
self.check_lifetime_names(&generics.lifetimes);
79-
ItemScope(&generics.lifetimes)
93+
EarlyScope(0, &generics.lifetimes, &root)
8094
}
8195
};
8296
debug!("entering scope {:?}", scope);
@@ -90,49 +104,41 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
90104
match *fk {
91105
visit::FkItemFn(_, generics, _, _) |
92106
visit::FkMethod(_, generics, _) => {
93-
let scope1 = FnScope(n, &generics.lifetimes, scope);
94-
self.check_lifetime_names(&generics.lifetimes);
95-
debug!("pushing fn scope id={} due to item/method", n);
96-
visit::walk_fn(self, fk, fd, b, s, n, &scope1);
97-
debug!("popping fn scope id={} due to item/method", n);
107+
self.visit_fn_decl(
108+
n, generics, scope,
109+
|this, scope1| visit::walk_fn(this, fk, fd, b, s, n, scope1))
98110
}
99111
visit::FkFnBlock(..) => {
100-
visit::walk_fn(self, fk, fd, b, s, n, scope);
112+
visit::walk_fn(self, fk, fd, b, s, n, scope)
101113
}
102114
}
103115
}
104116

105-
fn visit_ty(&mut self, ty: &ast::Ty,
106-
scope: Scope<'a>) {
117+
fn visit_ty(&mut self, ty: &ast::Ty, scope: Scope<'a>) {
107118
match ty.node {
108-
ast::TyClosure(closure) => {
109-
let scope1 = FnScope(ty.id, &closure.lifetimes, scope);
110-
self.check_lifetime_names(&closure.lifetimes);
111-
debug!("pushing fn scope id={} due to type", ty.id);
112-
visit::walk_ty(self, ty, &scope1);
113-
debug!("popping fn scope id={} due to type", ty.id);
114-
}
115-
ast::TyBareFn(bare_fn) => {
116-
let scope1 = FnScope(ty.id, &bare_fn.lifetimes, scope);
117-
self.check_lifetime_names(&bare_fn.lifetimes);
118-
debug!("pushing fn scope id={} due to type", ty.id);
119-
visit::walk_ty(self, ty, &scope1);
120-
debug!("popping fn scope id={} due to type", ty.id);
121-
}
122-
_ => {
123-
visit::walk_ty(self, ty, scope);
124-
}
119+
ast::TyClosure(c) => push_fn_scope(self, ty, scope, &c.lifetimes),
120+
ast::TyBareFn(c) => push_fn_scope(self, ty, scope, &c.lifetimes),
121+
_ => visit::walk_ty(self, ty, scope),
122+
}
123+
124+
fn push_fn_scope(this: &mut LifetimeContext,
125+
ty: &ast::Ty,
126+
scope: Scope,
127+
lifetimes: &Vec<ast::Lifetime>) {
128+
let scope1 = LateScope(ty.id, lifetimes, scope);
129+
this.check_lifetime_names(lifetimes);
130+
debug!("pushing fn scope id={} due to type", ty.id);
131+
visit::walk_ty(this, ty, &scope1);
132+
debug!("popping fn scope id={} due to type", ty.id);
125133
}
126134
}
127135

128136
fn visit_ty_method(&mut self,
129137
m: &ast::TypeMethod,
130138
scope: Scope<'a>) {
131-
let scope1 = FnScope(m.id, &m.generics.lifetimes, scope);
132-
self.check_lifetime_names(&m.generics.lifetimes);
133-
debug!("pushing fn scope id={} due to ty_method", m.id);
134-
visit::walk_ty_method(self, m, &scope1);
135-
debug!("popping fn scope id={} due to ty_method", m.id);
139+
self.visit_fn_decl(
140+
m.id, &m.generics, scope,
141+
|this, scope1| visit::walk_ty_method(this, m, scope1))
136142
}
137143

138144
fn visit_block(&mut self,
@@ -155,7 +161,82 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
155161
}
156162
}
157163

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

180-
ItemScope(lifetimes) => {
261+
EarlyScope(base, lifetimes, s) => {
181262
match search_lifetimes(lifetimes, lifetime_ref) {
182-
Some((index, decl_id)) => {
263+
Some((offset, decl_id)) => {
264+
let index = base + offset;
183265
let def = ast::DefEarlyBoundRegion(index, decl_id);
184266
self.insert_lifetime(lifetime_ref, def);
185267
return;
186268
}
187269
None => {
188-
break;
270+
depth += 1;
271+
scope = s;
189272
}
190273
}
191274
}
192275

193-
FnScope(id, lifetimes, s) => {
276+
LateScope(binder_id, lifetimes, s) => {
194277
match search_lifetimes(lifetimes, lifetime_ref) {
195278
Some((_index, decl_id)) => {
196-
let def = ast::DefLateBoundRegion(id, depth, decl_id);
279+
let def = ast::DefLateBoundRegion(binder_id, depth, decl_id);
197280
self.insert_lifetime(lifetime_ref, def);
198281
return;
199282
}
@@ -231,12 +314,8 @@ impl LifetimeContext {
231314
break;
232315
}
233316

234-
ItemScope(lifetimes) => {
235-
search_result = search_lifetimes(lifetimes, lifetime_ref);
236-
break;
237-
}
238-
239-
FnScope(_, lifetimes, s) => {
317+
EarlyScope(_, lifetimes, s) |
318+
LateScope(_, lifetimes, s) => {
240319
search_result = search_lifetimes(lifetimes, lifetime_ref);
241320
if search_result.is_some() {
242321
break;
@@ -323,3 +402,44 @@ fn search_lifetimes(lifetimes: &Vec<ast::Lifetime>,
323402
}
324403
return None;
325404
}
405+
406+
///////////////////////////////////////////////////////////////////////////
407+
408+
pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::Lifetime> {
409+
let referenced_idents = free_lifetimes(&generics.ty_params);
410+
if referenced_idents.is_empty() {
411+
return Vec::new();
412+
}
413+
414+
generics.lifetimes.iter()
415+
.filter(|l| referenced_idents.iter().any(|&i| i == l.name))
416+
.map(|l| *l)
417+
.collect()
418+
}
419+
420+
pub fn free_lifetimes(ty_params: &OptVec<ast::TyParam>) -> OptVec<ast::Name> {
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 currently have any binders in type parameter
425+
* declarations; just being forwards compatible with future extensions.
426+
*/
427+
428+
let mut collector = FreeLifetimeCollector { names: opt_vec::Empty };
429+
for ty_param in ty_params.iter() {
430+
visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds, ());
431+
}
432+
return collector.names;
433+
434+
struct FreeLifetimeCollector {
435+
names: OptVec<ast::Name>,
436+
}
437+
438+
impl Visitor<()> for FreeLifetimeCollector {
439+
fn visit_lifetime_ref(&mut self,
440+
lifetime_ref: &ast::Lifetime,
441+
_: ()) {
442+
self.names.push(lifetime_ref.name);
443+
}
444+
}
445+
}

src/librustc/middle/subst.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ impl Subst for ty::Region {
276276
// bound in *fn types*. Region substitution of the bound
277277
// regions that appear in a function signature is done using
278278
// the specialized routine
279-
// `middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig()`.
279+
// `middle::typeck::check::regionmanip::replace_late_regions_in_fn_sig()`.
280280
match self {
281281
&ty::ReEarlyBound(_, i, _) => {
282282
match substs.regions {

src/librustc/middle/ty.rs

+29-5
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,7 @@ pub struct Generics {
10081008
type_param_defs: Rc<Vec<TypeParameterDef> >,
10091009

10101010
/// List of region parameters declared on the item.
1011+
/// For a fn or method, only includes *early-bound* lifetimes.
10111012
region_param_defs: Rc<Vec<RegionParameterDef> >,
10121013
}
10131014

@@ -5077,6 +5078,7 @@ pub fn construct_parameter_environment(
50775078
item_type_params: &[TypeParameterDef],
50785079
method_type_params: &[TypeParameterDef],
50795080
item_region_params: &[RegionParameterDef],
5081+
method_region_params: &[RegionParameterDef],
50805082
free_id: ast::NodeId)
50815083
-> ParameterEnvironment
50825084
{
@@ -5104,11 +5106,24 @@ pub fn construct_parameter_environment(
51045106
});
51055107

51065108
// map bound 'a => free 'a
5107-
let region_params = item_region_params.iter().
5108-
map(|r| ty::ReFree(ty::FreeRegion {
5109-
scope_id: free_id,
5110-
bound_region: ty::BrNamed(r.def_id, r.name)})).
5111-
collect();
5109+
let region_params = {
5110+
fn push_region_params(accum: OptVec<ty::Region>,
5111+
free_id: ast::NodeId,
5112+
region_params: &[RegionParameterDef])
5113+
-> OptVec<ty::Region> {
5114+
let mut accum = accum;
5115+
for r in region_params.iter() {
5116+
accum.push(
5117+
ty::ReFree(ty::FreeRegion {
5118+
scope_id: free_id,
5119+
bound_region: ty::BrNamed(r.def_id, r.name)}));
5120+
}
5121+
accum
5122+
}
5123+
5124+
let t = push_region_params(opt_vec::Empty, free_id, item_region_params);
5125+
push_region_params(t, free_id, method_region_params)
5126+
};
51125127

51135128
let free_substs = substs {
51145129
self_ty: self_ty,
@@ -5130,6 +5145,15 @@ pub fn construct_parameter_environment(
51305145
}
51315146
});
51325147

5148+
debug!("construct_parameter_environment: free_id={} \
5149+
free_subst={} \
5150+
self_param_bound={} \
5151+
type_param_bound={}",
5152+
free_id,
5153+
free_substs.repr(tcx),
5154+
self_bound_substd.repr(tcx),
5155+
type_param_bounds_substd.repr(tcx));
5156+
51335157
ty::ParameterEnvironment {
51345158
free_substs: free_substs,
51355159
self_param_bound: self_bound_substd,

0 commit comments

Comments
 (0)