Introduce HirId, a replacement for NodeId after lowering to HIR.

HirId has a more stable representation than NodeId, meaning that
modifications to one item don't influence (part of) the IDs within
other items. The other part is a DefIndex for which there already
is a way of stable hashing and persistence.

This commit introduces the HirId type and generates a HirId for
every NodeId during HIR lowering, but the resulting values are
not yet used anywhere, except in consistency checks.
This commit is contained in:
Michael Woerister 2017-03-14 15:50:40 +01:00
parent 559127b451
commit bc259ee844
7 changed files with 1309 additions and 810 deletions

View File

@ -43,12 +43,14 @@
use hir;
use hir::map::{Definitions, DefKey};
use hir::map::definitions::DefPathData;
use hir::def_id::{DefIndex, DefId};
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX};
use hir::def::{Def, PathResolution};
use rustc_data_structures::indexed_vec::IndexVec;
use session::Session;
use util::nodemap::{DefIdMap, NodeMap};
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::iter;
use std::mem;
@ -63,6 +65,8 @@ use syntax::util::small_vector::SmallVector;
use syntax::visit::{self, Visitor};
use syntax_pos::Span;
const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF;
pub struct LoweringContext<'a> {
crate_root: Option<&'static str>,
// Use to assign ids to hir nodes that do not directly correspond to an ast node
@ -89,6 +93,10 @@ pub struct LoweringContext<'a> {
is_in_loop_condition: bool,
type_def_lifetime_params: DefIdMap<usize>,
current_hir_id_owner: Vec<(DefIndex, u32)>,
item_local_id_counters: NodeMap<u32>,
node_id_to_hir_id: IndexVec<NodeId, hir::HirId>,
}
pub trait Resolver {
@ -128,6 +136,9 @@ pub fn lower_crate(sess: &Session,
loop_scopes: Vec::new(),
is_in_loop_condition: false,
type_def_lifetime_params: DefIdMap(),
current_hir_id_owner: vec![(CRATE_DEF_INDEX, 0)],
item_local_id_counters: NodeMap(),
node_id_to_hir_id: IndexVec::new(),
}.lower_crate(krate)
}
@ -152,6 +163,8 @@ impl<'a> LoweringContext<'a> {
impl<'lcx, 'interner> Visitor<'lcx> for MiscCollector<'lcx, 'interner> {
fn visit_item(&mut self, item: &'lcx Item) {
self.lctx.allocate_hir_id_counter(item.id, item);
match item.node {
ItemKind::Struct(_, ref generics) |
ItemKind::Union(_, ref generics) |
@ -166,6 +179,16 @@ impl<'a> LoweringContext<'a> {
}
visit::walk_item(self, item);
}
fn visit_trait_item(&mut self, item: &'lcx TraitItem) {
self.lctx.allocate_hir_id_counter(item.id, item);
visit::walk_trait_item(self, item);
}
fn visit_impl_item(&mut self, item: &'lcx ImplItem) {
self.lctx.allocate_hir_id_counter(item.id, item);
visit::walk_impl_item(self, item);
}
}
struct ItemLowerer<'lcx, 'interner: 'lcx> {
@ -174,27 +197,43 @@ impl<'a> LoweringContext<'a> {
impl<'lcx, 'interner> Visitor<'lcx> for ItemLowerer<'lcx, 'interner> {
fn visit_item(&mut self, item: &'lcx Item) {
if let Some(hir_item) = self.lctx.lower_item(item) {
self.lctx.items.insert(item.id, hir_item);
let mut item_lowered = true;
self.lctx.with_hir_id_owner(item.id, |lctx| {
if let Some(hir_item) = lctx.lower_item(item) {
lctx.items.insert(item.id, hir_item);
} else {
item_lowered = false;
}
});
if item_lowered {
visit::walk_item(self, item);
}
}
fn visit_trait_item(&mut self, item: &'lcx TraitItem) {
self.lctx.with_hir_id_owner(item.id, |lctx| {
let id = hir::TraitItemId { node_id: item.id };
let hir_item = self.lctx.lower_trait_item(item);
self.lctx.trait_items.insert(id, hir_item);
let hir_item = lctx.lower_trait_item(item);
lctx.trait_items.insert(id, hir_item);
});
visit::walk_trait_item(self, item);
}
fn visit_impl_item(&mut self, item: &'lcx ImplItem) {
self.lctx.with_hir_id_owner(item.id, |lctx| {
let id = hir::ImplItemId { node_id: item.id };
let hir_item = self.lctx.lower_impl_item(item);
self.lctx.impl_items.insert(id, hir_item);
let hir_item = lctx.lower_impl_item(item);
lctx.impl_items.insert(id, hir_item);
});
visit::walk_impl_item(self, item);
}
}
self.lower_node_id(CRATE_NODE_ID);
debug_assert!(self.node_id_to_hir_id[CRATE_NODE_ID] == hir::CRATE_HIR_ID);
visit::walk_crate(&mut MiscCollector { lctx: &mut self }, c);
visit::walk_crate(&mut ItemLowerer { lctx: &mut self }, c);
@ -202,6 +241,10 @@ impl<'a> LoweringContext<'a> {
let attrs = self.lower_attrs(&c.attrs);
let body_ids = body_ids(&self.bodies);
self.resolver
.definitions()
.init_node_id_to_hir_id_mapping(self.node_id_to_hir_id);
hir::Crate {
module: module,
attrs: attrs,
@ -217,6 +260,103 @@ impl<'a> LoweringContext<'a> {
}
}
fn allocate_hir_id_counter<T: Debug>(&mut self,
owner: NodeId,
debug: &T) {
if self.item_local_id_counters.insert(owner, 0).is_some() {
bug!("Tried to allocate item_local_id_counter for {:?} twice", debug);
}
// Always allocate the first HirId for the owner itself
self.lower_node_id_with_owner(owner, owner);
}
fn lower_node_id_generic<F>(&mut self,
ast_node_id: NodeId,
alloc_hir_id: F)
-> NodeId
where F: FnOnce(&mut Self) -> hir::HirId
{
if ast_node_id == DUMMY_NODE_ID {
return ast_node_id;
}
let min_size = ast_node_id.as_usize() + 1;
if min_size > self.node_id_to_hir_id.len() {
self.node_id_to_hir_id.resize(min_size, hir::DUMMY_HIR_ID);
}
if self.node_id_to_hir_id[ast_node_id] == hir::DUMMY_HIR_ID {
// Generate a new HirId
self.node_id_to_hir_id[ast_node_id] = alloc_hir_id(self);
}
ast_node_id
}
fn with_hir_id_owner<F>(&mut self, owner: NodeId, f: F)
where F: FnOnce(&mut Self)
{
let counter = self.item_local_id_counters
.insert(owner, HIR_ID_COUNTER_LOCKED)
.unwrap();
let def_index = self.resolver.definitions().opt_def_index(owner).unwrap();
self.current_hir_id_owner.push((def_index, counter));
f(self);
let (new_def_index, new_counter) = self.current_hir_id_owner.pop().unwrap();
debug_assert!(def_index == new_def_index);
debug_assert!(new_counter >= counter);
let prev = self.item_local_id_counters.insert(owner, new_counter).unwrap();
debug_assert!(prev == HIR_ID_COUNTER_LOCKED);
}
/// This method allocates a new HirId for the given NodeId and stores it in
/// the LoweringContext's NodeId => HirId map.
/// Take care not to call this method if the resulting HirId is then not
/// actually used in the HIR, as that would trigger an assertion in the
/// HirIdValidator later on, which makes sure that all NodeIds got mapped
/// properly. Calling the method twice with the same NodeId is fine though.
fn lower_node_id(&mut self, ast_node_id: NodeId) -> NodeId {
self.lower_node_id_generic(ast_node_id, |this| {
let &mut (def_index, ref mut local_id_counter) = this.current_hir_id_owner
.last_mut()
.unwrap();
let local_id = *local_id_counter;
*local_id_counter += 1;
hir::HirId {
owner: def_index,
local_id: hir::ItemLocalId(local_id),
}
})
}
fn lower_node_id_with_owner(&mut self,
ast_node_id: NodeId,
owner: NodeId)
-> NodeId {
self.lower_node_id_generic(ast_node_id, |this| {
let local_id_counter = this.item_local_id_counters
.get_mut(&owner)
.unwrap();
let local_id = *local_id_counter;
// We want to be sure not to modify the counter in the map while it
// is also on the stack. Otherwise we'll get lost updates when writing
// back from the stack to the map.
debug_assert!(local_id != HIR_ID_COUNTER_LOCKED);
*local_id_counter += 1;
let def_index = this.resolver.definitions().opt_def_index(owner).unwrap();
hir::HirId {
owner: def_index,
local_id: hir::ItemLocalId(local_id),
}
})
}
fn record_body(&mut self, value: hir::Expr, decl: Option<&FnDecl>)
-> hir::BodyId {
let body = hir::Body {
@ -230,8 +370,8 @@ impl<'a> LoweringContext<'a> {
id
}
fn next_id(&self) -> NodeId {
self.sess.next_node_id()
fn next_id(&mut self) -> NodeId {
self.lower_node_id(self.sess.next_node_id())
}
fn expect_full_def(&mut self, id: NodeId) -> Def {
@ -362,7 +502,7 @@ impl<'a> LoweringContext<'a> {
match destination {
Some((id, label_ident)) => {
let target = if let Def::Label(loop_id) = self.expect_full_def(id) {
hir::LoopIdResult::Ok(loop_id)
hir::LoopIdResult::Ok(self.lower_node_id(loop_id))
} else {
hir::LoopIdResult::Err(hir::LoopIdError::UnresolvedLabel)
};
@ -371,11 +511,18 @@ impl<'a> LoweringContext<'a> {
target_id: hir::ScopeTarget::Loop(target),
}
},
None => hir::Destination {
None => {
let loop_id = self.loop_scopes
.last()
.map(|innermost_loop_id| *innermost_loop_id);
hir::Destination {
ident: None,
target_id: hir::ScopeTarget::Loop(
self.loop_scopes.last().map(|innermost_loop_id| Ok(*innermost_loop_id))
.unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)).into())
loop_id.map(|id| Ok(self.lower_node_id(id)))
.unwrap_or(Err(hir::LoopIdError::OutsideLoopScope))
.into())
}
}
}
}
@ -395,7 +542,7 @@ impl<'a> LoweringContext<'a> {
fn lower_ty_binding(&mut self, b: &TypeBinding) -> hir::TypeBinding {
hir::TypeBinding {
id: b.id,
id: self.lower_node_id(b.id),
name: b.ident.name,
ty: self.lower_ty(&b.ty),
span: b.span,
@ -403,9 +550,7 @@ impl<'a> LoweringContext<'a> {
}
fn lower_ty(&mut self, t: &Ty) -> P<hir::Ty> {
P(hir::Ty {
id: t.id,
node: match t.node {
let kind = match t.node {
TyKind::Infer => hir::TyInfer,
TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty)),
TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)),
@ -433,8 +578,9 @@ impl<'a> LoweringContext<'a> {
return self.lower_ty(ty);
}
TyKind::Path(ref qself, ref path) => {
let id = self.lower_node_id(t.id);
let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit);
return self.ty_path(t.id, t.span, qpath);
return self.ty_path(id, t.span, qpath);
}
TyKind::ImplicitSelf => {
hir::TyPath(hir::QPath::Resolved(None, P(hir::Path {
@ -464,7 +610,9 @@ impl<'a> LoweringContext<'a> {
}
TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
RegionTyParamBound(ref lifetime) => {
if lifetime_bound.is_none() {
lifetime_bound = Some(self.lower_lifetime(lifetime));
}
None
}
}
@ -478,7 +626,11 @@ impl<'a> LoweringContext<'a> {
hir::TyImplTrait(self.lower_bounds(bounds))
}
TyKind::Mac(_) => panic!("TyMac should have been expanded by now."),
},
};
P(hir::Ty {
id: self.lower_node_id(t.id),
node: kind,
span: t.span,
})
}
@ -712,7 +864,7 @@ impl<'a> LoweringContext<'a> {
fn lower_local(&mut self, l: &Local) -> P<hir::Local> {
P(hir::Local {
id: l.id,
id: self.lower_node_id(l.id),
ty: l.ty.as_ref().map(|t| self.lower_ty(t)),
pat: self.lower_pat(&l.pat),
init: l.init.as_ref().map(|e| P(self.lower_expr(e))),
@ -730,7 +882,7 @@ impl<'a> LoweringContext<'a> {
fn lower_arg(&mut self, arg: &Arg) -> hir::Arg {
hir::Arg {
id: arg.id,
id: self.lower_node_id(arg.id),
pat: self.lower_pat(&arg.pat),
}
}
@ -786,7 +938,7 @@ impl<'a> LoweringContext<'a> {
}
hir::TyParam {
id: tp.id,
id: self.lower_node_id(tp.id),
name: name,
bounds: bounds,
default: tp.default.as_ref().map(|x| self.lower_ty(x)),
@ -804,7 +956,7 @@ impl<'a> LoweringContext<'a> {
fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
hir::Lifetime {
id: l.id,
id: self.lower_node_id(l.id),
name: l.name,
span: l.span,
}
@ -876,7 +1028,7 @@ impl<'a> LoweringContext<'a> {
fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause {
hir::WhereClause {
id: wc.id,
id: self.lower_node_id(wc.id),
predicates: wc.predicates
.iter()
.map(|predicate| self.lower_where_predicate(predicate))
@ -915,7 +1067,7 @@ impl<'a> LoweringContext<'a> {
ref rhs_ty,
span}) => {
hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
id: id,
id: self.lower_node_id(id),
lhs_ty: self.lower_ty(lhs_ty),
rhs_ty: self.lower_ty(rhs_ty),
span: span,
@ -931,16 +1083,16 @@ impl<'a> LoweringContext<'a> {
.enumerate()
.map(|f| self.lower_struct_field(f))
.collect(),
id)
self.lower_node_id(id))
}
VariantData::Tuple(ref fields, id) => {
hir::VariantData::Tuple(fields.iter()
.enumerate()
.map(|f| self.lower_struct_field(f))
.collect(),
id)
self.lower_node_id(id))
}
VariantData::Unit(id) => hir::VariantData::Unit(id),
VariantData::Unit(id) => hir::VariantData::Unit(self.lower_node_id(id)),
}
}
@ -951,7 +1103,7 @@ impl<'a> LoweringContext<'a> {
};
hir::TraitRef {
path: path,
ref_id: p.ref_id,
ref_id: self.lower_node_id(p.ref_id),
}
}
@ -966,9 +1118,9 @@ impl<'a> LoweringContext<'a> {
fn lower_struct_field(&mut self, (index, f): (usize, &StructField)) -> hir::StructField {
hir::StructField {
span: f.span,
id: f.id,
id: self.lower_node_id(f.id),
name: f.ident.map(|ident| ident.name).unwrap_or(Symbol::intern(&index.to_string())),
vis: self.lower_visibility(&f.vis),
vis: self.lower_visibility(&f.vis, None),
ty: self.lower_ty(&f.ty),
attrs: self.lower_attrs(&f.attrs),
}
@ -997,17 +1149,22 @@ impl<'a> LoweringContext<'a> {
fn lower_block(&mut self, b: &Block, break_to: Option<NodeId>) -> P<hir::Block> {
let mut expr = None;
let mut stmts = b.stmts.iter().flat_map(|s| self.lower_stmt(s)).collect::<Vec<_>>();
if let Some(last) = stmts.pop() {
if let hir::StmtExpr(e, _) = last.node {
expr = Some(e);
let mut stmts = vec![];
for (index, stmt) in b.stmts.iter().enumerate() {
if index == b.stmts.len() - 1 {
if let StmtKind::Expr(ref e) = stmt.node {
expr = Some(P(self.lower_expr(e)));
} else {
stmts.push(last);
stmts.extend(self.lower_stmt(stmt));
}
} else {
stmts.extend(self.lower_stmt(stmt));
}
}
P(hir::Block {
id: b.id,
id: self.lower_node_id(b.id),
stmts: stmts.into(),
expr: expr,
rules: self.lower_block_check_mode(&b.rules),
@ -1046,14 +1203,31 @@ impl<'a> LoweringContext<'a> {
let mut path = self.lower_path_extra(import.id, path, suffix,
ParamMode::Explicit, true);
path.span = span;
self.items.insert(import.id, hir::Item {
self.allocate_hir_id_counter(import.id, import);
self.with_hir_id_owner(import.id, |this| {
let vis = match *vis {
hir::Visibility::Public => hir::Visibility::Public,
hir::Visibility::Crate => hir::Visibility::Crate,
hir::Visibility::Inherited => hir::Visibility::Inherited,
hir::Visibility::Restricted { ref path, id: _ } => {
hir::Visibility::Restricted {
path: path.clone(),
// We are allocating a new NodeId here
id: this.next_id(),
}
}
};
this.items.insert(import.id, hir::Item {
id: import.id,
name: import.rename.unwrap_or(ident).name,
attrs: attrs.clone(),
node: hir::ItemUse(P(path), hir::UseKind::Single),
vis: vis.clone(),
vis: vis,
span: span,
});
});
}
path
}
@ -1167,7 +1341,7 @@ impl<'a> LoweringContext<'a> {
fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem {
self.with_parent_def(i.id, |this| {
hir::TraitItem {
id: i.id,
id: this.lower_node_id(i.id),
name: i.ident.name,
attrs: this.lower_attrs(&i.attrs),
node: match i.node {
@ -1228,10 +1402,10 @@ impl<'a> LoweringContext<'a> {
fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem {
self.with_parent_def(i.id, |this| {
hir::ImplItem {
id: i.id,
id: this.lower_node_id(i.id),
name: i.ident.name,
attrs: this.lower_attrs(&i.attrs),
vis: this.lower_visibility(&i.vis),
vis: this.lower_visibility(&i.vis, None),
defaultness: this.lower_defaultness(i.defaultness, true /* [1] */),
node: match i.node {
ImplItemKind::Const(ref ty, ref expr) => {
@ -1260,7 +1434,7 @@ impl<'a> LoweringContext<'a> {
id: hir::ImplItemId { node_id: i.id },
name: i.ident.name,
span: i.span,
vis: self.lower_visibility(&i.vis),
vis: self.lower_visibility(&i.vis, Some(i.id)),
defaultness: self.lower_defaultness(i.defaultness, true /* [1] */),
kind: match i.node {
ImplItemKind::Const(..) => hir::AssociatedItemKind::Const,
@ -1299,7 +1473,6 @@ impl<'a> LoweringContext<'a> {
pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item> {
let mut name = i.ident.name;
let attrs = self.lower_attrs(&i.attrs);
let mut vis = self.lower_visibility(&i.vis);
if let ItemKind::MacroDef(ref tts) = i.node {
if i.attrs.iter().any(|attr| attr.path == "macro_export") {
self.exported_macros.push(hir::MacroDef {
@ -1309,12 +1482,13 @@ impl<'a> LoweringContext<'a> {
return None;
}
let mut vis = self.lower_visibility(&i.vis, None);
let node = self.with_parent_def(i.id, |this| {
this.lower_item_kind(i.id, &mut name, &attrs, &mut vis, &i.node)
});
Some(hir::Item {
id: i.id,
id: self.lower_node_id(i.id),
name: name,
attrs: attrs,
node: node,
@ -1326,7 +1500,7 @@ impl<'a> LoweringContext<'a> {
fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem {
self.with_parent_def(i.id, |this| {
hir::ForeignItem {
id: i.id,
id: this.lower_node_id(i.id),
name: i.ident.name,
attrs: this.lower_attrs(&i.attrs),
node: match i.node {
@ -1339,7 +1513,7 @@ impl<'a> LoweringContext<'a> {
hir::ForeignItemStatic(this.lower_ty(t), m)
}
},
vis: this.lower_visibility(&i.vis),
vis: this.lower_visibility(&i.vis, None),
span: i.span,
}
})
@ -1405,7 +1579,7 @@ impl<'a> LoweringContext<'a> {
fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
P(hir::Pat {
id: p.id,
id: self.lower_node_id(p.id),
node: match p.node {
PatKind::Wild => hir::PatKind::Wild,
PatKind::Ident(ref binding_mode, pth1, ref sub) => {
@ -1491,9 +1665,7 @@ impl<'a> LoweringContext<'a> {
}
fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
hir::Expr {
id: e.id,
node: match e.node {
let kind = match e.node {
// Issue #22181:
// Eventually a desugaring for `box EXPR`
// (similar to the desugaring above for `in PLACE BLOCK`)
@ -1510,8 +1682,8 @@ impl<'a> LoweringContext<'a> {
// }
//
// But for now there are type-inference issues doing that.
ExprKind::Box(ref e) => {
hir::ExprBox(P(self.lower_expr(e)))
ExprKind::Box(ref inner) => {
hir::ExprBox(P(self.lower_expr(inner)))
}
// Desugar ExprBox: `in (PLACE) EXPR`
@ -1602,8 +1774,7 @@ impl<'a> LoweringContext<'a> {
};
let block = self.block_all(e.span, hir_vec![s1, s2, s3], Some(expr));
// add the attributes to the outer returned expr node
return self.expr_block(P(block), e.attrs.clone());
hir::ExprBlock(P(block))
}
ExprKind::Array(ref exprs) => {
@ -1864,78 +2035,115 @@ impl<'a> LoweringContext<'a> {
// _ => [<else_opt> | ()]
// }
let mut arms = vec![];
// `<pat> => <body>`
let pat_arm = {
{
let body = self.lower_block(body, None);
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pat = self.lower_pat(pat);
self.arm(hir_vec![pat], body_expr)
};
arms.push(self.arm(hir_vec![pat], body_expr));
}
// `[_ if <else_opt_if_cond> => <else_opt_if_body>,]`
let mut else_opt = else_opt.as_ref().map(|e| P(self.lower_expr(e)));
let else_if_arms = {
let mut arms = vec![];
// `_ => [<else_opt> | ()]`
{
let mut current: Option<&Expr> = else_opt.as_ref().map(|p| &**p);
let mut else_exprs: Vec<Option<&Expr>> = vec![current];
// First, we traverse the AST and recursively collect all
// `else` branches into else_exprs, e.g.:
//
// if let Some(_) = x {
// ...
// } else if ... { // Expr1
// ...
// } else if ... { // Expr2
// ...
// } else { // Expr3
// ...
// }
//
// ... results in else_exprs = [Some(&Expr1),
// Some(&Expr2),
// Some(&Expr3)]
//
// Because there also the case there is no `else`, these
// entries can also be `None`, as in:
//
// if let Some(_) = x {
// ...
// } else if ... { // Expr1
// ...
// } else if ... { // Expr2
// ...
// }
//
// ... results in else_exprs = [Some(&Expr1),
// Some(&Expr2),
// None]
//
// The last entry in this list is always translated into
// the final "unguard" wildcard arm of the `match`. In the
// case of a `None`, it becomes `_ => ()`.
loop {
let else_opt_continue = else_opt.and_then(|els| {
els.and_then(|els| {
match els.node {
// else if
hir::ExprIf(cond, then, else_opt) => {
let pat_under = self.pat_wild(e.span);
if let Some(e) = current {
// There is an else branch at this level
if let ExprKind::If(_, _, ref else_opt) = e.node {
// The else branch is again an if-expr
current = else_opt.as_ref().map(|p| &**p);
else_exprs.push(current);
} else {
// The last item in the list is not an if-expr,
// stop here
break
}
} else {
// We have no more else branch
break
}
}
// Now translate the list of nested else-branches into the
// arms of the match statement.
for else_expr in else_exprs {
if let Some(else_expr) = else_expr {
let (guard, body) = if let ExprKind::If(ref cond,
ref then,
_) = else_expr.node {
let then = self.lower_block(then, None);
(Some(cond),
self.expr_block(then, ThinVec::new()))
} else {
(None,
self.lower_expr(else_expr))
};
arms.push(hir::Arm {
attrs: hir_vec![],
pats: hir_vec![pat_under],
guard: Some(cond),
body: P(self.expr_block(then, ThinVec::new())),
pats: hir_vec![self.pat_wild(e.span)],
guard: guard.map(|e| P(self.lower_expr(e))),
body: P(body),
});
else_opt.map(|else_opt| (else_opt, true))
}
_ => Some((P(els), false)),
}
})
});
match else_opt_continue {
Some((e, true)) => {
else_opt = Some(e);
}
Some((e, false)) => {
else_opt = Some(e);
break;
}
None => {
else_opt = None;
break;
} else {
// There was no else-branch, push a noop
let pat_under = self.pat_wild(e.span);
let unit = self.expr_tuple(e.span, hir_vec![]);
arms.push(self.arm(hir_vec![pat_under], unit));
}
}
}
arms
};
let contains_else_clause = else_opt.is_some();
// `_ => [<else_opt> | ()]`
let else_arm = {
let pat_under = self.pat_wild(e.span);
let else_expr =
else_opt.unwrap_or_else(|| self.expr_tuple(e.span, hir_vec![]));
self.arm(hir_vec![pat_under], else_expr)
};
let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
arms.push(pat_arm);
arms.extend(else_if_arms);
arms.push(else_arm);
let sub_expr = P(self.lower_expr(sub_expr));
// add attributes to the outer returned expr node
return self.expr(e.span,
hir::ExprMatch(sub_expr,
hir::ExprMatch(
sub_expr,
arms.into(),
hir::MatchSource::IfLetDesugar {
contains_else_clause: contains_else_clause,
}),
e.attrs.clone());
})
}
// Desugar ExprWhileLet
@ -1984,8 +2192,7 @@ impl<'a> LoweringContext<'a> {
let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
hir::LoopSource::WhileLet);
// add attributes to the outer returned expr node
let attrs = e.attrs.clone();
return hir::Expr { id: e.id, node: loop_expr, span: e.span, attrs: attrs };
loop_expr
}
// Desugar ExprForLoop
@ -2056,7 +2263,7 @@ impl<'a> LoweringContext<'a> {
let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
hir::LoopSource::ForLoop);
let loop_expr = P(hir::Expr {
id: e.id,
id: self.lower_node_id(e.id),
node: loop_expr,
span: e.span,
attrs: ThinVec::new(),
@ -2182,16 +2389,22 @@ impl<'a> LoweringContext<'a> {
thin_attrs))
};
let err_pat = self.pat_err(e.span, err_local);
self.arm(hir_vec![err_pat], ret_expr)
};
return self.expr_match(e.span, discr, hir_vec![err_arm, ok_arm],
hir::MatchSource::TryDesugar);
hir::ExprMatch(discr,
hir_vec![err_arm, ok_arm],
hir::MatchSource::TryDesugar)
}
ExprKind::Mac(_) => panic!("Shouldn't exist here"),
},
};
hir::Expr {
id: self.lower_node_id(e.id),
node: kind,
span: e.span,
attrs: e.attrs.clone(),
}
@ -2203,7 +2416,7 @@ impl<'a> LoweringContext<'a> {
node: hir::StmtDecl(P(Spanned {
node: hir::DeclLocal(self.lower_local(l)),
span: s.span,
}), s.id),
}), self.lower_node_id(s.id)),
span: s.span,
},
StmtKind::Item(ref it) => {
@ -2213,19 +2426,23 @@ impl<'a> LoweringContext<'a> {
node: hir::StmtDecl(P(Spanned {
node: hir::DeclItem(item_id),
span: s.span,
}), id.take().unwrap_or_else(|| self.next_id())),
}), id.take()
.map(|id| self.lower_node_id(id))
.unwrap_or_else(|| self.next_id())),
span: s.span,
}).collect();
}
StmtKind::Expr(ref e) => {
Spanned {
node: hir::StmtExpr(P(self.lower_expr(e)), s.id),
node: hir::StmtExpr(P(self.lower_expr(e)),
self.lower_node_id(s.id)),
span: s.span,
}
}
StmtKind::Semi(ref e) => {
Spanned {
node: hir::StmtSemi(P(self.lower_expr(e)), s.id),
node: hir::StmtSemi(P(self.lower_expr(e)),
self.lower_node_id(s.id)),
span: s.span,
}
}
@ -2240,14 +2457,26 @@ impl<'a> LoweringContext<'a> {
}
}
fn lower_visibility(&mut self, v: &Visibility) -> hir::Visibility {
/// If an `explicit_owner` is given, this method allocates the `HirId` in
/// the address space of that item instead of the item currently being
/// lowered. This can happen during `lower_impl_item_ref()` where we need to
/// lower a `Visibility` value although we haven't lowered the owning
/// `ImplItem` in question yet.
fn lower_visibility(&mut self,
v: &Visibility,
explicit_owner: Option<NodeId>)
-> hir::Visibility {
match *v {
Visibility::Public => hir::Public,
Visibility::Crate(_) => hir::Visibility::Crate,
Visibility::Restricted { ref path, id } => {
hir::Visibility::Restricted {
path: P(self.lower_path(id, path, ParamMode::Explicit, true)),
id: id
id: if let Some(owner) = explicit_owner {
self.lower_node_id_with_owner(id, owner)
} else {
self.lower_node_id(id)
}
}
}
Visibility::Inherited => hir::Inherited,

View File

@ -14,8 +14,10 @@
//! There are also some rather random cases (like const initializer
//! expressions) that are mostly just leftovers.
use hir;
use hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::stable_hasher::StableHasher;
use serialize::{Encodable, Decodable, Encoder, Decoder};
use std::fmt::Write;
@ -121,6 +123,7 @@ pub struct Definitions {
table: DefPathTable,
node_to_def_index: NodeMap<DefIndex>,
def_index_to_node: Vec<ast::NodeId>,
pub(super) node_to_hir_id: IndexVec<ast::NodeId, hir::HirId>,
}
/// A unique identifier that we can use to lookup a definition
@ -206,6 +209,23 @@ impl DefPath {
s
}
/// Returns a string representation of the DefPath without
/// the crate-prefix. This method is useful if you don't have
/// a TyCtxt available.
pub fn to_string_no_crate(&self) -> String {
let mut s = String::with_capacity(self.data.len() * 16);
for component in &self.data {
write!(s,
"::{}[{}]",
component.data.as_interned_str(),
component.disambiguator)
.unwrap();
}
s
}
pub fn deterministic_hash(&self, tcx: TyCtxt) -> u64 {
debug!("deterministic_hash({:?})", self);
let mut state = StableHasher::new();
@ -275,6 +295,7 @@ impl Definitions {
},
node_to_def_index: NodeMap(),
def_index_to_node: vec![],
node_to_hir_id: IndexVec::new(),
}
}
@ -367,6 +388,15 @@ impl Definitions {
index
}
/// Initialize the ast::NodeId to HirId mapping once it has been generated during
/// AST to HIR lowering.
pub fn init_node_id_to_hir_id_mapping(&mut self,
mapping: IndexVec<ast::NodeId, hir::HirId>) {
assert!(self.node_to_hir_id.is_empty(),
"Trying initialize NodeId -> HirId mapping twice");
self.node_to_hir_id = mapping;
}
}
impl DefPathData {

View File

@ -0,0 +1,184 @@
// Copyright 2017 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.
use hir::def_id::{DefId, DefIndex, CRATE_DEF_INDEX};
use hir::{self, intravisit, HirId, ItemLocalId};
use syntax::ast::NodeId;
use hir::itemlikevisit::ItemLikeVisitor;
use rustc_data_structures::fx::FxHashMap;
pub fn check_crate<'hir>(hir_map: &hir::map::Map<'hir>) {
let mut outer_visitor = OuterVisitor {
hir_map: hir_map,
errors: vec![],
};
hir_map.dep_graph.with_ignore(|| {
hir_map.krate().visit_all_item_likes(&mut outer_visitor);
if !outer_visitor.errors.is_empty() {
let message = outer_visitor
.errors
.iter()
.fold(String::new(), |s1, s2| s1 + "\n" + s2);
bug!("{}", message);
}
});
}
struct HirIdValidator<'a, 'hir: 'a> {
hir_map: &'a hir::map::Map<'hir>,
owner_def_index: Option<DefIndex>,
hir_ids_seen: FxHashMap<ItemLocalId, NodeId>,
errors: Vec<String>,
}
struct OuterVisitor<'a, 'hir: 'a> {
hir_map: &'a hir::map::Map<'hir>,
errors: Vec<String>,
}
impl<'a, 'hir: 'a> OuterVisitor<'a, 'hir> {
fn new_inner_visitor(&self,
hir_map: &'a hir::map::Map<'hir>)
-> HirIdValidator<'a, 'hir> {
HirIdValidator {
hir_map: hir_map,
owner_def_index: None,
hir_ids_seen: FxHashMap(),
errors: Vec::new(),
}
}
}
impl<'a, 'hir: 'a> ItemLikeVisitor<'hir> for OuterVisitor<'a, 'hir> {
fn visit_item(&mut self, i: &'hir hir::Item) {
let mut inner_visitor = self.new_inner_visitor(self.hir_map);
inner_visitor.check(i.id, |this| intravisit::walk_item(this, i));
self.errors.extend(inner_visitor.errors.drain(..));
}
fn visit_trait_item(&mut self, i: &'hir hir::TraitItem) {
let mut inner_visitor = self.new_inner_visitor(self.hir_map);
inner_visitor.check(i.id, |this| intravisit::walk_trait_item(this, i));
self.errors.extend(inner_visitor.errors.drain(..));
}
fn visit_impl_item(&mut self, i: &'hir hir::ImplItem) {
let mut inner_visitor = self.new_inner_visitor(self.hir_map);
inner_visitor.check(i.id, |this| intravisit::walk_impl_item(this, i));
self.errors.extend(inner_visitor.errors.drain(..));
}
}
impl<'a, 'hir: 'a> HirIdValidator<'a, 'hir> {
fn check<F: FnOnce(&mut HirIdValidator<'a, 'hir>)>(&mut self,
node_id: NodeId,
walk: F) {
assert!(self.owner_def_index.is_none());
let owner_def_index = self.hir_map.local_def_id(node_id).index;
self.owner_def_index = Some(owner_def_index);
walk(self);
if owner_def_index == CRATE_DEF_INDEX {
return
}
// There's always at least one entry for the owning item itself
let max = self.hir_ids_seen
.keys()
.map(|local_id| local_id.as_usize())
.max()
.unwrap();
if max != self.hir_ids_seen.len() - 1 {
// Collect the missing ItemLocalIds
let missing: Vec<_> = (0 .. max + 1)
.filter(|&i| !self.hir_ids_seen.contains_key(&ItemLocalId(i as u32)))
.collect();
// Try to map those to something more useful
let mut missing_items = vec![];
for local_id in missing {
let hir_id = HirId {
owner: owner_def_index,
local_id: ItemLocalId(local_id as u32),
};
// We are already in ICE mode here, so doing a linear search
// should be fine.
let (node_id, _) = self.hir_map
.definitions()
.node_to_hir_id
.iter()
.enumerate()
.find(|&(_, &entry)| hir_id == entry)
.unwrap();
let node_id = NodeId::new(node_id);
missing_items.push(format!("[local_id: {}, node:{}]",
local_id,
self.hir_map.node_to_string(node_id)));
}
self.errors.push(format!(
"ItemLocalIds not assigned densely in {}. \
Max ItemLocalId = {}, missing IDs = {:?}",
self.hir_map.def_path(DefId::local(owner_def_index)).to_string_no_crate(),
max,
missing_items));
}
}
}
impl<'a, 'hir: 'a> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
fn nested_visit_map<'this>(&'this mut self)
-> intravisit::NestedVisitorMap<'this, 'hir> {
intravisit::NestedVisitorMap::OnlyBodies(self.hir_map)
}
fn visit_id(&mut self, node_id: NodeId) {
let owner = self.owner_def_index.unwrap();
let stable_id = self.hir_map.definitions().node_to_hir_id[node_id];
if stable_id == hir::DUMMY_HIR_ID {
self.errors.push(format!("HirIdValidator: No HirId assigned for NodeId {}: {:?}",
node_id,
self.hir_map.node_to_string(node_id)));
}
if owner != stable_id.owner {
self.errors.push(format!(
"HirIdValidator: The recorded owner of {} is {} instead of {}",
self.hir_map.node_to_string(node_id),
self.hir_map.def_path(DefId::local(stable_id.owner)).to_string_no_crate(),
self.hir_map.def_path(DefId::local(owner)).to_string_no_crate()));
}
if let Some(prev) = self.hir_ids_seen.insert(stable_id.local_id, node_id) {
if prev != node_id {
self.errors.push(format!(
"HirIdValidator: Same HirId {}/{} assigned for nodes {} and {}",
self.hir_map.def_path(DefId::local(stable_id.owner)).to_string_no_crate(),
stable_id.local_id.as_usize(),
self.hir_map.node_to_string(prev),
self.hir_map.node_to_string(node_id)));
}
}
}
fn visit_impl_item_ref(&mut self, _: &'hir hir::ImplItemRef) {
// Explicitly do nothing here. ImplItemRefs contain hir::Visibility
// values that actually belong to an ImplItem instead of the ItemImpl
// we are currently in. So for those it's correct that they have a
// different owner.
}
}

View File

@ -36,6 +36,7 @@ pub mod blocks;
mod collector;
mod def_collector;
pub mod definitions;
mod hir_id_validator;
#[derive(Copy, Clone, Debug)]
pub enum Node<'hir> {
@ -964,13 +965,17 @@ pub fn map_crate<'hir>(forest: &'hir mut Forest,
entries, vector_length, (entries as f64 / vector_length as f64) * 100.);
}
Map {
let map = Map {
forest: forest,
dep_graph: forest.dep_graph.clone(),
map: map,
definitions: definitions,
inlined_bodies: RefCell::new(DefIdMap()),
}
};
hir_id_validator::check_crate(&map);
map
}
/// Identical to the `PpAnn` implementation for `hir::Crate`,

View File

@ -30,7 +30,7 @@ pub use self::Visibility::{Public, Inherited};
pub use self::PathParameters::*;
use hir::def::Def;
use hir::def_id::DefId;
use hir::def_id::{DefId, DefIndex, CRATE_DEF_INDEX};
use util::nodemap::{NodeMap, FxHashSet};
use syntax_pos::{Span, ExpnId, DUMMY_SP};
@ -43,6 +43,8 @@ use syntax::symbol::{Symbol, keywords};
use syntax::tokenstream::TokenStream;
use syntax::util::ThinVec;
use rustc_data_structures::indexed_vec;
use std::collections::BTreeMap;
use std::fmt;
@ -73,6 +75,63 @@ pub mod pat_util;
pub mod print;
pub mod svh;
/// A HirId uniquely identifies a node in the HIR of then current crate. It is
/// composed of the `owner`, which is the DefIndex of the directly enclosing
/// hir::Item, hir::TraitItem, or hir::ImplItem (i.e. the closest "item-like"),
/// and the `local_id` which is unique within the given owner.
///
/// This two-level structure makes for more stable values: One can move an item
/// around within the source code, or add or remove stuff before it, without
/// the local_id part of the HirId changing, which is a very useful property
/// incremental compilation where we have to persist things through changes to
/// the code base.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug,
RustcEncodable, RustcDecodable)]
pub struct HirId {
pub owner: DefIndex,
pub local_id: ItemLocalId,
}
/// An `ItemLocalId` uniquely identifies something within a given "item-like",
/// that is within a hir::Item, hir::TraitItem, or hir::ImplItem. There is no
/// guarantee that the numerical value of a given `ItemLocalId` corresponds to
/// the node's position within the owning item in any way, but there is a
/// guarantee that the `LocalItemId`s within an owner occupy a dense range of
/// integers starting at zero, so a mapping that maps all or most nodes within
/// an "item-like" to something else can be implement by a `Vec` instead of a
/// tree or hash map.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug,
RustcEncodable, RustcDecodable)]
pub struct ItemLocalId(pub u32);
impl ItemLocalId {
pub fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl indexed_vec::Idx for ItemLocalId {
fn new(idx: usize) -> Self {
debug_assert!((idx as u32) as usize == idx);
ItemLocalId(idx as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
/// The `HirId` corresponding to CRATE_NODE_ID and CRATE_DEF_INDEX
pub const CRATE_HIR_ID: HirId = HirId {
owner: CRATE_DEF_INDEX,
local_id: ItemLocalId(0)
};
pub const DUMMY_HIR_ID: HirId = HirId {
owner: CRATE_DEF_INDEX,
local_id: ItemLocalId(!0)
};
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
pub struct Lifetime {
pub id: NodeId,

View File

@ -178,19 +178,11 @@ impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> {
block.stmts = block.stmts.move_flat_map(|mut stmt| {
remaining_stmts -= 1;
match stmt.node {
// Avoid wasting a node id on a trailing expression statement,
// which shares a HIR node with the expression itself.
ast::StmtKind::Expr(ref expr) if remaining_stmts == 0 => stmt.id = expr.id,
_ if self.monotonic => {
if self.monotonic {
assert_eq!(stmt.id, ast::DUMMY_NODE_ID);
stmt.id = self.cx.resolver.next_node_id();
}
_ => {}
}
Some(stmt)
});

View File

@ -18,7 +18,7 @@ trait SomeTrait { }
// Bounds on object types:
struct Foo<'a,'b,'c> { //~ ERROR parameter `'b` is never used
struct Foo<'a,'b,'c> { //~ ERROR parameter `'c` is never used
// All of these are ok, because we can derive exactly one bound:
a: Box<IsStatic>,
b: Box<Is<'static>>,