rustc: simplify scope-tracking in resolve_lifetime.
This commit is contained in:
parent
0682a75f44
commit
bbc341424c
@ -30,6 +30,7 @@
|
|||||||
#![feature(const_fn)]
|
#![feature(const_fn)]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
#![feature(libc)]
|
#![feature(libc)]
|
||||||
|
#![feature(loop_break_value)]
|
||||||
#![feature(nonzero)]
|
#![feature(nonzero)]
|
||||||
#![feature(pub_restricted)]
|
#![feature(pub_restricted)]
|
||||||
#![feature(quote)]
|
#![feature(quote)]
|
||||||
|
@ -26,11 +26,10 @@ use std::mem::replace;
|
|||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::symbol::keywords;
|
use syntax::symbol::keywords;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
use util::nodemap::NodeMap;
|
use util::nodemap::{NodeMap, FxHashSet, FxHashMap};
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
|
||||||
use hir;
|
use hir;
|
||||||
use hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
|
use hir::intravisit::{self, Visitor, NestedVisitorMap};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
|
||||||
pub enum Region {
|
pub enum Region {
|
||||||
@ -40,6 +39,37 @@ pub enum Region {
|
|||||||
Free(region::CallSiteScopeData, /* lifetime decl */ ast::NodeId),
|
Free(region::CallSiteScopeData, /* lifetime decl */ ast::NodeId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Region {
|
||||||
|
fn early(index: &mut u32, def: &hir::LifetimeDef) -> (ast::Name, Region) {
|
||||||
|
let i = *index;
|
||||||
|
*index += 1;
|
||||||
|
(def.lifetime.name, Region::EarlyBound(i, def.lifetime.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn late(def: &hir::LifetimeDef) -> (ast::Name, Region) {
|
||||||
|
let depth = ty::DebruijnIndex::new(1);
|
||||||
|
(def.lifetime.name, Region::LateBound(depth, def.lifetime.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> Option<ast::NodeId> {
|
||||||
|
match *self {
|
||||||
|
Region::Static => None,
|
||||||
|
Region::EarlyBound(_, id) |
|
||||||
|
Region::LateBound(_, id) |
|
||||||
|
Region::Free(_, id) => Some(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shifted(self, amount: u32) -> Region {
|
||||||
|
match self {
|
||||||
|
Region::LateBound(depth, id) => {
|
||||||
|
Region::LateBound(depth.shifted(amount), id)
|
||||||
|
}
|
||||||
|
_ => self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Maps the id of each lifetime reference to the lifetime decl
|
// Maps the id of each lifetime reference to the lifetime decl
|
||||||
// that it corresponds to.
|
// that it corresponds to.
|
||||||
pub struct NamedRegionMap {
|
pub struct NamedRegionMap {
|
||||||
@ -79,28 +109,21 @@ struct LifetimeContext<'a, 'tcx: 'a> {
|
|||||||
labels_in_fn: Vec<(ast::Name, Span)>,
|
labels_in_fn: Vec<(ast::Name, Span)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(Debug)]
|
||||||
enum Scope<'a> {
|
enum Scope<'a> {
|
||||||
/// Extends `s` with early-bound `lifetimes`, having consecutive parameter
|
/// Declares lifetimes, and each can be early-bound or late-bound.
|
||||||
/// indices from `start`, i.e. `lifetimes[i]` has index `start + i`.
|
/// The `DebruijnIndex` of late-bound lifetimes starts at `1` and
|
||||||
/// Indices before `start` correspond to other generic parameters
|
/// it should be shifted by the number of `Binder`s in between the
|
||||||
/// of a parent item (trait/impl of a method), or `Self` in traits.
|
/// declaration `Binder` and the location it's referenced from.
|
||||||
Early {
|
Binder {
|
||||||
lifetimes: &'a [hir::LifetimeDef],
|
lifetimes: FxHashMap<ast::Name, Region>,
|
||||||
start: u32,
|
|
||||||
s: ScopeRef<'a>
|
s: ScopeRef<'a>
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Extends `s` with late-bound `lifetimes`.
|
/// Lifetimes introduced by a fn are scoped to the call-site for that fn,
|
||||||
Late {
|
/// if this is a fn body, otherwise the original definitions are used.
|
||||||
lifetimes: &'a [hir::LifetimeDef],
|
Body {
|
||||||
s: ScopeRef<'a>
|
id: hir::BodyId,
|
||||||
},
|
|
||||||
|
|
||||||
/// Lifetimes introduced by a fn are scoped to the call-site for that fn.
|
|
||||||
Fn {
|
|
||||||
fn_id: ast::NodeId,
|
|
||||||
body_id: ast::NodeId,
|
|
||||||
s: ScopeRef<'a>
|
s: ScopeRef<'a>
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -121,121 +144,93 @@ pub fn krate(sess: &Session,
|
|||||||
late_bound: NodeMap(),
|
late_bound: NodeMap(),
|
||||||
};
|
};
|
||||||
sess.track_errors(|| {
|
sess.track_errors(|| {
|
||||||
intravisit::walk_crate(&mut LifetimeContext {
|
let mut visitor = LifetimeContext {
|
||||||
sess: sess,
|
sess: sess,
|
||||||
hir_map: hir_map,
|
hir_map: hir_map,
|
||||||
map: &mut map,
|
map: &mut map,
|
||||||
scope: ROOT_SCOPE,
|
scope: ROOT_SCOPE,
|
||||||
trait_ref_hack: false,
|
trait_ref_hack: false,
|
||||||
labels_in_fn: vec![],
|
labels_in_fn: vec![],
|
||||||
}, krate);
|
};
|
||||||
|
for (_, item) in &krate.items {
|
||||||
|
visitor.visit_item(item);
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
Ok(map)
|
Ok(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
// Override the nested functions -- lifetimes follow lexical scope,
|
|
||||||
// so it's convenient to walk the tree in lexical order.
|
|
||||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||||
NestedVisitorMap::All(&self.hir_map)
|
NestedVisitorMap::All(self.hir_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_item(&mut self, item: &'tcx hir::Item) {
|
// We want to nest trait/impl items in their parent, but nothing else.
|
||||||
// Save labels for nested items.
|
fn visit_nested_item(&mut self, _: hir::ItemId) {}
|
||||||
let saved_labels_in_fn = replace(&mut self.labels_in_fn, vec![]);
|
|
||||||
|
|
||||||
// Items always introduce a new root scope
|
fn visit_nested_body(&mut self, body: hir::BodyId) {
|
||||||
self.with(Scope::Root, |_, this| {
|
// Each body has their own set of labels, save labels.
|
||||||
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::ItemUnion(_, 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 start = if let hir::ItemTrait(..) = item.node {
|
|
||||||
1 // Self comes before lifetimes
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
let early = Scope::Early {
|
|
||||||
lifetimes: lifetimes,
|
|
||||||
start: start,
|
|
||||||
s: ROOT_SCOPE
|
|
||||||
};
|
|
||||||
this.with(early, |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 = saved_labels_in_fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_foreign_item(&mut self, item: &'tcx 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![]);
|
let saved = replace(&mut self.labels_in_fn, vec![]);
|
||||||
|
let body = self.hir_map.body(body);
|
||||||
// Items always introduce a new root scope
|
extract_labels(self, body);
|
||||||
self.with(Scope::Root, |_, this| {
|
self.with(Scope::Body { id: body.id(), s: self.scope }, |_, this| {
|
||||||
match item.node {
|
this.visit_body(body);
|
||||||
hir::ForeignItemFn(ref decl, _, ref generics) => {
|
|
||||||
this.visit_early_late(item.id, 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);
|
replace(&mut self.labels_in_fn, saved);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_fn(&mut self, fk: FnKind<'tcx>, decl: &'tcx hir::FnDecl,
|
fn visit_item(&mut self, item: &'tcx hir::Item) {
|
||||||
b: hir::BodyId, s: Span, fn_id: ast::NodeId) {
|
match item.node {
|
||||||
match fk {
|
hir::ItemFn(ref decl, _, _, _, ref generics, _) => {
|
||||||
FnKind::ItemFn(_, generics, ..) => {
|
self.visit_early_late(item.id, None, decl, generics, |this| {
|
||||||
self.visit_early_late(fn_id,decl, generics, |this| {
|
intravisit::walk_item(this, item);
|
||||||
this.add_scope_and_walk_fn(fk, decl, b, s, fn_id)
|
});
|
||||||
|
}
|
||||||
|
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(self, item);
|
||||||
|
}
|
||||||
|
hir::ItemTy(_, ref generics) |
|
||||||
|
hir::ItemEnum(_, ref generics) |
|
||||||
|
hir::ItemStruct(_, ref generics) |
|
||||||
|
hir::ItemUnion(_, ref generics) |
|
||||||
|
hir::ItemTrait(_, ref generics, ..) |
|
||||||
|
hir::ItemImpl(_, _, ref generics, ..) => {
|
||||||
|
// These kinds of items have only early bound lifetime parameters.
|
||||||
|
let mut index = if let hir::ItemTrait(..) = item.node {
|
||||||
|
1 // Self comes before lifetimes
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let lifetimes = generics.lifetimes.iter().map(|def| {
|
||||||
|
Region::early(&mut index, def)
|
||||||
|
}).collect();
|
||||||
|
let scope = Scope::Binder {
|
||||||
|
lifetimes: lifetimes,
|
||||||
|
s: ROOT_SCOPE
|
||||||
|
};
|
||||||
|
self.with(scope, |old_scope, this| {
|
||||||
|
this.check_lifetime_defs(old_scope, &generics.lifetimes);
|
||||||
|
intravisit::walk_item(this, item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) {
|
||||||
|
match item.node {
|
||||||
|
hir::ForeignItemFn(ref decl, _, ref generics) => {
|
||||||
|
self.visit_early_late(item.id, None, decl, generics, |this| {
|
||||||
|
intravisit::walk_foreign_item(this, item);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FnKind::Method(_, sig, ..) => {
|
hir::ForeignItemStatic(..) => {
|
||||||
self.visit_early_late(
|
intravisit::walk_foreign_item(self, item);
|
||||||
fn_id,
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,11 +238,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
|
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
|
||||||
match ty.node {
|
match ty.node {
|
||||||
hir::TyBareFn(ref c) => {
|
hir::TyBareFn(ref c) => {
|
||||||
let late = Scope::Late {
|
let scope = Scope::Binder {
|
||||||
lifetimes: &c.lifetimes,
|
lifetimes: c.lifetimes.iter().map(Region::late).collect(),
|
||||||
s: self.scope
|
s: self.scope
|
||||||
};
|
};
|
||||||
self.with(late, |old_scope, this| {
|
self.with(scope, |old_scope, this| {
|
||||||
// a bare fn has no bounds, so everything
|
// a bare fn has no bounds, so everything
|
||||||
// contained within is scoped within its binder.
|
// contained within is scoped within its binder.
|
||||||
this.check_lifetime_defs(old_scope, &c.lifetimes);
|
this.check_lifetime_defs(old_scope, &c.lifetimes);
|
||||||
@ -259,7 +254,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||||||
// a trait ref, which introduces a binding scope.
|
// a trait ref, which introduces a binding scope.
|
||||||
match path.def {
|
match path.def {
|
||||||
Def::Trait(..) => {
|
Def::Trait(..) => {
|
||||||
self.with(Scope::Late { lifetimes: &[], s: self.scope }, |_, this| {
|
let scope = Scope::Binder {
|
||||||
|
lifetimes: FxHashMap(),
|
||||||
|
s: self.scope
|
||||||
|
};
|
||||||
|
self.with(scope, |_, this| {
|
||||||
this.visit_path(path, ty.id);
|
this.visit_path(path, ty.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -275,21 +274,27 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
|
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
|
||||||
// We reset the labels on every trait item, so that different
|
if let hir::TraitItemKind::Method(ref sig, _) = trait_item.node {
|
||||||
// methods in an impl can reuse label names.
|
|
||||||
let saved = replace(&mut self.labels_in_fn, vec![]);
|
|
||||||
|
|
||||||
if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) =
|
|
||||||
trait_item.node {
|
|
||||||
self.visit_early_late(
|
self.visit_early_late(
|
||||||
trait_item.id,
|
trait_item.id,
|
||||||
|
Some(self.hir_map.get_parent(trait_item.id)),
|
||||||
&sig.decl, &sig.generics,
|
&sig.decl, &sig.generics,
|
||||||
|this| intravisit::walk_trait_item(this, trait_item))
|
|this| intravisit::walk_trait_item(this, trait_item))
|
||||||
} else {
|
} else {
|
||||||
intravisit::walk_trait_item(self, trait_item);
|
intravisit::walk_trait_item(self, trait_item);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
replace(&mut self.labels_in_fn, saved);
|
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
|
||||||
|
if let hir::ImplItemKind::Method(ref sig, _) = impl_item.node {
|
||||||
|
self.visit_early_late(
|
||||||
|
impl_item.id,
|
||||||
|
Some(self.hir_map.get_parent(impl_item.id)),
|
||||||
|
&sig.decl, &sig.generics,
|
||||||
|
|this| intravisit::walk_impl_item(this, impl_item))
|
||||||
|
} else {
|
||||||
|
intravisit::walk_impl_item(self, impl_item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
|
fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
|
||||||
@ -318,11 +323,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||||||
.. }) => {
|
.. }) => {
|
||||||
if !bound_lifetimes.is_empty() {
|
if !bound_lifetimes.is_empty() {
|
||||||
self.trait_ref_hack = true;
|
self.trait_ref_hack = true;
|
||||||
let late = Scope::Late {
|
let scope = Scope::Binder {
|
||||||
lifetimes: bound_lifetimes,
|
lifetimes: bound_lifetimes.iter().map(Region::late).collect(),
|
||||||
s: self.scope
|
s: self.scope
|
||||||
};
|
};
|
||||||
let result = self.with(late, |old_scope, this| {
|
let result = self.with(scope, |old_scope, this| {
|
||||||
this.check_lifetime_defs(old_scope, bound_lifetimes);
|
this.check_lifetime_defs(old_scope, bound_lifetimes);
|
||||||
this.visit_ty(&bounded_ty);
|
this.visit_ty(&bounded_ty);
|
||||||
walk_list!(this, visit_ty_param_bound, bounds);
|
walk_list!(this, visit_ty_param_bound, bounds);
|
||||||
@ -363,11 +368,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||||||
span_err!(self.sess, trait_ref.span, E0316,
|
span_err!(self.sess, trait_ref.span, E0316,
|
||||||
"nested quantification of lifetimes");
|
"nested quantification of lifetimes");
|
||||||
}
|
}
|
||||||
let late = Scope::Late {
|
let scope = Scope::Binder {
|
||||||
lifetimes: &trait_ref.bound_lifetimes,
|
lifetimes: trait_ref.bound_lifetimes.iter().map(Region::late).collect(),
|
||||||
s: self.scope
|
s: self.scope
|
||||||
};
|
};
|
||||||
self.with(late, |old_scope, this| {
|
self.with(scope, |old_scope, this| {
|
||||||
this.check_lifetime_defs(old_scope, &trait_ref.bound_lifetimes);
|
this.check_lifetime_defs(old_scope, &trait_ref.bound_lifetimes);
|
||||||
for lifetime in &trait_ref.bound_lifetimes {
|
for lifetime in &trait_ref.bound_lifetimes {
|
||||||
this.visit_lifetime_def(lifetime);
|
this.visit_lifetime_def(lifetime);
|
||||||
@ -391,8 +396,8 @@ fn original_label(span: Span) -> Original {
|
|||||||
fn shadower_label(span: Span) -> Shadower {
|
fn shadower_label(span: Span) -> Shadower {
|
||||||
Shadower { kind: ShadowKind::Label, span: span }
|
Shadower { kind: ShadowKind::Label, span: span }
|
||||||
}
|
}
|
||||||
fn original_lifetime(l: &hir::Lifetime) -> Original {
|
fn original_lifetime(span: Span) -> Original {
|
||||||
Original { kind: ShadowKind::Lifetime, span: l.span }
|
Original { kind: ShadowKind::Lifetime, span: span }
|
||||||
}
|
}
|
||||||
fn shadower_lifetime(l: &hir::Lifetime) -> Shadower {
|
fn shadower_lifetime(l: &hir::Lifetime) -> Shadower {
|
||||||
Shadower { kind: ShadowKind::Lifetime, span: l.span }
|
Shadower { kind: ShadowKind::Lifetime, span: l.span }
|
||||||
@ -430,33 +435,28 @@ fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, sha
|
|||||||
|
|
||||||
// Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
|
// Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
|
||||||
// if one of the label shadows a lifetime or another label.
|
// if one of the label shadows a lifetime or another label.
|
||||||
fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) {
|
fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
|
||||||
struct GatherLabels<'a> {
|
struct GatherLabels<'a, 'tcx: 'a> {
|
||||||
sess: &'a Session,
|
sess: &'a Session,
|
||||||
|
hir_map: &'a Map<'tcx>,
|
||||||
scope: ScopeRef<'a>,
|
scope: ScopeRef<'a>,
|
||||||
labels_in_fn: &'a mut Vec<(ast::Name, Span)>,
|
labels_in_fn: &'a mut Vec<(ast::Name, Span)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut gather = GatherLabels {
|
let mut gather = GatherLabels {
|
||||||
sess: ctxt.sess,
|
sess: ctxt.sess,
|
||||||
|
hir_map: ctxt.hir_map,
|
||||||
scope: ctxt.scope,
|
scope: ctxt.scope,
|
||||||
labels_in_fn: &mut ctxt.labels_in_fn,
|
labels_in_fn: &mut ctxt.labels_in_fn,
|
||||||
};
|
};
|
||||||
gather.visit_body(ctxt.hir_map.body(b));
|
gather.visit_body(body);
|
||||||
return;
|
|
||||||
|
|
||||||
impl<'v, 'a> Visitor<'v> for GatherLabels<'a> {
|
impl<'v, 'a, 'tcx> Visitor<'v> for GatherLabels<'a, 'tcx> {
|
||||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
|
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
|
||||||
NestedVisitorMap::None
|
NestedVisitorMap::None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, ex: &'v hir::Expr) {
|
fn visit_expr(&mut self, ex: &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) {
|
if let Some((label, label_span)) = expression_label(ex) {
|
||||||
for &(prior, prior_span) in &self.labels_in_fn[..] {
|
for &(prior, prior_span) in &self.labels_in_fn[..] {
|
||||||
// FIXME (#24278): non-hygienic comparison
|
// FIXME (#24278): non-hygienic comparison
|
||||||
@ -469,6 +469,7 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_if_label_shadows_lifetime(self.sess,
|
check_if_label_shadows_lifetime(self.sess,
|
||||||
|
self.hir_map,
|
||||||
self.scope,
|
self.scope,
|
||||||
label,
|
label,
|
||||||
label_span);
|
label_span);
|
||||||
@ -492,26 +493,24 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_if_label_shadows_lifetime<'a>(sess: &'a Session,
|
fn check_if_label_shadows_lifetime<'a>(sess: &'a Session,
|
||||||
|
hir_map: &Map,
|
||||||
mut scope: ScopeRef<'a>,
|
mut scope: ScopeRef<'a>,
|
||||||
label: ast::Name,
|
label: ast::Name,
|
||||||
label_span: Span) {
|
label_span: Span) {
|
||||||
loop {
|
loop {
|
||||||
match *scope {
|
match *scope {
|
||||||
Scope::Fn { s, .. } => { scope = s; }
|
Scope::Body { s, .. } => { scope = s; }
|
||||||
Scope::Root => { return; }
|
Scope::Root => { return; }
|
||||||
|
|
||||||
Scope::Early { lifetimes, s, .. } |
|
Scope::Binder { ref lifetimes, s } => {
|
||||||
Scope::Late { lifetimes, s } => {
|
// FIXME (#24278): non-hygienic comparison
|
||||||
for lifetime_def in lifetimes {
|
if let Some(def) = lifetimes.get(&label) {
|
||||||
// FIXME (#24278): non-hygienic comparison
|
signal_shadowing_problem(
|
||||||
if label == lifetime_def.lifetime.name {
|
sess,
|
||||||
signal_shadowing_problem(
|
label,
|
||||||
sess,
|
original_lifetime(hir_map.span(def.id().unwrap())),
|
||||||
label,
|
shadower_label(label_span));
|
||||||
original_lifetime(&lifetime_def.lifetime),
|
return;
|
||||||
shadower_label(label_span));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
scope = s;
|
scope = s;
|
||||||
}
|
}
|
||||||
@ -521,34 +520,6 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
fn add_scope_and_walk_fn(&mut self,
|
|
||||||
fk: FnKind<'tcx>,
|
|
||||||
fd: &'tcx hir::FnDecl,
|
|
||||||
fb: hir::BodyId,
|
|
||||||
_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(Scope::Fn { fn_id: fn_id, body_id: fb.node_id, s: self.scope },
|
|
||||||
|_old_scope, this| this.visit_nested_body(fb))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(#37666) this works around a limitation in the region inferencer
|
// FIXME(#37666) this works around a limitation in the region inferencer
|
||||||
fn hack<F>(&mut self, f: F) where
|
fn hack<F>(&mut self, f: F) where
|
||||||
F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>),
|
F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>),
|
||||||
@ -560,17 +531,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||||||
F: for<'b> FnOnce(ScopeRef, &mut LifetimeContext<'b, 'tcx>),
|
F: for<'b> FnOnce(ScopeRef, &mut LifetimeContext<'b, 'tcx>),
|
||||||
{
|
{
|
||||||
let LifetimeContext {sess, hir_map, ref mut map, ..} = *self;
|
let LifetimeContext {sess, hir_map, ref mut map, ..} = *self;
|
||||||
|
let labels_in_fn = replace(&mut self.labels_in_fn, vec![]);
|
||||||
let mut this = LifetimeContext {
|
let mut this = LifetimeContext {
|
||||||
sess: sess,
|
sess: sess,
|
||||||
hir_map: hir_map,
|
hir_map: hir_map,
|
||||||
map: *map,
|
map: *map,
|
||||||
scope: &wrap_scope,
|
scope: &wrap_scope,
|
||||||
trait_ref_hack: self.trait_ref_hack,
|
trait_ref_hack: self.trait_ref_hack,
|
||||||
labels_in_fn: self.labels_in_fn.clone(),
|
labels_in_fn: labels_in_fn,
|
||||||
};
|
};
|
||||||
debug!("entering scope {:?}", this.scope);
|
debug!("entering scope {:?}", this.scope);
|
||||||
f(self.scope, &mut this);
|
f(self.scope, &mut this);
|
||||||
debug!("exiting scope {:?}", this.scope);
|
debug!("exiting scope {:?}", this.scope);
|
||||||
|
self.labels_in_fn = this.labels_in_fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
|
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
|
||||||
@ -593,6 +566,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||||||
/// ordering is not important there.
|
/// ordering is not important there.
|
||||||
fn visit_early_late<F>(&mut self,
|
fn visit_early_late<F>(&mut self,
|
||||||
fn_id: ast::NodeId,
|
fn_id: ast::NodeId,
|
||||||
|
parent_id: Option<ast::NodeId>,
|
||||||
decl: &'tcx hir::FnDecl,
|
decl: &'tcx hir::FnDecl,
|
||||||
generics: &'tcx hir::Generics,
|
generics: &'tcx hir::Generics,
|
||||||
walk: F) where
|
walk: F) where
|
||||||
@ -604,162 +578,98 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||||||
decl,
|
decl,
|
||||||
generics);
|
generics);
|
||||||
|
|
||||||
let (late, early): (Vec<_>, _) =
|
|
||||||
generics.lifetimes
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.partition(|l| self.map.late_bound.contains_key(&l.lifetime.id));
|
|
||||||
|
|
||||||
// Find the start of nested early scopes, e.g. in methods.
|
// Find the start of nested early scopes, e.g. in methods.
|
||||||
let mut start = 0;
|
let mut index = 0;
|
||||||
if let Scope::Early {..} = *self.scope {
|
if let Some(parent_id) = parent_id {
|
||||||
let parent = self.hir_map.expect_item(self.hir_map.get_parent(fn_id));
|
let parent = self.hir_map.expect_item(parent_id);
|
||||||
if let hir::ItemTrait(..) = parent.node {
|
if let hir::ItemTrait(..) = parent.node {
|
||||||
start += 1; // Self comes first.
|
index += 1; // Self comes first.
|
||||||
}
|
}
|
||||||
match parent.node {
|
match parent.node {
|
||||||
hir::ItemTrait(_, ref generics, ..) |
|
hir::ItemTrait(_, ref generics, ..) |
|
||||||
hir::ItemImpl(_, _, ref generics, ..) => {
|
hir::ItemImpl(_, _, ref generics, ..) => {
|
||||||
start += generics.lifetimes.len() + generics.ty_params.len();
|
index += (generics.lifetimes.len() + generics.ty_params.len()) as u32;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let early = Scope::Early {
|
let lifetimes = generics.lifetimes.iter().map(|def| {
|
||||||
lifetimes: &early,
|
if self.map.late_bound.contains_key(&def.lifetime.id) {
|
||||||
start: start as u32,
|
Region::late(def)
|
||||||
|
} else {
|
||||||
|
Region::early(&mut index, def)
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let scope = Scope::Binder {
|
||||||
|
lifetimes: lifetimes,
|
||||||
s: self.scope
|
s: self.scope
|
||||||
};
|
};
|
||||||
self.with(early, move |old_scope, this| {
|
self.with(scope, move |old_scope, this| {
|
||||||
let late = Scope::Late {
|
this.check_lifetime_defs(old_scope, &generics.lifetimes);
|
||||||
lifetimes: &late,
|
this.hack(walk); // FIXME(#37666) workaround in place of `walk(this)`
|
||||||
s: this.scope
|
|
||||||
};
|
|
||||||
this.with(late, move |_, this| {
|
|
||||||
this.check_lifetime_defs(old_scope, &generics.lifetimes);
|
|
||||||
this.hack(walk); // FIXME(#37666) workaround in place of `walk(this)`
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) {
|
fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) {
|
||||||
// Walk up the scope chain, tracking the number of fn scopes
|
// Walk up the scope chain, tracking the number of fn scopes
|
||||||
// that we pass through, until we find a lifetime with the
|
// that we pass through, until we find a lifetime with the
|
||||||
// given name or we run out of scopes. If we encounter a code
|
// given name or we run out of scopes.
|
||||||
// block, then the lifetime is not bound but free, so switch
|
|
||||||
// over to `resolve_free_lifetime_ref()` to complete the
|
|
||||||
// search.
|
// search.
|
||||||
let mut late_depth = 0;
|
let mut late_depth = 0;
|
||||||
let mut scope = self.scope;
|
let mut scope = self.scope;
|
||||||
loop {
|
let mut outermost_body = None;
|
||||||
|
let result = loop {
|
||||||
match *scope {
|
match *scope {
|
||||||
Scope::Fn { fn_id, body_id, s } => {
|
Scope::Body { id, s } => {
|
||||||
return self.resolve_free_lifetime_ref(
|
outermost_body = Some(id);
|
||||||
region::CallSiteScopeData { fn_id: fn_id, body_id: body_id },
|
|
||||||
lifetime_ref,
|
|
||||||
s);
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope::Root => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope::Early { lifetimes, start, s } => {
|
|
||||||
match search_lifetimes(lifetimes, lifetime_ref) {
|
|
||||||
Some((index, lifetime_def)) => {
|
|
||||||
let decl_id = lifetime_def.id;
|
|
||||||
let def = Region::EarlyBound(start + index, decl_id);
|
|
||||||
self.insert_lifetime(lifetime_ref, def);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
scope = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope::Late { 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 = Region::LateBound(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: ScopeRef) {
|
|
||||||
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 {
|
|
||||||
Scope::Fn { fn_id, body_id, s } => {
|
|
||||||
scope_data = region::CallSiteScopeData {
|
|
||||||
fn_id: fn_id, body_id: body_id
|
|
||||||
};
|
|
||||||
scope = s;
|
scope = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope::Root => {
|
Scope::Root => {
|
||||||
break;
|
break None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope::Early { lifetimes, s, .. } |
|
Scope::Binder { ref lifetimes, s } => {
|
||||||
Scope::Late { lifetimes, s } => {
|
if let Some(&def) = lifetimes.get(&lifetime_ref.name) {
|
||||||
search_result = search_lifetimes(lifetimes, lifetime_ref);
|
break Some(def.shifted(late_depth));
|
||||||
if search_result.is_some() {
|
} else {
|
||||||
break;
|
late_depth += 1;
|
||||||
|
scope = s;
|
||||||
}
|
}
|
||||||
scope = s;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
match search_result {
|
if let Some(mut def) = result {
|
||||||
Some((_depth, lifetime)) => {
|
if let Some(body_id) = outermost_body {
|
||||||
let def = Region::Free(scope_data, lifetime.id);
|
let fn_id = self.hir_map.body_owner(body_id);
|
||||||
self.insert_lifetime(lifetime_ref, def);
|
let scope_data = region::CallSiteScopeData {
|
||||||
}
|
fn_id: fn_id, body_id: body_id.node_id
|
||||||
|
};
|
||||||
None => {
|
match self.hir_map.get(fn_id) {
|
||||||
self.unresolved_lifetime_ref(lifetime_ref);
|
hir::map::NodeItem(&hir::Item {
|
||||||
|
node: hir::ItemFn(..), ..
|
||||||
|
}) |
|
||||||
|
hir::map::NodeTraitItem(&hir::TraitItem {
|
||||||
|
node: hir::TraitItemKind::Method(..), ..
|
||||||
|
}) |
|
||||||
|
hir::map::NodeImplItem(&hir::ImplItem {
|
||||||
|
node: hir::ImplItemKind::Method(..), ..
|
||||||
|
}) => {
|
||||||
|
def = Region::Free(scope_data, def.id().unwrap());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.insert_lifetime(lifetime_ref, def);
|
||||||
|
} else {
|
||||||
|
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 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: ScopeRef, lifetimes: &[hir::LifetimeDef]) {
|
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) {
|
||||||
@ -819,7 +729,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match *old_scope {
|
match *old_scope {
|
||||||
Scope::Fn { s, .. } => {
|
Scope::Body { s, .. } => {
|
||||||
old_scope = s;
|
old_scope = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,13 +737,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope::Early { lifetimes, s, .. } |
|
Scope::Binder { ref lifetimes, s } => {
|
||||||
Scope::Late { lifetimes, s } => {
|
if let Some(&def) = lifetimes.get(&lifetime.name) {
|
||||||
if let Some((_, lifetime_def)) = search_lifetimes(lifetimes, lifetime) {
|
|
||||||
signal_shadowing_problem(
|
signal_shadowing_problem(
|
||||||
self.sess,
|
self.sess,
|
||||||
lifetime.name,
|
lifetime.name,
|
||||||
original_lifetime(&lifetime_def),
|
original_lifetime(self.hir_map.span(def.id().unwrap())),
|
||||||
shadower_lifetime(&lifetime));
|
shadower_lifetime(&lifetime));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -861,17 +770,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/// Detects late-bound lifetimes and inserts them into
|
||||||
|
Loading…
Reference in New Issue
Block a user