-// 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+//! Name resolution for lifetimes: type declarations.
-//! 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.
+use crate::ty;
-pub use self::DefRegion::*;
-use self::ScopeChain::*;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{GenericParam, ItemLocalId};
+use rustc_hir::{GenericParamKind, LifetimeParamKind};
+use rustc_macros::HashStable;
-use dep_graph::DepNode;
-use hir::map::Map;
-use session::Session;
-use hir::def::{Def, DefMap};
-use hir::def_id::DefId;
-use middle::region;
-use ty::subst;
-use ty;
-use std::fmt;
-use std::mem::replace;
-use syntax::ast;
-use syntax::parse::token::keywords;
-use syntax_pos::Span;
-use util::nodemap::NodeMap;
-
-use rustc_data_structures::fnv::FnvHashSet;
-use hir;
-use hir::print::lifetime_to_string;
-use hir::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 struct NamedRegionMap {
- // maps from every use of a named (not anonymous) lifetime to a
- // `DefRegion` describing how that region is bound
- pub defs: NodeMap<DefRegion>,
-
- // the set of lifetime def ids that are late-bound; late-bound ids
- // are named regions appearing in fn arguments that do not appear
- // in where-clauses
- pub late_bound: NodeMap<ty::Issue32330>,
-}
-
-struct LifetimeContext<'a, 'tcx: 'a> {
- sess: &'a Session,
- hir_map: &'a Map<'tcx>,
- 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<NamedRegionMap, usize> {
- let _task = hir_map.dep_graph.in_task(DepNode::ResolveLifetimes);
- let krate = hir_map.krate();
- let mut map = NamedRegionMap {
- defs: NodeMap(),
- late_bound: NodeMap(),
- };
- sess.track_errors(|| {
- krate.visit_all_items(&mut LifetimeContext {
- sess: sess,
- hir_map: hir_map,
- map: &mut map,
- scope: &ROOT_SCOPE,
- def_map: def_map,
- trait_ref_hack: false,
- labels_in_fn: vec![],
- });
- })?;
- Ok(map)
-}
-
-impl<'a, 'tcx, 'v> Visitor<'v> for LifetimeContext<'a, 'tcx> {
- 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 decl, ref generics) => {
- this.visit_early_late(item.id,
- subst::FnSpace,
- decl,
- 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>, decl: &'v hir::FnDecl,
- b: &'v hir::Block, s: Span, fn_id: ast::NodeId) {
- match fk {
- FnKind::ItemFn(_, generics, _, _, _, _, _) => {
- self.visit_early_late(fn_id, subst::FnSpace, decl, generics, |this| {
- this.add_scope_and_walk_fn(fk, decl, b, s, fn_id)
- })
- }
- FnKind::Method(_, sig, _, _) => {
- self.visit_early_late(
- fn_id,
- subst::FnSpace,
- decl,
- &sig.generics,
- |this| this.add_scope_and_walk_fn(fk, decl, b, s, fn_id));
- }
- FnKind::Closure(_) => {
- // Closures have their own set of labels, save labels just
- // like for foreign items above.
- let saved = replace(&mut self.labels_in_fn, vec![]);
- let result = self.add_scope_and_walk_fn(fk, decl, b, s, fn_id);
- replace(&mut self.labels_in_fn, saved);
- result
- }
- }
- }
-
- 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(
- trait_item.id, subst::FnSpace,
- &sig.decl, &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 == keywords::StaticLifetime.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);
- if let Some(ref ty) = ty_param.default {
- self.visit_ty(&ty);
- }
- }
- 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(ctxt: &mut LifetimeContext, b: &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, label_span)) = 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(label_span));
- }
- }
-
- check_if_label_shadows_lifetime(self.sess,
- self.scope,
- label,
- label_span);
-
- self.labels_in_fn.push((label, label_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<(ast::Name, Span)> {
- match ex.node {
- hir::ExprWhile(_, _, Some(label)) |
- hir::ExprLoop(_, Some(label)) => Some((label.node, label.span)),
- _ => 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, 'tcx> LifetimeContext<'a, 'tcx> {
- 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);
- }
- 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<F>(&mut self, wrap_scope: ScopeChain, f: F) where
- F: FnOnce(Scope, &mut LifetimeContext),
- {
- let LifetimeContext {sess, hir_map, ref mut map, ..} = *self;
- let mut this = LifetimeContext {
- sess: sess,
- hir_map: hir_map,
- map: *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<F>(&mut self,
- fn_id: ast::NodeId,
- early_space: subst::ParamSpace,
- decl: &hir::FnDecl,
- generics: &hir::Generics,
- walk: F) where
- F: FnOnce(&mut LifetimeContext),
- {
- let fn_def_id = self.hir_map.local_def_id(fn_id);
- insert_late_bound_lifetimes(self.map,
- fn_def_id,
- decl,
- generics);
-
- let (late, early): (Vec<_>, _) =
- generics.lifetimes
- .iter()
- .cloned()
- .partition(|l| self.map.late_bound.contains_key(&l.lifetime.id));
-
- let this = self;
- this.with(EarlyScope(early_space, &early, this.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) {
- struct_span_err!(self.sess, lifetime_ref.span, E0261,
- "use of undeclared lifetime name `{}`", lifetime_ref.name)
- .span_label(lifetime_ref.span, &format!("undeclared lifetime"))
- .emit();
- }
-
- fn check_lifetime_defs(&mut self, old_scope: Scope, lifetimes: &[hir::LifetimeDef]) {
- for i in 0..lifetimes.len() {
- let lifetime_i = &lifetimes[i];
-
- for lifetime in lifetimes {
- if lifetime.lifetime.name == keywords::StaticLifetime.name() {
- let lifetime = lifetime.lifetime;
- let mut err = struct_span_err!(self.sess, lifetime.span, E0262,
- "invalid lifetime parameter name: `{}`", lifetime.name);
- err.span_label(lifetime.span,
- &format!("{} is a reserved lifetime name", lifetime.name));
- err.emit();
- }
- }
-
- // 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 {
- struct_span_err!(self.sess, lifetime_j.lifetime.span, E0263,
- "lifetime name `{}` declared twice in the same scope",
- lifetime_j.lifetime.name)
- .span_label(lifetime_j.lifetime.span,
- &format!("declared twice"))
- .span_label(lifetime_i.lifetime.span,
- &format!("previous declaration here"))
- .emit();
- }
- }
-
- // 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 {
- span_bug!(lifetime_ref.span,
- "lifetime reference not renumbered, \
- probably a bug in syntax::fold");
- }
-
- debug!("lifetime_ref={:?} id={:?} resolved to {:?} span={:?}",
- lifetime_to_string(lifetime_ref),
- lifetime_ref.id,
- def,
- self.sess.codemap().span_to_string(lifetime_ref.span));
- self.map.defs.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;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-/// Detects late-bound lifetimes and inserts them into
-/// `map.late_bound`.
+/// The origin of a named lifetime definition.
///
-/// A region declared on a fn is **late-bound** if:
-/// - it is constrained by an argument type;
-/// - it does not appear in a where-clause.
-///
-/// "Constrained" basically means that it appears in any type but
-/// not amongst the inputs to a projection. In other words, `<&'a
-/// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`.
-fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
- fn_def_id: DefId,
- decl: &hir::FnDecl,
- generics: &hir::Generics) {
- debug!("insert_late_bound_lifetimes(decl={:?}, generics={:?})", decl, generics);
-
- let mut constrained_by_input = ConstrainedCollector { regions: FnvHashSet() };
- for arg in &decl.inputs {
- constrained_by_input.visit_ty(&arg.ty);
- }
-
- let mut appears_in_output = AllCollector {
- regions: FnvHashSet(),
- impl_trait: false
- };
- intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
-
- debug!("insert_late_bound_lifetimes: constrained_by_input={:?}",
- constrained_by_input.regions);
-
- // Walk the lifetimes that appear in where clauses.
- //
- // Subtle point: because we disallow nested bindings, we can just
- // ignore binders here and scrape up all names we see.
- let mut appears_in_where_clause = AllCollector {
- regions: FnvHashSet(),
- impl_trait: false
- };
- for ty_param in generics.ty_params.iter() {
- walk_list!(&mut appears_in_where_clause,
- visit_ty_param_bound,
- &ty_param.bounds);
- }
- walk_list!(&mut appears_in_where_clause,
- visit_where_predicate,
- &generics.where_clause.predicates);
- for lifetime_def in &generics.lifetimes {
- if !lifetime_def.bounds.is_empty() {
- // `'a: 'b` means both `'a` and `'b` are referenced
- appears_in_where_clause.visit_lifetime_def(lifetime_def);
- }
- }
-
- debug!("insert_late_bound_lifetimes: appears_in_where_clause={:?}",
- appears_in_where_clause.regions);
-
- // Late bound regions are those that:
- // - appear in the inputs
- // - do not appear in the where-clauses
- // - are not implicitly captured by `impl Trait`
- for lifetime in &generics.lifetimes {
- let name = lifetime.lifetime.name;
-
- // appears in the where clauses? early-bound.
- if appears_in_where_clause.regions.contains(&name) { continue; }
-
- // any `impl Trait` in the return type? early-bound.
- if appears_in_output.impl_trait { continue; }
-
- // does not appear in the inputs, but appears in the return
- // type? eventually this will be early-bound, but for now we
- // just mark it so we can issue warnings.
- let constrained_by_input = constrained_by_input.regions.contains(&name);
- let appears_in_output = appears_in_output.regions.contains(&name);
- let will_change = !constrained_by_input && appears_in_output;
- let issue_32330 = if will_change {
- ty::Issue32330::WillChange {
- fn_def_id: fn_def_id,
- region_name: name,
- }
- } else {
- ty::Issue32330::WontChange
+/// This is used to prevent the usage of in-band lifetimes in `Fn`/`fn` syntax.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, HashStable)]
+pub enum LifetimeDefOrigin {
+ // Explicit binders like `fn foo<'a>(x: &'a u8)` or elided like `impl Foo<&u32>`
+ ExplicitOrElided,
+ // In-band declarations like `fn foo(x: &'a u8)`
+ InBand,
+ // Some kind of erroneous origin
+ Error,
+}
+
+impl LifetimeDefOrigin {
+ pub fn from_param(param: &GenericParam<'_>) -> Self {
+ match param.kind {
+ GenericParamKind::Lifetime { kind } => match kind {
+ LifetimeParamKind::InBand => LifetimeDefOrigin::InBand,
+ LifetimeParamKind::Explicit => LifetimeDefOrigin::ExplicitOrElided,
+ LifetimeParamKind::Elided => LifetimeDefOrigin::ExplicitOrElided,
+ LifetimeParamKind::Error => LifetimeDefOrigin::Error,
+ },
+ _ => bug!("expected a lifetime param"),
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, HashStable)]
+pub enum Region {
+ Static,
+ EarlyBound(/* index */ u32, /* lifetime decl */ DefId, LifetimeDefOrigin),
+ LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId, LifetimeDefOrigin),
+ LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32),
+ Free(DefId, /* lifetime decl */ DefId),
+}
+
+/// A set containing, at most, one known element.
+/// If two distinct values are inserted into a set, then it
+/// becomes `Many`, which can be used to detect ambiguities.
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable)]
+pub enum Set1<T> {
+ Empty,
+ One(T),
+ Many,
+}
+
+impl<T: PartialEq> Set1<T> {
+ pub fn insert(&mut self, value: T) {
+ *self = match self {
+ Set1::Empty => Set1::One(value),
+ Set1::One(old) if *old == value => return,
+ _ => Set1::Many,
};
-
- debug!("insert_late_bound_lifetimes: \
- lifetime {:?} with id {:?} is late-bound ({:?}",
- lifetime.lifetime.name, lifetime.lifetime.id, issue_32330);
-
- let prev = map.late_bound.insert(lifetime.lifetime.id, issue_32330);
- assert!(prev.is_none(), "visited lifetime {:?} twice", lifetime.lifetime.id);
- }
-
- return;
-
- struct ConstrainedCollector {
- regions: FnvHashSet<ast::Name>,
- }
-
- impl<'v> Visitor<'v> for ConstrainedCollector {
- fn visit_ty(&mut self, ty: &'v hir::Ty) {
- match ty.node {
- hir::TyPath(Some(_), _) => {
- // ignore lifetimes appearing in associated type
- // projections, as they are not *constrained*
- // (defined above)
- }
-
- hir::TyPath(None, ref path) => {
- // consider only the lifetimes on the final
- // segment; I am not sure it's even currently
- // valid to have them elsewhere, but even if it
- // is, those would be potentially inputs to
- // projections
- if let Some(last_segment) = path.segments.last() {
- self.visit_path_segment(path.span, last_segment);
- }
- }
-
- _ => {
- intravisit::walk_ty(self, ty);
- }
- }
- }
-
- fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
- self.regions.insert(lifetime_ref.name);
- }
}
+}
- struct AllCollector {
- regions: FnvHashSet<ast::Name>,
- impl_trait: bool
- }
+pub type ObjectLifetimeDefault = Set1<Region>;
- impl<'v> Visitor<'v> for AllCollector {
- fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
- self.regions.insert(lifetime_ref.name);
- }
+/// Maps the id of each lifetime reference to the lifetime decl
+/// that it corresponds to.
+#[derive(Default, HashStable)]
+pub struct ResolveLifetimes {
+ /// Maps from every use of a named (not anonymous) lifetime to a
+ /// `Region` describing how that region is bound
+ pub defs: FxHashMap<LocalDefId, FxHashMap<ItemLocalId, Region>>,
- fn visit_ty(&mut self, ty: &hir::Ty) {
- if let hir::TyImplTrait(_) = ty.node {
- self.impl_trait = true;
- }
- intravisit::walk_ty(self, ty);
- }
- }
-}
+ /// Set of lifetime def ids that are late-bound; a region can
+ /// be late-bound if (a) it does NOT appear in a where-clause and
+ /// (b) it DOES appear in the arguments.
+ pub late_bound: FxHashMap<LocalDefId, FxHashSet<ItemLocalId>>,
-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"),
- }
- }
+ /// For each type and trait definition, maps type parameters
+ /// to the trait object lifetime defaults computed from them.
+ pub object_lifetime_defaults:
+ FxHashMap<LocalDefId, FxHashMap<ItemLocalId, Vec<ObjectLifetimeDefault>>>,
}