@@ -23,6 +23,8 @@ use std::vec_ng::Vec;
23
23
use util:: nodemap:: NodeMap ;
24
24
use syntax:: ast;
25
25
use syntax:: codemap:: Span ;
26
+ use syntax:: opt_vec;
27
+ use syntax:: opt_vec:: OptVec ;
26
28
use syntax:: parse:: token:: special_idents;
27
29
use syntax:: parse:: token;
28
30
use syntax:: print:: pprust:: { lifetime_to_str} ;
@@ -33,14 +35,25 @@ use syntax::visit::Visitor;
33
35
// that it corresponds to
34
36
pub type NamedRegionMap = NodeMap < ast:: DefRegion > ;
35
37
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
+
36
43
struct LifetimeContext {
37
44
sess : session:: Session ,
38
45
named_region_map : @RefCell < NamedRegionMap > ,
39
46
}
40
47
41
48
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.
44
57
BlockScope ( ast:: NodeId , Scope < ' a > ) ,
45
58
RootScope
46
59
}
@@ -62,6 +75,7 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
62
75
fn visit_item ( & mut self ,
63
76
item : & ast:: Item ,
64
77
_: Scope < ' a > ) {
78
+ let root = RootScope ;
65
79
let scope = match item. node {
66
80
ast:: ItemFn ( ..) | // fn lifetimes get added in visit_fn below
67
81
ast:: ItemMod ( ..) |
@@ -76,7 +90,7 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
76
90
ast:: ItemImpl ( ref generics, _, _, _) |
77
91
ast:: ItemTrait ( ref generics, _, _) => {
78
92
self . check_lifetime_names ( & generics. lifetimes ) ;
79
- ItemScope ( & generics. lifetimes )
93
+ EarlyScope ( 0 , & generics. lifetimes , & root )
80
94
}
81
95
} ;
82
96
debug ! ( "entering scope {:?}" , scope) ;
@@ -90,49 +104,41 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
90
104
match * fk {
91
105
visit:: FkItemFn ( _, generics, _, _) |
92
106
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) )
98
110
}
99
111
visit:: FkFnBlock ( ..) => {
100
- visit:: walk_fn ( self , fk, fd, b, s, n, scope) ;
112
+ visit:: walk_fn ( self , fk, fd, b, s, n, scope)
101
113
}
102
114
}
103
115
}
104
116
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 > ) {
107
118
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) ;
125
133
}
126
134
}
127
135
128
136
fn visit_ty_method ( & mut self ,
129
137
m : & ast:: TypeMethod ,
130
138
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) )
136
142
}
137
143
138
144
fn visit_block ( & mut self ,
@@ -155,7 +161,82 @@ impl<'a> Visitor<Scope<'a>> for LifetimeContext {
155
161
}
156
162
}
157
163
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
+
158
184
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
+
159
240
fn resolve_lifetime_ref ( & self ,
160
241
lifetime_ref : & ast:: Lifetime ,
161
242
scope : Scope ) {
@@ -177,23 +258,25 @@ impl LifetimeContext {
177
258
break ;
178
259
}
179
260
180
- ItemScope ( lifetimes) => {
261
+ EarlyScope ( base , lifetimes, s ) => {
181
262
match search_lifetimes ( lifetimes, lifetime_ref) {
182
- Some ( ( index, decl_id) ) => {
263
+ Some ( ( offset, decl_id) ) => {
264
+ let index = base + offset;
183
265
let def = ast:: DefEarlyBoundRegion ( index, decl_id) ;
184
266
self . insert_lifetime ( lifetime_ref, def) ;
185
267
return ;
186
268
}
187
269
None => {
188
- break ;
270
+ depth += 1 ;
271
+ scope = s;
189
272
}
190
273
}
191
274
}
192
275
193
- FnScope ( id , lifetimes, s) => {
276
+ LateScope ( binder_id , lifetimes, s) => {
194
277
match search_lifetimes ( lifetimes, lifetime_ref) {
195
278
Some ( ( _index, decl_id) ) => {
196
- let def = ast:: DefLateBoundRegion ( id , depth, decl_id) ;
279
+ let def = ast:: DefLateBoundRegion ( binder_id , depth, decl_id) ;
197
280
self . insert_lifetime ( lifetime_ref, def) ;
198
281
return ;
199
282
}
@@ -231,12 +314,8 @@ impl LifetimeContext {
231
314
break ;
232
315
}
233
316
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) => {
240
319
search_result = search_lifetimes ( lifetimes, lifetime_ref) ;
241
320
if search_result. is_some ( ) {
242
321
break ;
@@ -323,3 +402,44 @@ fn search_lifetimes(lifetimes: &Vec<ast::Lifetime>,
323
402
}
324
403
return None ;
325
404
}
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
+ }
0 commit comments