// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Name resolution for lifetimes. //! //! Name resolution for lifetimes follows MUCH simpler rules than the //! full resolve. For example, lifetime names are never exported or //! used between functions, and they operate in a purely top-down //! way. Therefore we break lifetime name resolution into a separate pass. pub use self::DefRegion::*; use self::ScopeChain::*; use dep_graph::DepNode; use front::map::Map; use session::Session; use middle::def::{Def, DefMap}; use middle::region; use middle::subst; use middle::ty; use std::fmt; use std::mem::replace; use syntax::ast; use syntax::codemap::Span; use syntax::parse::token::special_idents; use util::nodemap::NodeMap; use rustc_front::hir; use rustc_front::print::pprust::lifetime_to_string; use rustc_front::intravisit::{self, Visitor, FnKind}; #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] pub enum DefRegion { DefStaticRegion, DefEarlyBoundRegion(/* space */ subst::ParamSpace, /* index */ u32, /* lifetime decl */ ast::NodeId), DefLateBoundRegion(ty::DebruijnIndex, /* lifetime decl */ ast::NodeId), DefFreeRegion(region::CallSiteScopeData, /* lifetime decl */ ast::NodeId), } // Maps the id of each lifetime reference to the lifetime decl // that it corresponds to. pub type NamedRegionMap = NodeMap; struct LifetimeContext<'a> { sess: &'a Session, named_region_map: &'a mut NamedRegionMap, scope: Scope<'a>, def_map: &'a DefMap, // Deep breath. Our representation for poly trait refs contains a single // binder and thus we only allow a single level of quantification. However, // the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>` // and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the de Bruijn indices // correct when representing these constraints, we should only introduce one // scope. However, we want to support both locations for the quantifier and // during lifetime resolution we want precise information (so we can't // desugar in an earlier phase). // SO, if we encounter a quantifier at the outer scope, we set // trait_ref_hack to true (and introduce a scope), and then if we encounter // a quantifier at the inner scope, we error. If trait_ref_hack is false, // then we introduce the scope at the inner quantifier. // I'm sorry. trait_ref_hack: bool, // List of labels in the function/method currently under analysis. labels_in_fn: Vec<(ast::Name, Span)>, } enum ScopeChain<'a> { /// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound /// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc. EarlyScope(subst::ParamSpace, &'a [hir::LifetimeDef], Scope<'a>), /// LateScope(['a, 'b, ...], s) extends s with late-bound /// lifetimes introduced by the declaration binder_id. LateScope(&'a [hir::LifetimeDef], Scope<'a>), /// lifetimes introduced by a fn are scoped to the call-site for that fn. FnScope { fn_id: ast::NodeId, body_id: ast::NodeId, s: Scope<'a> }, RootScope } type Scope<'a> = &'a ScopeChain<'a>; static ROOT_SCOPE: ScopeChain<'static> = RootScope; pub fn krate(sess: &Session, hir_map: &Map, def_map: &DefMap) -> Result { let _task = hir_map.dep_graph.in_task(DepNode::ResolveLifetimes); let krate = hir_map.krate(); let mut named_region_map = NodeMap(); try!(sess.track_errors(|| { krate.visit_all_items(&mut LifetimeContext { sess: sess, named_region_map: &mut named_region_map, scope: &ROOT_SCOPE, def_map: def_map, trait_ref_hack: false, labels_in_fn: vec![], }); })); Ok(named_region_map) } impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { fn visit_item(&mut self, item: &hir::Item) { assert!(self.labels_in_fn.is_empty()); // Items always introduce a new root scope self.with(RootScope, |_, this| { match item.node { hir::ItemFn(..) => { // Fn lifetimes get added in visit_fn below: intravisit::walk_item(this, item); } hir::ItemExternCrate(_) | hir::ItemUse(_) | hir::ItemMod(..) | hir::ItemDefaultImpl(..) | hir::ItemForeignMod(..) | hir::ItemStatic(..) | hir::ItemConst(..) => { // These sorts of items have no lifetime parameters at all. intravisit::walk_item(this, item); } hir::ItemTy(_, ref generics) | hir::ItemEnum(_, ref generics) | hir::ItemStruct(_, ref generics) | hir::ItemTrait(_, ref generics, _, _) | hir::ItemImpl(_, _, ref generics, _, _, _) => { // These kinds of items have only early bound lifetime parameters. let lifetimes = &generics.lifetimes; let early_scope = EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE); this.with(early_scope, |old_scope, this| { this.check_lifetime_defs(old_scope, lifetimes); intravisit::walk_item(this, item); }); } } }); // Done traversing the item; remove any labels it created self.labels_in_fn.truncate(0); } fn visit_foreign_item(&mut self, item: &hir::ForeignItem) { // Items save/restore the set of labels. This way inner items // can freely reuse names, be they loop labels or lifetimes. let saved = replace(&mut self.labels_in_fn, vec![]); // Items always introduce a new root scope self.with(RootScope, |_, this| { match item.node { hir::ForeignItemFn(_, ref generics) => { this.visit_early_late(subst::FnSpace, generics, |this| { intravisit::walk_foreign_item(this, item); }) } hir::ForeignItemStatic(..) => { intravisit::walk_foreign_item(this, item); } } }); // Done traversing the item; restore saved set of labels. replace(&mut self.labels_in_fn, saved); } fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl, b: &'v hir::Block, s: Span, fn_id: ast::NodeId) { match fk { FnKind::ItemFn(_, generics, _, _, _, _) => { self.visit_early_late(subst::FnSpace, generics, |this| { this.add_scope_and_walk_fn(fk, fd, b, s, fn_id) }) } FnKind::Method(_, sig, _) => { self.visit_early_late(subst::FnSpace, &sig.generics, |this| { this.add_scope_and_walk_fn(fk, fd, b, s, fn_id) }) } FnKind::Closure => { self.add_scope_and_walk_fn(fk, fd, b, s, fn_id) } } } fn visit_ty(&mut self, ty: &hir::Ty) { match ty.node { hir::TyBareFn(ref c) => { self.with(LateScope(&c.lifetimes, self.scope), |old_scope, this| { // a bare fn has no bounds, so everything // contained within is scoped within its binder. this.check_lifetime_defs(old_scope, &c.lifetimes); intravisit::walk_ty(this, ty); }); } hir::TyPath(None, ref path) => { // if this path references a trait, then this will resolve to // a trait ref, which introduces a binding scope. match self.def_map.get(&ty.id).map(|d| (d.base_def, d.depth)) { Some((Def::Trait(..), 0)) => { self.with(LateScope(&[], self.scope), |_, this| { this.visit_path(path, ty.id); }); } _ => { intravisit::walk_ty(self, ty); } } } _ => { intravisit::walk_ty(self, ty) } } } fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) { // We reset the labels on every trait item, so that different // methods in an impl can reuse label names. let saved = replace(&mut self.labels_in_fn, vec![]); if let hir::MethodTraitItem(ref sig, None) = trait_item.node { self.visit_early_late( subst::FnSpace, &sig.generics, |this| intravisit::walk_trait_item(this, trait_item)) } else { intravisit::walk_trait_item(self, trait_item); } replace(&mut self.labels_in_fn, saved); } fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { if lifetime_ref.name == special_idents::static_lifetime.name { self.insert_lifetime(lifetime_ref, DefStaticRegion); return; } self.resolve_lifetime_ref(lifetime_ref); } fn visit_generics(&mut self, generics: &hir::Generics) { for ty_param in generics.ty_params.iter() { walk_list!(self, visit_ty_param_bound, &ty_param.bounds); match ty_param.default { Some(ref ty) => self.visit_ty(&ty), None => {} } } for predicate in &generics.where_clause.predicates { match predicate { &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate{ ref bounded_ty, ref bounds, ref bound_lifetimes, .. }) => { if !bound_lifetimes.is_empty() { self.trait_ref_hack = true; let result = self.with(LateScope(bound_lifetimes, self.scope), |old_scope, this| { this.check_lifetime_defs(old_scope, bound_lifetimes); this.visit_ty(&bounded_ty); walk_list!(this, visit_ty_param_bound, bounds); }); self.trait_ref_hack = false; result } else { self.visit_ty(&bounded_ty); walk_list!(self, visit_ty_param_bound, bounds); } } &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate{ref lifetime, ref bounds, .. }) => { self.visit_lifetime(lifetime); for bound in bounds { self.visit_lifetime(bound); } } &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate{ id, ref path, ref ty, .. }) => { self.visit_path(path, id); self.visit_ty(&ty); } } } } fn visit_poly_trait_ref(&mut self, trait_ref: &hir::PolyTraitRef, _modifier: &hir::TraitBoundModifier) { debug!("visit_poly_trait_ref trait_ref={:?}", trait_ref); if !self.trait_ref_hack || !trait_ref.bound_lifetimes.is_empty() { if self.trait_ref_hack { println!("{:?}", trait_ref.span); span_err!(self.sess, trait_ref.span, E0316, "nested quantification of lifetimes"); } self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |old_scope, this| { this.check_lifetime_defs(old_scope, &trait_ref.bound_lifetimes); for lifetime in &trait_ref.bound_lifetimes { this.visit_lifetime_def(lifetime); } intravisit::walk_path(this, &trait_ref.trait_ref.path) }) } else { self.visit_trait_ref(&trait_ref.trait_ref) } } } #[derive(Copy, Clone, PartialEq)] enum ShadowKind { Label, Lifetime } struct Original { kind: ShadowKind, span: Span } struct Shadower { kind: ShadowKind, span: Span } fn original_label(span: Span) -> Original { Original { kind: ShadowKind::Label, span: span } } fn shadower_label(span: Span) -> Shadower { Shadower { kind: ShadowKind::Label, span: span } } fn original_lifetime(l: &hir::Lifetime) -> Original { Original { kind: ShadowKind::Lifetime, span: l.span } } fn shadower_lifetime(l: &hir::Lifetime) -> Shadower { Shadower { kind: ShadowKind::Lifetime, span: l.span } } impl ShadowKind { fn desc(&self) -> &'static str { match *self { ShadowKind::Label => "label", ShadowKind::Lifetime => "lifetime", } } } fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) { let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) { // lifetime/lifetime shadowing is an error struct_span_err!(sess, shadower.span, E0496, "{} name `{}` shadows a \ {} name that is already in scope", shadower.kind.desc(), name, orig.kind.desc()) } else { // shadowing involving a label is only a warning, due to issues with // labels and lifetimes not being macro-hygienic. sess.struct_span_warn(shadower.span, &format!("{} name `{}` shadows a \ {} name that is already in scope", shadower.kind.desc(), name, orig.kind.desc())) }; err.span_note(orig.span, &format!("shadowed {} `{}` declared here", orig.kind.desc(), name)); err.emit(); } // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning // if one of the label shadows a lifetime or another label. fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) { struct GatherLabels<'a> { sess: &'a Session, scope: Scope<'a>, labels_in_fn: &'a mut Vec<(ast::Name, Span)>, } let mut gather = GatherLabels { sess: ctxt.sess, scope: ctxt.scope, labels_in_fn: &mut ctxt.labels_in_fn, }; gather.visit_block(b); return; impl<'v, 'a> Visitor<'v> for GatherLabels<'a> { fn visit_expr(&mut self, ex: &'v hir::Expr) { // do not recurse into closures defined in the block // since they are treated as separate fns from the POV of // labels_in_fn if let hir::ExprClosure(..) = ex.node { return } if let Some(label) = expression_label(ex) { for &(prior, prior_span) in &self.labels_in_fn[..] { // FIXME (#24278): non-hygienic comparison if label == prior { signal_shadowing_problem(self.sess, label, original_label(prior_span), shadower_label(ex.span)); } } check_if_label_shadows_lifetime(self.sess, self.scope, label, ex.span); self.labels_in_fn.push((label, ex.span)); } intravisit::walk_expr(self, ex) } fn visit_item(&mut self, _: &hir::Item) { // do not recurse into items defined in the block } } fn expression_label(ex: &hir::Expr) -> Option { match ex.node { hir::ExprWhile(_, _, Some(label)) | hir::ExprLoop(_, Some(label)) => Some(label.unhygienic_name), _ => None, } } fn check_if_label_shadows_lifetime<'a>(sess: &'a Session, mut scope: Scope<'a>, label: ast::Name, label_span: Span) { loop { match *scope { FnScope { s, .. } => { scope = s; } RootScope => { return; } EarlyScope(_, lifetimes, s) | LateScope(lifetimes, s) => { for lifetime_def in lifetimes { // FIXME (#24278): non-hygienic comparison if label == lifetime_def.lifetime.name { signal_shadowing_problem( sess, label, original_lifetime(&lifetime_def.lifetime), shadower_label(label_span)); return; } } scope = s; } } } } } impl<'a> LifetimeContext<'a> { fn add_scope_and_walk_fn<'b>(&mut self, fk: FnKind, fd: &hir::FnDecl, fb: &'b hir::Block, _span: Span, fn_id: ast::NodeId) { match fk { FnKind::ItemFn(_, generics, _, _, _, _) => { intravisit::walk_fn_decl(self, fd); self.visit_generics(generics); } FnKind::Method(_, sig, _) => { intravisit::walk_fn_decl(self, fd); self.visit_generics(&sig.generics); self.visit_explicit_self(&sig.explicit_self); } FnKind::Closure => { intravisit::walk_fn_decl(self, fd); } } // After inpsecting the decl, add all labels from the body to // `self.labels_in_fn`. extract_labels(self, fb); self.with(FnScope { fn_id: fn_id, body_id: fb.id, s: self.scope }, |_old_scope, this| this.visit_block(fb)) } fn with(&mut self, wrap_scope: ScopeChain, f: F) where F: FnOnce(Scope, &mut LifetimeContext), { let LifetimeContext {sess, ref mut named_region_map, ..} = *self; let mut this = LifetimeContext { sess: sess, named_region_map: *named_region_map, scope: &wrap_scope, def_map: self.def_map, trait_ref_hack: self.trait_ref_hack, labels_in_fn: self.labels_in_fn.clone(), }; debug!("entering scope {:?}", this.scope); f(self.scope, &mut this); debug!("exiting scope {:?}", this.scope); } /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. /// /// Handles visiting fns and methods. These are a bit complicated because we must distinguish /// early- vs late-bound lifetime parameters. We do this by checking which lifetimes appear /// within type bounds; those are early bound lifetimes, and the rest are late bound. /// /// For example: /// /// fn foo<'a,'b,'c,T:Trait<'b>>(...) /// /// Here `'a` and `'c` are late bound but `'b` is early bound. Note that early- and late-bound /// lifetimes may be interspersed together. /// /// If early bound lifetimes are present, we separate them into their own list (and likewise /// for late bound). They will be numbered sequentially, starting from the lowest index that is /// already in scope (for a fn item, that will be 0, but for a method it might not be). Late /// bound lifetimes are resolved by name and associated with a binder id (`binder_id`), so the /// ordering is not important there. fn visit_early_late(&mut self, early_space: subst::ParamSpace, generics: &hir::Generics, walk: F) where F: FnOnce(&mut LifetimeContext), { let referenced_idents = early_bound_lifetime_names(generics); debug!("visit_early_late: referenced_idents={:?}", referenced_idents); let (early, late): (Vec<_>, _) = generics.lifetimes.iter().cloned().partition( |l| referenced_idents.iter().any(|&i| i == l.lifetime.name)); self.with(EarlyScope(early_space, &early, self.scope), move |old_scope, this| { this.with(LateScope(&late, this.scope), move |_, this| { this.check_lifetime_defs(old_scope, &generics.lifetimes); walk(this); }); }); } fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) { // Walk up the scope chain, tracking the number of fn scopes // that we pass through, until we find a lifetime with the // given name or we run out of scopes. If we encounter a code // block, then the lifetime is not bound but free, so switch // over to `resolve_free_lifetime_ref()` to complete the // search. let mut late_depth = 0; let mut scope = self.scope; loop { match *scope { FnScope {fn_id, body_id, s } => { return self.resolve_free_lifetime_ref( region::CallSiteScopeData { fn_id: fn_id, body_id: body_id }, lifetime_ref, s); } RootScope => { break; } EarlyScope(space, lifetimes, s) => { match search_lifetimes(lifetimes, lifetime_ref) { Some((index, lifetime_def)) => { let decl_id = lifetime_def.id; let def = DefEarlyBoundRegion(space, index, decl_id); self.insert_lifetime(lifetime_ref, def); return; } None => { scope = s; } } } LateScope(lifetimes, s) => { match search_lifetimes(lifetimes, lifetime_ref) { Some((_index, lifetime_def)) => { let decl_id = lifetime_def.id; let debruijn = ty::DebruijnIndex::new(late_depth + 1); let def = DefLateBoundRegion(debruijn, decl_id); self.insert_lifetime(lifetime_ref, def); return; } None => { late_depth += 1; scope = s; } } } } } self.unresolved_lifetime_ref(lifetime_ref); } fn resolve_free_lifetime_ref(&mut self, scope_data: region::CallSiteScopeData, lifetime_ref: &hir::Lifetime, scope: Scope) { debug!("resolve_free_lifetime_ref \ scope_data: {:?} lifetime_ref: {:?} scope: {:?}", scope_data, lifetime_ref, scope); // Walk up the scope chain, tracking the outermost free scope, // until we encounter a scope that contains the named lifetime // or we run out of scopes. let mut scope_data = scope_data; let mut scope = scope; let mut search_result = None; loop { debug!("resolve_free_lifetime_ref \ scope_data: {:?} scope: {:?} search_result: {:?}", scope_data, scope, search_result); match *scope { FnScope { fn_id, body_id, s } => { scope_data = region::CallSiteScopeData { fn_id: fn_id, body_id: body_id }; scope = s; } RootScope => { break; } EarlyScope(_, lifetimes, s) | LateScope(lifetimes, s) => { search_result = search_lifetimes(lifetimes, lifetime_ref); if search_result.is_some() { break; } scope = s; } } } match search_result { Some((_depth, lifetime)) => { let def = DefFreeRegion(scope_data, lifetime.id); self.insert_lifetime(lifetime_ref, def); } None => { self.unresolved_lifetime_ref(lifetime_ref); } } } fn unresolved_lifetime_ref(&self, lifetime_ref: &hir::Lifetime) { span_err!(self.sess, lifetime_ref.span, E0261, "use of undeclared lifetime name `{}`", lifetime_ref.name); } fn check_lifetime_defs(&mut self, old_scope: Scope, lifetimes: &[hir::LifetimeDef]) { for i in 0..lifetimes.len() { let lifetime_i = &lifetimes[i]; let special_idents = [special_idents::static_lifetime]; for lifetime in lifetimes { if special_idents.iter().any(|&i| i.name == lifetime.lifetime.name) { span_err!(self.sess, lifetime.lifetime.span, E0262, "invalid lifetime parameter name: `{}`", lifetime.lifetime.name); } } // It is a hard error to shadow a lifetime within the same scope. for j in i + 1..lifetimes.len() { let lifetime_j = &lifetimes[j]; if lifetime_i.lifetime.name == lifetime_j.lifetime.name { span_err!(self.sess, lifetime_j.lifetime.span, E0263, "lifetime name `{}` declared twice in \ the same scope", lifetime_j.lifetime.name); } } // It is a soft error to shadow a lifetime within a parent scope. self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime); for bound in &lifetime_i.bounds { self.resolve_lifetime_ref(bound); } } } fn check_lifetime_def_for_shadowing(&self, mut old_scope: Scope, lifetime: &hir::Lifetime) { for &(label, label_span) in &self.labels_in_fn { // FIXME (#24278): non-hygienic comparison if lifetime.name == label { signal_shadowing_problem(self.sess, lifetime.name, original_label(label_span), shadower_lifetime(&lifetime)); return; } } loop { match *old_scope { FnScope { s, .. } => { old_scope = s; } RootScope => { return; } EarlyScope(_, lifetimes, s) | LateScope(lifetimes, s) => { if let Some((_, lifetime_def)) = search_lifetimes(lifetimes, lifetime) { signal_shadowing_problem( self.sess, lifetime.name, original_lifetime(&lifetime_def), shadower_lifetime(&lifetime)); return; } old_scope = s; } } } } fn insert_lifetime(&mut self, lifetime_ref: &hir::Lifetime, def: DefRegion) { if lifetime_ref.id == ast::DUMMY_NODE_ID { self.sess.span_bug(lifetime_ref.span, "lifetime reference not renumbered, \ probably a bug in syntax::fold"); } debug!("lifetime_ref={:?} id={:?} resolved to {:?}", lifetime_to_string(lifetime_ref), lifetime_ref.id, def); self.named_region_map.insert(lifetime_ref.id, def); } } fn search_lifetimes<'a>(lifetimes: &'a [hir::LifetimeDef], lifetime_ref: &hir::Lifetime) -> Option<(u32, &'a hir::Lifetime)> { for (i, lifetime_decl) in lifetimes.iter().enumerate() { if lifetime_decl.lifetime.name == lifetime_ref.name { return Some((i as u32, &lifetime_decl.lifetime)); } } return None; } /////////////////////////////////////////////////////////////////////////// pub fn early_bound_lifetimes<'a>(generics: &'a hir::Generics) -> Vec { let referenced_idents = early_bound_lifetime_names(generics); if referenced_idents.is_empty() { return Vec::new(); } generics.lifetimes.iter() .filter(|l| referenced_idents.iter().any(|&i| i == l.lifetime.name)) .cloned() .collect() } /// Given a set of generic declarations, returns a list of names containing all early bound /// lifetime names for those generics. (In fact, this list may also contain other names.) fn early_bound_lifetime_names(generics: &hir::Generics) -> Vec { // Create two lists, dividing the lifetimes into early/late bound. // Initially, all of them are considered late, but we will move // things from late into early as we go if we find references to // them. let mut early_bound = Vec::new(); let mut late_bound = generics.lifetimes.iter() .map(|l| l.lifetime.name) .collect(); // Any lifetime that appears in a type bound is early. { let mut collector = FreeLifetimeCollector { early_bound: &mut early_bound, late_bound: &mut late_bound }; for ty_param in generics.ty_params.iter() { walk_list!(&mut collector, visit_ty_param_bound, &ty_param.bounds); } for predicate in &generics.where_clause.predicates { match predicate { &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate{ref bounds, ref bounded_ty, ..}) => { collector.visit_ty(&bounded_ty); walk_list!(&mut collector, visit_ty_param_bound, bounds); } &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate{ref lifetime, ref bounds, ..}) => { collector.visit_lifetime(lifetime); for bound in bounds { collector.visit_lifetime(bound); } } &hir::WherePredicate::EqPredicate(_) => unimplemented!() } } } // Any lifetime that either has a bound or is referenced by a // bound is early. for lifetime_def in &generics.lifetimes { if !lifetime_def.bounds.is_empty() { shuffle(&mut early_bound, &mut late_bound, lifetime_def.lifetime.name); for bound in &lifetime_def.bounds { shuffle(&mut early_bound, &mut late_bound, bound.name); } } } return early_bound; struct FreeLifetimeCollector<'a> { early_bound: &'a mut Vec, late_bound: &'a mut Vec, } impl<'a, 'v> Visitor<'v> for FreeLifetimeCollector<'a> { fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { shuffle(self.early_bound, self.late_bound, lifetime_ref.name); } } fn shuffle(early_bound: &mut Vec, late_bound: &mut Vec, name: ast::Name) { match late_bound.iter().position(|n| *n == name) { Some(index) => { late_bound.swap_remove(index); early_bound.push(name); } None => { } } } } impl<'a> fmt::Debug for ScopeChain<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { EarlyScope(space, defs, _) => write!(fmt, "EarlyScope({:?}, {:?})", space, defs), LateScope(defs, _) => write!(fmt, "LateScope({:?})", defs), FnScope { fn_id, body_id, s: _ } => write!(fmt, "FnScope({:?}, {:?})", fn_id, body_id), RootScope => write!(fmt, "RootScope"), } } }