@@ -23,6 +23,8 @@ use std::vec_ng::Vec;
2323use util:: nodemap:: NodeMap ;
2424use syntax:: ast;
2525use syntax:: codemap:: Span ;
26+ use syntax:: opt_vec;
27+ use syntax:: opt_vec:: OptVec ;
2628use syntax:: parse:: token:: special_idents;
2729use syntax:: parse:: token;
2830use syntax:: print:: pprust:: { lifetime_to_str} ;
@@ -33,14 +35,25 @@ use syntax::visit::Visitor;
3335// that it corresponds to
3436pub 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+
3643struct LifetimeContext {
3744 sess : session:: Session ,
3845 named_region_map : @RefCell < NamedRegionMap > ,
3946}
4047
4148enum 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+
158184impl 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+ }
0 commit comments