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.
11 //! Name resolution for lifetimes.
13 //! Name resolution for lifetimes follows MUCH simpler rules than the
14 //! full resolve. For example, lifetime names are never exported or
15 //! used between functions, and they operate in a purely top-down
16 //! way. Therefore we break lifetime name resolution into a separate pass.
18 pub use self::DefRegion::*;
19 use self::ScopeChain::*;
24 use middle::resolve::DefMap;
29 use syntax::codemap::Span;
30 use syntax::parse::token::special_idents;
31 use syntax::parse::token;
32 use syntax::print::pprust::{lifetime_to_string};
34 use syntax::visit::Visitor;
35 use util::nodemap::NodeMap;
37 #[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
40 DefEarlyBoundRegion(/* space */ subst::ParamSpace,
42 /* lifetime decl */ ast::NodeId),
43 DefLateBoundRegion(ty::DebruijnIndex,
44 /* lifetime decl */ ast::NodeId),
45 DefFreeRegion(/* block scope */ region::CodeExtent,
46 /* lifetime decl */ ast::NodeId),
49 impl Copy for DefRegion {}
51 // maps the id of each lifetime reference to the lifetime decl
52 // that it corresponds to
53 pub type NamedRegionMap = NodeMap<DefRegion>;
55 struct LifetimeContext<'a> {
57 named_region_map: &'a mut NamedRegionMap,
63 /// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
64 /// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
65 EarlyScope(subst::ParamSpace, &'a Vec<ast::LifetimeDef>, Scope<'a>),
66 /// LateScope(['a, 'b, ...], s) extends s with late-bound
67 /// lifetimes introduced by the declaration binder_id.
68 LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>),
69 /// lifetimes introduced by items within a code block are scoped
71 BlockScope(region::CodeExtent, Scope<'a>),
75 type Scope<'a> = &'a ScopeChain<'a>;
77 static ROOT_SCOPE: ScopeChain<'static> = RootScope;
79 pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegionMap {
80 let mut named_region_map = NodeMap::new();
81 visit::walk_crate(&mut LifetimeContext {
83 named_region_map: &mut named_region_map,
87 sess.abort_if_errors();
91 impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
92 fn visit_item(&mut self, item: &ast::Item) {
95 // Fn lifetimes get added in visit_fn below:
96 self.with(RootScope, |this| visit::walk_item(this, item));
100 ast::ItemForeignMod(..) |
101 ast::ItemStatic(..) |
102 ast::ItemConst(..) => {
103 // These sorts of items have no lifetime parameters at all.
104 self.with(RootScope, |this| visit::walk_item(this, item));
106 ast::ItemTy(_, ref generics) |
107 ast::ItemEnum(_, ref generics) |
108 ast::ItemStruct(_, ref generics) |
109 ast::ItemTrait(ref generics, _, _, _) => {
110 // These kinds of items have only early bound lifetime parameters.
111 let lifetimes = &generics.lifetimes;
112 self.with(EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE), |this| {
113 this.check_lifetime_defs(lifetimes);
114 visit::walk_item(this, item);
117 ast::ItemImpl(ref generics, _, _, _) => {
118 // Impls have both early- and late-bound lifetimes.
119 self.visit_early_late(subst::TypeSpace, generics, |this| {
120 this.check_lifetime_defs(&generics.lifetimes);
121 visit::walk_item(this, item);
127 fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
128 b: &'v ast::Block, s: Span, _: ast::NodeId) {
130 visit::FkItemFn(_, generics, _, _) |
131 visit::FkMethod(_, generics, _) => {
132 self.visit_early_late(
133 subst::FnSpace, generics,
134 |this| visit::walk_fn(this, fk, fd, b, s))
136 visit::FkFnBlock(..) => {
137 visit::walk_fn(self, fk, fd, b, s)
142 fn visit_ty(&mut self, ty: &ast::Ty) {
144 ast::TyClosure(ref c) | ast::TyProc(ref c) => {
145 // Careful, the bounds on a closure/proc are *not* within its binder.
146 visit::walk_ty_param_bounds_helper(self, &c.bounds);
147 visit::walk_lifetime_decls_helper(self, &c.lifetimes);
148 self.with(LateScope(&c.lifetimes, self.scope), |this| {
149 this.check_lifetime_defs(&c.lifetimes);
150 for argument in c.decl.inputs.iter() {
151 this.visit_ty(&*argument.ty)
153 visit::walk_fn_ret_ty(this, &c.decl.output);
156 ast::TyBareFn(ref c) => {
157 visit::walk_lifetime_decls_helper(self, &c.lifetimes);
158 self.with(LateScope(&c.lifetimes, self.scope), |this| {
159 // a bare fn has no bounds, so everything
160 // contained within is scoped within its binder.
161 this.check_lifetime_defs(&c.lifetimes);
162 visit::walk_ty(this, ty);
165 ast::TyPath(ref path, id) => {
166 // if this path references a trait, then this will resolve to
167 // a trait ref, which introduces a binding scope.
168 match self.def_map.borrow().get(&id) {
169 Some(&def::DefTrait(..)) => {
170 self.with(LateScope(&Vec::new(), self.scope), |this| {
171 this.visit_path(path, id);
175 visit::walk_ty(self, ty);
180 visit::walk_ty(self, ty)
185 fn visit_ty_method(&mut self, m: &ast::TypeMethod) {
186 self.visit_early_late(
187 subst::FnSpace, &m.generics,
188 |this| visit::walk_ty_method(this, m))
191 fn visit_block(&mut self, b: &ast::Block) {
192 self.with(BlockScope(region::CodeExtent::from_node_id(b.id), self.scope),
193 |this| visit::walk_block(this, b));
196 fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
197 if lifetime_ref.name == special_idents::static_lifetime.name {
198 self.insert_lifetime(lifetime_ref, DefStaticRegion);
201 self.resolve_lifetime_ref(lifetime_ref);
204 fn visit_generics(&mut self, generics: &ast::Generics) {
205 for ty_param in generics.ty_params.iter() {
206 visit::walk_ty_param_bounds_helper(self, &ty_param.bounds);
207 match ty_param.default {
208 Some(ref ty) => self.visit_ty(&**ty),
212 for predicate in generics.where_clause.predicates.iter() {
213 self.visit_ident(predicate.span, predicate.ident);
214 visit::walk_ty_param_bounds_helper(self, &predicate.bounds);
218 fn visit_poly_trait_ref(&mut self, trait_ref: &ast::PolyTraitRef) {
219 debug!("visit_poly_trait_ref trait_ref={}", trait_ref);
221 self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |this| {
222 this.check_lifetime_defs(&trait_ref.bound_lifetimes);
223 for lifetime in trait_ref.bound_lifetimes.iter() {
224 this.visit_lifetime_def(lifetime);
226 this.visit_trait_ref(&trait_ref.trait_ref)
230 fn visit_trait_ref(&mut self, trait_ref: &ast::TraitRef) {
231 self.visit_path(&trait_ref.path, trait_ref.ref_id);
235 impl<'a> LifetimeContext<'a> {
236 fn with(&mut self, wrap_scope: ScopeChain, f: |&mut LifetimeContext|) {
237 let LifetimeContext {sess, ref mut named_region_map, ..} = *self;
238 let mut this = LifetimeContext {
240 named_region_map: *named_region_map,
242 def_map: self.def_map,
244 debug!("entering scope {}", this.scope);
246 debug!("exiting scope {}", this.scope);
249 /// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
251 /// Handles visiting fns and methods. These are a bit complicated because we must distinguish
252 /// early- vs late-bound lifetime parameters. We do this by checking which lifetimes appear
253 /// within type bounds; those are early bound lifetimes, and the rest are late bound.
257 /// fn foo<'a,'b,'c,T:Trait<'b>>(...)
259 /// Here `'a` and `'c` are late bound but `'b` is early bound. Note that early- and late-bound
260 /// lifetimes may be interspersed together.
262 /// If early bound lifetimes are present, we separate them into their own list (and likewise
263 /// for late bound). They will be numbered sequentially, starting from the lowest index that is
264 /// already in scope (for a fn item, that will be 0, but for a method it might not be). Late
265 /// bound lifetimes are resolved by name and associated with a binder id (`binder_id`), so the
266 /// ordering is not important there.
267 fn visit_early_late(&mut self,
268 early_space: subst::ParamSpace,
269 generics: &ast::Generics,
270 walk: |&mut LifetimeContext|) {
271 let referenced_idents = early_bound_lifetime_names(generics);
273 debug!("visit_early_late: referenced_idents={}",
276 let (early, late) = generics.lifetimes.clone().partition(
277 |l| referenced_idents.iter().any(|&i| i == l.lifetime.name));
279 self.with(EarlyScope(early_space, &early, self.scope), |this| {
280 this.with(LateScope(&late, this.scope), |this| {
281 this.check_lifetime_defs(&generics.lifetimes);
287 fn resolve_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
288 // Walk up the scope chain, tracking the number of fn scopes
289 // that we pass through, until we find a lifetime with the
290 // given name or we run out of scopes. If we encounter a code
291 // block, then the lifetime is not bound but free, so switch
292 // over to `resolve_free_lifetime_ref()` to complete the
294 let mut late_depth = 0;
295 let mut scope = self.scope;
298 BlockScope(blk_scope, s) => {
299 return self.resolve_free_lifetime_ref(blk_scope, lifetime_ref, s);
306 EarlyScope(space, lifetimes, s) => {
307 match search_lifetimes(lifetimes, lifetime_ref) {
308 Some((index, decl_id)) => {
309 let def = DefEarlyBoundRegion(space, index, decl_id);
310 self.insert_lifetime(lifetime_ref, def);
319 LateScope(lifetimes, s) => {
320 match search_lifetimes(lifetimes, lifetime_ref) {
321 Some((_index, decl_id)) => {
322 let debruijn = ty::DebruijnIndex::new(late_depth + 1);
323 let def = DefLateBoundRegion(debruijn, decl_id);
324 self.insert_lifetime(lifetime_ref, def);
337 self.unresolved_lifetime_ref(lifetime_ref);
340 fn resolve_free_lifetime_ref(&mut self,
341 scope_data: region::CodeExtent,
342 lifetime_ref: &ast::Lifetime,
344 // Walk up the scope chain, tracking the outermost free scope,
345 // until we encounter a scope that contains the named lifetime
346 // or we run out of scopes.
347 let mut scope_data = scope_data;
348 let mut scope = scope;
349 let mut search_result = None;
352 BlockScope(blk_scope_data, s) => {
353 scope_data = blk_scope_data;
361 EarlyScope(_, lifetimes, s) |
362 LateScope(lifetimes, s) => {
363 search_result = search_lifetimes(lifetimes, lifetime_ref);
364 if search_result.is_some() {
372 match search_result {
373 Some((_depth, decl_id)) => {
374 let def = DefFreeRegion(scope_data, decl_id);
375 self.insert_lifetime(lifetime_ref, def);
379 self.unresolved_lifetime_ref(lifetime_ref);
385 fn unresolved_lifetime_ref(&self, lifetime_ref: &ast::Lifetime) {
388 format!("use of undeclared lifetime name `{}`",
389 token::get_name(lifetime_ref.name)).as_slice());
392 fn check_lifetime_defs(&mut self, lifetimes: &Vec<ast::LifetimeDef>) {
393 for i in range(0, lifetimes.len()) {
394 let lifetime_i = &lifetimes[i];
396 let special_idents = [special_idents::static_lifetime];
397 for lifetime in lifetimes.iter() {
398 if special_idents.iter().any(|&i| i.name == lifetime.lifetime.name) {
400 lifetime.lifetime.span,
401 format!("illegal lifetime parameter name: `{}`",
402 token::get_name(lifetime.lifetime.name))
407 for j in range(i + 1, lifetimes.len()) {
408 let lifetime_j = &lifetimes[j];
410 if lifetime_i.lifetime.name == lifetime_j.lifetime.name {
412 lifetime_j.lifetime.span,
413 format!("lifetime name `{}` declared twice in \
415 token::get_name(lifetime_j.lifetime.name))
420 for bound in lifetime_i.bounds.iter() {
421 self.resolve_lifetime_ref(bound);
426 fn insert_lifetime(&mut self,
427 lifetime_ref: &ast::Lifetime,
429 if lifetime_ref.id == ast::DUMMY_NODE_ID {
430 self.sess.span_bug(lifetime_ref.span,
431 "lifetime reference not renumbered, \
432 probably a bug in syntax::fold");
435 debug!("lifetime_ref={} id={} resolved to {}",
436 lifetime_to_string(lifetime_ref),
439 self.named_region_map.insert(lifetime_ref.id, def);
443 fn search_lifetimes(lifetimes: &Vec<ast::LifetimeDef>,
444 lifetime_ref: &ast::Lifetime)
445 -> Option<(uint, ast::NodeId)> {
446 for (i, lifetime_decl) in lifetimes.iter().enumerate() {
447 if lifetime_decl.lifetime.name == lifetime_ref.name {
448 return Some((i, lifetime_decl.lifetime.id));
454 ///////////////////////////////////////////////////////////////////////////
456 pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::LifetimeDef> {
457 let referenced_idents = early_bound_lifetime_names(generics);
458 if referenced_idents.is_empty() {
462 generics.lifetimes.iter()
463 .filter(|l| referenced_idents.iter().any(|&i| i == l.lifetime.name))
464 .map(|l| (*l).clone())
468 /// Given a set of generic declarations, returns a list of names containing all early bound
469 /// lifetime names for those generics. (In fact, this list may also contain other names.)
470 fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec<ast::Name> {
471 // Create two lists, dividing the lifetimes into early/late bound.
472 // Initially, all of them are considered late, but we will move
473 // things from late into early as we go if we find references to
475 let mut early_bound = Vec::new();
476 let mut late_bound = generics.lifetimes.iter()
477 .map(|l| l.lifetime.name)
480 // Any lifetime that appears in a type bound is early.
483 FreeLifetimeCollector { early_bound: &mut early_bound,
484 late_bound: &mut late_bound };
485 for ty_param in generics.ty_params.iter() {
486 visit::walk_ty_param_bounds_helper(&mut collector, &ty_param.bounds);
488 for predicate in generics.where_clause.predicates.iter() {
489 visit::walk_ty_param_bounds_helper(&mut collector, &predicate.bounds);
493 // Any lifetime that either has a bound or is referenced by a
495 for lifetime_def in generics.lifetimes.iter() {
496 if !lifetime_def.bounds.is_empty() {
497 shuffle(&mut early_bound, &mut late_bound,
498 lifetime_def.lifetime.name);
499 for bound in lifetime_def.bounds.iter() {
500 shuffle(&mut early_bound, &mut late_bound,
507 struct FreeLifetimeCollector<'a> {
508 early_bound: &'a mut Vec<ast::Name>,
509 late_bound: &'a mut Vec<ast::Name>,
512 impl<'a, 'v> Visitor<'v> for FreeLifetimeCollector<'a> {
513 fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
514 shuffle(self.early_bound, self.late_bound,
519 fn shuffle(early_bound: &mut Vec<ast::Name>,
520 late_bound: &mut Vec<ast::Name>,
522 match late_bound.iter().position(|n| *n == name) {
524 late_bound.swap_remove(index);
525 early_bound.push(name);
532 impl<'a> fmt::Show for ScopeChain<'a> {
533 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
535 EarlyScope(space, defs, _) => write!(fmt, "EarlyScope({}, {})", space, defs),
536 LateScope(defs, _) => write!(fmt, "LateScope({})", defs),
537 BlockScope(id, _) => write!(fmt, "BlockScope({})", id),
538 RootScope => write!(fmt, "RootScope"),