@@ -21,6 +21,7 @@ use driver::session;
21
21
use std:: hashmap:: HashMap ;
22
22
use syntax:: ast;
23
23
use syntax:: codemap:: Span ;
24
+ use syntax:: opt_vec;
24
25
use syntax:: opt_vec:: OptVec ;
25
26
use syntax:: parse:: token:: special_idents;
26
27
use syntax:: print:: pprust:: { lifetime_to_str} ;
@@ -37,12 +38,14 @@ struct LifetimeContext {
37
38
}
38
39
39
40
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 > ) ,
43
44
RootScope
44
45
}
45
46
47
+ type Scope < ' self > = & ' self ScopeChain < ' self > ;
48
+
46
49
pub fn crate ( sess : session:: Session ,
47
50
crate : & ast:: Crate )
48
51
-> @mut NamedRegionMap {
@@ -55,10 +58,10 @@ pub fn crate(sess: session::Session,
55
58
ctxt. named_region_map
56
59
}
57
60
58
- impl < ' self > Visitor < & ' self ScopeChain < ' self > > for LifetimeContext {
61
+ impl < ' self > Visitor < Scope < ' self > > for LifetimeContext {
59
62
fn visit_item ( & mut self ,
60
63
item : @ast:: item ,
61
- _: & ' self ScopeChain < ' self > ) {
64
+ _: Scope < ' self > ) {
62
65
let scope = match item. node {
63
66
ast:: item_fn( * ) | // fn lifetimes get added in visit_fn below
64
67
ast:: item_mod( * ) |
@@ -73,7 +76,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
73
76
ast:: item_impl( ref generics, _, _, _) |
74
77
ast:: item_trait( ref generics, _, _) => {
75
78
self . check_lifetime_names ( & generics. lifetimes ) ;
76
- ItemScope ( & generics. lifetimes )
79
+ EarlyScope ( 0 , & generics. lifetimes , & RootScope )
77
80
}
78
81
} ;
79
82
debug ! ( "entering scope {:?}" , scope) ;
@@ -87,33 +90,32 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
87
90
b : & ast:: Block ,
88
91
s : Span ,
89
92
n : ast:: NodeId ,
90
- scope : & ' self ScopeChain < ' self > ) {
93
+ scope : Scope < ' self > ) {
91
94
match * fk {
92
95
visit:: fk_item_fn( _, generics, _, _) |
93
96
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) )
99
101
}
100
102
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)
102
104
}
103
105
}
104
106
}
105
107
106
108
fn visit_ty ( & mut self ,
107
109
ty : & ast:: Ty ,
108
- scope : & ' self ScopeChain < ' self > ) {
110
+ scope : Scope < ' self > ) {
109
111
match ty. node {
110
112
ast:: ty_closure( @ast:: TyClosure { lifetimes : ref lifetimes, _ } ) |
111
113
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) ;
113
115
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) ;
115
117
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) ;
117
119
}
118
120
_ => {
119
121
visit:: walk_ty ( self , ty, scope) ;
@@ -123,17 +125,15 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
123
125
124
126
fn visit_ty_method ( & mut self ,
125
127
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) )
132
132
}
133
133
134
134
fn visit_block ( & mut self ,
135
135
b : & ast:: Block ,
136
- scope : & ' self ScopeChain < ' self > ) {
136
+ scope : Scope < ' self > ) {
137
137
let scope1 = BlockScope ( b. id , scope) ;
138
138
debug ! ( "pushing block scope {}" , b. id) ;
139
139
visit:: walk_block ( self , b, & scope1) ;
@@ -142,7 +142,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
142
142
143
143
fn visit_lifetime_ref ( & mut self ,
144
144
lifetime_ref : & ast:: Lifetime ,
145
- scope : & ' self ScopeChain < ' self > ) {
145
+ scope : Scope < ' self > ) {
146
146
if lifetime_ref. ident == special_idents:: statik {
147
147
self . insert_lifetime ( lifetime_ref, ast:: DefStaticRegion ) ;
148
148
return ;
@@ -151,7 +151,93 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
151
151
}
152
152
}
153
153
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
+
154
174
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
+
155
241
fn resolve_lifetime_ref ( & self ,
156
242
lifetime_ref : & ast:: Lifetime ,
157
243
scope : & ScopeChain ) {
@@ -173,23 +259,25 @@ impl LifetimeContext {
173
259
break ;
174
260
}
175
261
176
- ItemScope ( lifetimes) => {
262
+ EarlyScope ( base , lifetimes, s ) => {
177
263
match search_lifetimes ( lifetimes, lifetime_ref) {
178
- Some ( ( index, decl_id) ) => {
264
+ Some ( ( offset, decl_id) ) => {
265
+ let index = base + offset;
179
266
let def = ast:: DefEarlyBoundRegion ( index, decl_id) ;
180
267
self . insert_lifetime ( lifetime_ref, def) ;
181
268
return ;
182
269
}
183
270
None => {
184
- break ;
271
+ depth += 1 ;
272
+ scope = s;
185
273
}
186
274
}
187
275
}
188
276
189
- FnScope ( id , lifetimes, s) => {
277
+ LateScope ( binder_id , lifetimes, s) => {
190
278
match search_lifetimes ( lifetimes, lifetime_ref) {
191
279
Some ( ( _index, decl_id) ) => {
192
- let def = ast:: DefLateBoundRegion ( id , depth, decl_id) ;
280
+ let def = ast:: DefLateBoundRegion ( binder_id , depth, decl_id) ;
193
281
self . insert_lifetime ( lifetime_ref, def) ;
194
282
return ;
195
283
}
@@ -227,12 +315,7 @@ impl LifetimeContext {
227
315
break ;
228
316
}
229
317
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) => {
236
319
search_result = search_lifetimes ( lifetimes, lifetime_ref) ;
237
320
if search_result. is_some ( ) {
238
321
break ;
@@ -319,3 +402,51 @@ fn search_lifetimes(lifetimes: &OptVec<ast::Lifetime>,
319
402
}
320
403
return None ;
321
404
}
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
+ }
0 commit comments