1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 * Name resolution for lifetimes.
14 * Name resolution for lifetimes follows MUCH simpler rules than the
15 * full resolve. For example, lifetime names are never exported or
16 * used between functions, and they operate in a purely top-down
17 * way. Therefore we break lifetime name resolution into a separate pass.
20 use driver::session::Session;
23 use syntax::codemap::Span;
24 use syntax::owned_slice::OwnedSlice;
25 use syntax::parse::token::special_idents;
26 use syntax::parse::token;
27 use syntax::print::pprust::{lifetime_to_str};
29 use syntax::visit::Visitor;
30 use util::nodemap::NodeMap;
32 #[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
35 DefEarlyBoundRegion(/* space */ subst::ParamSpace,
37 /* lifetime decl */ ast::NodeId),
38 DefLateBoundRegion(/* binder_id */ ast::NodeId,
40 /* lifetime decl */ ast::NodeId),
41 DefFreeRegion(/* block scope */ ast::NodeId,
42 /* lifetime decl */ ast::NodeId),
45 // maps the id of each lifetime reference to the lifetime decl
46 // that it corresponds to
47 pub type NamedRegionMap = NodeMap<DefRegion>;
49 // Returns an instance of some type that implements std::fmt::Show
50 fn lifetime_show(lt_name: &ast::Name) -> token::InternedString {
51 token::get_name(*lt_name)
54 struct LifetimeContext<'a> {
56 named_region_map: NamedRegionMap,
60 /// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
61 /// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
62 EarlyScope(subst::ParamSpace, &'a Vec<ast::Lifetime>, Scope<'a>),
63 /// LateScope(binder_id, ['a, 'b, ...], s) extends s with late-bound
64 /// lifetimes introduced by the declaration binder_id.
65 LateScope(ast::NodeId, &'a Vec<ast::Lifetime>, Scope<'a>),
66 /// lifetimes introduced by items within a code block are scoped
68 BlockScope(ast::NodeId, Scope<'a>),
72 type Scope<'a> = &'a ScopeChain<'a>;
74 pub fn krate(sess: &Session, krate: &ast::Crate) -> NamedRegionMap {
75 let mut ctxt = LifetimeContext {
77 named_region_map: NodeMap::new()
79 visit::walk_crate(&mut ctxt, krate, &RootScope);
80 sess.abort_if_errors();
84 impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> {
85 fn visit_item(&mut self,
89 let scope = match item.node {
90 ast::ItemFn(..) | // fn lifetimes get added in visit_fn below
93 ast::ItemForeignMod(..) |
94 ast::ItemStatic(..) => {
97 ast::ItemTy(_, ref generics) |
98 ast::ItemEnum(_, ref generics) |
99 ast::ItemStruct(_, ref generics) |
100 ast::ItemImpl(ref generics, _, _, _) |
101 ast::ItemTrait(ref generics, _, _, _) => {
102 self.check_lifetime_names(&generics.lifetimes);
103 EarlyScope(subst::TypeSpace, &generics.lifetimes, &root)
106 debug!("entering scope {:?}", scope);
107 visit::walk_item(self, item, &scope);
108 debug!("exiting scope {:?}", scope);
111 fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl,
112 b: &ast::Block, s: Span, n: ast::NodeId,
115 visit::FkItemFn(_, generics, _, _) |
116 visit::FkMethod(_, generics, _) => {
119 |this, scope1| visit::walk_fn(this, fk, fd, b, s, scope1))
121 visit::FkFnBlock(..) => {
122 visit::walk_fn(self, fk, fd, b, s, scope)
127 fn visit_ty(&mut self, ty: &ast::Ty, scope: Scope<'a>) {
129 ast::TyClosure(c, _) | ast::TyProc(c) => {
130 push_fn_scope(self, ty, scope, &c.lifetimes);
132 ast::TyBareFn(c) => push_fn_scope(self, ty, scope, &c.lifetimes),
133 _ => visit::walk_ty(self, ty, scope),
136 fn push_fn_scope(this: &mut LifetimeContext,
139 lifetimes: &Vec<ast::Lifetime>) {
140 let scope1 = LateScope(ty.id, lifetimes, scope);
141 this.check_lifetime_names(lifetimes);
142 debug!("pushing fn scope id={} due to type", ty.id);
143 visit::walk_ty(this, ty, &scope1);
144 debug!("popping fn scope id={} due to type", ty.id);
148 fn visit_ty_method(&mut self,
152 m.id, &m.generics, scope,
153 |this, scope1| visit::walk_ty_method(this, m, scope1))
156 fn visit_block(&mut self,
159 let scope1 = BlockScope(b.id, scope);
160 debug!("pushing block scope {}", b.id);
161 visit::walk_block(self, b, &scope1);
162 debug!("popping block scope {}", b.id);
165 fn visit_lifetime_ref(&mut self,
166 lifetime_ref: &ast::Lifetime,
168 if lifetime_ref.name == special_idents::static_lifetime.name {
169 self.insert_lifetime(lifetime_ref, DefStaticRegion);
172 self.resolve_lifetime_ref(lifetime_ref, scope);
176 impl<'a> LifetimeContext<'a> {
177 /// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
178 fn visit_fn_decl(&mut self,
180 generics: &ast::Generics,
182 walk: |&mut LifetimeContext, Scope|) {
184 * Handles visiting fns and methods. These are a bit
185 * complicated because we must distinguish early- vs late-bound
186 * lifetime parameters. We do this by checking which lifetimes
187 * appear within type bounds; those are early bound lifetimes,
188 * and the rest are late bound.
192 * fn foo<'a,'b,'c,T:Trait<'b>>(...)
194 * Here `'a` and `'c` are late bound but `'b` is early
195 * bound. Note that early- and late-bound lifetimes may be
196 * interspersed together.
198 * If early bound lifetimes are present, we separate them into
199 * their own list (and likewise for late bound). They will be
200 * numbered sequentially, starting from the lowest index that
201 * is already in scope (for a fn item, that will be 0, but for
202 * a method it might not be). Late bound lifetimes are
203 * resolved by name and associated with a binder id (`n`), so
204 * the ordering is not important there.
207 self.check_lifetime_names(&generics.lifetimes);
209 let referenced_idents = free_lifetimes(&generics.ty_params);
210 debug!("pushing fn scope id={} due to fn item/method\
211 referenced_idents={:?}",
213 referenced_idents.iter().map(lifetime_show).collect::<Vec<token::InternedString>>());
214 if referenced_idents.is_empty() {
215 let scope1 = LateScope(n, &generics.lifetimes, scope);
218 let (early, late) = generics.lifetimes.clone().partition(
219 |l| referenced_idents.iter().any(|&i| i == l.name));
221 let scope1 = EarlyScope(subst::FnSpace, &early, scope);
222 let scope2 = LateScope(n, &late, &scope1);
226 debug!("popping fn scope id={} due to fn item/method", n);
229 fn resolve_lifetime_ref(&mut self,
230 lifetime_ref: &ast::Lifetime,
232 // Walk up the scope chain, tracking the number of fn scopes
233 // that we pass through, until we find a lifetime with the
234 // given name or we run out of scopes. If we encounter a code
235 // block, then the lifetime is not bound but free, so switch
236 // over to `resolve_free_lifetime_ref()` to complete the
239 let mut scope = scope;
242 BlockScope(id, s) => {
243 return self.resolve_free_lifetime_ref(id, lifetime_ref, s);
250 EarlyScope(space, lifetimes, s) => {
251 match search_lifetimes(lifetimes, lifetime_ref) {
252 Some((index, decl_id)) => {
253 let def = DefEarlyBoundRegion(space, index, decl_id);
254 self.insert_lifetime(lifetime_ref, def);
264 LateScope(binder_id, lifetimes, s) => {
265 match search_lifetimes(lifetimes, lifetime_ref) {
266 Some((_index, decl_id)) => {
267 let def = DefLateBoundRegion(binder_id, depth, decl_id);
268 self.insert_lifetime(lifetime_ref, def);
281 self.unresolved_lifetime_ref(lifetime_ref);
284 fn resolve_free_lifetime_ref(&mut self,
285 scope_id: ast::NodeId,
286 lifetime_ref: &ast::Lifetime,
288 // Walk up the scope chain, tracking the outermost free scope,
289 // until we encounter a scope that contains the named lifetime
290 // or we run out of scopes.
291 let mut scope_id = scope_id;
292 let mut scope = scope;
293 let mut search_result = None;
296 BlockScope(id, s) => {
305 EarlyScope(_, lifetimes, s) |
306 LateScope(_, lifetimes, s) => {
307 search_result = search_lifetimes(lifetimes, lifetime_ref);
308 if search_result.is_some() {
316 match search_result {
317 Some((_depth, decl_id)) => {
318 let def = DefFreeRegion(scope_id, decl_id);
319 self.insert_lifetime(lifetime_ref, def);
323 self.unresolved_lifetime_ref(lifetime_ref);
329 fn unresolved_lifetime_ref(&self,
330 lifetime_ref: &ast::Lifetime) {
333 format!("use of undeclared lifetime name `{}`",
334 token::get_name(lifetime_ref.name)).as_slice());
337 fn check_lifetime_names(&self, lifetimes: &Vec<ast::Lifetime>) {
338 for i in range(0, lifetimes.len()) {
339 let lifetime_i = lifetimes.get(i);
341 let special_idents = [special_idents::static_lifetime];
342 for lifetime in lifetimes.iter() {
343 if special_idents.iter().any(|&i| i.name == lifetime.name) {
346 format!("illegal lifetime parameter name: `{}`",
347 token::get_name(lifetime.name)).as_slice());
351 for j in range(i + 1, lifetimes.len()) {
352 let lifetime_j = lifetimes.get(j);
354 if lifetime_i.name == lifetime_j.name {
357 format!("lifetime name `{}` declared twice in \
359 token::get_name(lifetime_j.name)).as_slice());
365 fn insert_lifetime(&mut self,
366 lifetime_ref: &ast::Lifetime,
368 if lifetime_ref.id == ast::DUMMY_NODE_ID {
369 self.sess.span_bug(lifetime_ref.span,
370 "lifetime reference not renumbered, \
371 probably a bug in syntax::fold");
374 debug!("lifetime_ref={} id={} resolved to {:?}",
375 lifetime_to_str(lifetime_ref),
378 self.named_region_map.insert(lifetime_ref.id, def);
382 fn search_lifetimes(lifetimes: &Vec<ast::Lifetime>,
383 lifetime_ref: &ast::Lifetime)
384 -> Option<(uint, ast::NodeId)> {
385 for (i, lifetime_decl) in lifetimes.iter().enumerate() {
386 if lifetime_decl.name == lifetime_ref.name {
387 return Some((i, lifetime_decl.id));
393 ///////////////////////////////////////////////////////////////////////////
395 pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::Lifetime> {
396 let referenced_idents = free_lifetimes(&generics.ty_params);
397 if referenced_idents.is_empty() {
401 generics.lifetimes.iter()
402 .filter(|l| referenced_idents.iter().any(|&i| i == l.name))
407 pub fn free_lifetimes(ty_params: &OwnedSlice<ast::TyParam>) -> Vec<ast::Name> {
409 * Gathers up and returns the names of any lifetimes that appear
410 * free in `ty_params`. Of course, right now, all lifetimes appear
411 * free, since we don't currently have any binders in type parameter
412 * declarations; just being forwards compatible with future extensions.
415 let mut collector = FreeLifetimeCollector { names: vec!() };
416 for ty_param in ty_params.iter() {
417 visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds, ());
419 return collector.names;
421 struct FreeLifetimeCollector {
422 names: Vec<ast::Name>,
425 impl Visitor<()> for FreeLifetimeCollector {
426 fn visit_lifetime_ref(&mut self,
427 lifetime_ref: &ast::Lifetime,
429 self.names.push(lifetime_ref.name);