Skip to content

Commit

Permalink
rustc: always include elidable lifetimes in HIR types.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Jan 28, 2017
1 parent f79feba commit 7a2a669
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 132 deletions.
4 changes: 2 additions & 2 deletions src/librustc/hir/intravisit.rs
Expand Up @@ -547,8 +547,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
TyPtr(ref mutable_type) => {
visitor.visit_ty(&mutable_type.ty)
}
TyRptr(ref opt_lifetime, ref mutable_type) => {
walk_list!(visitor, visit_lifetime, opt_lifetime);
TyRptr(ref lifetime, ref mutable_type) => {
visitor.visit_lifetime(lifetime);
visitor.visit_ty(&mutable_type.ty)
}
TyNever => {},
Expand Down
150 changes: 120 additions & 30 deletions src/librustc/hir/lowering.rs
Expand Up @@ -41,12 +41,12 @@
// in the HIR, especially for multiple identifiers.

use hir;
use hir::map::Definitions;
use hir::map::{Definitions, DefKey};
use hir::map::definitions::DefPathData;
use hir::def_id::{DefIndex, DefId};
use hir::def::{Def, PathResolution};
use session::Session;
use util::nodemap::{NodeMap, FxHashMap};
use util::nodemap::{DefIdMap, NodeMap, FxHashMap};

use std::collections::BTreeMap;
use std::iter;
Expand Down Expand Up @@ -78,6 +78,8 @@ pub struct LoweringContext<'a> {
trait_items: BTreeMap<hir::TraitItemId, hir::TraitItem>,
impl_items: BTreeMap<hir::ImplItemId, hir::ImplItem>,
bodies: FxHashMap<hir::BodyId, hir::Body>,

type_def_lifetime_params: DefIdMap<usize>,
}

pub trait Resolver {
Expand Down Expand Up @@ -110,6 +112,7 @@ pub fn lower_crate(sess: &Session,
trait_items: BTreeMap::new(),
impl_items: BTreeMap::new(),
bodies: FxHashMap(),
type_def_lifetime_params: DefIdMap(),
}.lower_crate(krate)
}

Expand All @@ -123,24 +126,33 @@ enum ParamMode {

impl<'a> LoweringContext<'a> {
fn lower_crate(mut self, c: &Crate) -> hir::Crate {
self.lower_items(c);
let module = self.lower_mod(&c.module);
let attrs = self.lower_attrs(&c.attrs);
let exported_macros = c.exported_macros.iter().map(|m| self.lower_macro_def(m)).collect();
/// Full-crate AST visitor that inserts into a fresh
/// `LoweringContext` any information that may be
/// needed from arbitrary locations in the crate.
/// E.g. The number of lifetime generic parameters
/// declared for every type and trait definition.
struct MiscCollector<'lcx, 'interner: 'lcx> {
lctx: &'lcx mut LoweringContext<'interner>,
}

hir::Crate {
module: module,
attrs: attrs,
span: c.span,
exported_macros: exported_macros,
items: self.items,
trait_items: self.trait_items,
impl_items: self.impl_items,
bodies: self.bodies,
impl<'lcx, 'interner> Visitor<'lcx> for MiscCollector<'lcx, 'interner> {
fn visit_item(&mut self, item: &'lcx Item) {
match item.node {
ItemKind::Struct(_, ref generics) |
ItemKind::Union(_, ref generics) |
ItemKind::Enum(_, ref generics) |
ItemKind::Ty(_, ref generics) |
ItemKind::Trait(_, ref generics, ..) => {
let def_id = self.lctx.resolver.definitions().local_def_id(item.id);
let count = generics.lifetimes.len();
self.lctx.type_def_lifetime_params.insert(def_id, count);
}
_ => {}
}
visit::walk_item(self, item);
}
}
}

fn lower_items(&mut self, c: &Crate) {
struct ItemLowerer<'lcx, 'interner: 'lcx> {
lctx: &'lcx mut LoweringContext<'interner>,
}
Expand All @@ -167,8 +179,23 @@ impl<'a> LoweringContext<'a> {
}
}

let mut item_lowerer = ItemLowerer { lctx: self };
visit::walk_crate(&mut item_lowerer, c);
visit::walk_crate(&mut MiscCollector { lctx: &mut self }, c);
visit::walk_crate(&mut ItemLowerer { lctx: &mut self }, c);

let module = self.lower_mod(&c.module);
let attrs = self.lower_attrs(&c.attrs);
let exported_macros = c.exported_macros.iter().map(|m| self.lower_macro_def(m)).collect();

hir::Crate {
module: module,
attrs: attrs,
span: c.span,
exported_macros: exported_macros,
items: self.items,
trait_items: self.trait_items,
impl_items: self.impl_items,
bodies: self.bodies,
}
}

fn record_body(&mut self, value: hir::Expr, decl: Option<&FnDecl>)
Expand Down Expand Up @@ -232,6 +259,14 @@ impl<'a> LoweringContext<'a> {
result
}

fn def_key(&mut self, id: DefId) -> DefKey {
if id.is_local() {
self.resolver.definitions().def_key(id.index)
} else {
self.sess.cstore.def_key(id)
}
}

fn lower_opt_sp_ident(&mut self, o_id: Option<Spanned<Ident>>) -> Option<Spanned<Name>> {
o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name))
}
Expand Down Expand Up @@ -279,7 +314,11 @@ impl<'a> LoweringContext<'a> {
TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty)),
TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)),
TyKind::Rptr(ref region, ref mt) => {
hir::TyRptr(self.lower_opt_lifetime(region), self.lower_mt(mt))
let lifetime = match *region {
Some(ref lt) => self.lower_lifetime(lt),
None => self.elided_lifetime(t.span)
};
hir::TyRptr(lifetime, self.lower_mt(mt))
}
TyKind::BareFn(ref f) => {
hir::TyBareFn(P(hir::BareFnTy {
Expand Down Expand Up @@ -377,7 +416,40 @@ impl<'a> LoweringContext<'a> {
}
_ => param_mode
};
self.lower_path_segment(segment, param_mode)

// Figure out if this is a type/trait segment,
// which may need lifetime elision performed.
let parent_def_id = |this: &mut Self, def_id: DefId| {
DefId {
krate: def_id.krate,
index: this.def_key(def_id).parent.expect("missing parent")
}
};
let type_def_id = match resolution.base_def {
Def::AssociatedTy(def_id) if i + 2 == proj_start => {
Some(parent_def_id(self, def_id))
}
Def::Variant(def_id) if i + 1 == proj_start => {
Some(parent_def_id(self, def_id))
}
Def::Struct(def_id) |
Def::Union(def_id) |
Def::Enum(def_id) |
Def::TyAlias(def_id) |
Def::Trait(def_id) if i + 1 == proj_start => Some(def_id),
_ => None
};

let num_lifetimes = type_def_id.map_or(0, |def_id| {
if let Some(&n) = self.type_def_lifetime_params.get(&def_id) {
return n;
}
assert!(!def_id.is_local());
let (n, _) = self.sess.cstore.item_generics_own_param_counts(def_id);
self.type_def_lifetime_params.insert(def_id, n);
n
});
self.lower_path_segment(p.span, segment, param_mode, num_lifetimes)
}).collect(),
span: p.span,
});
Expand Down Expand Up @@ -411,7 +483,7 @@ impl<'a> LoweringContext<'a> {
// 3. `<<std::vec::Vec<T>>::IntoIter>::Item`
// * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`
for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {
let segment = P(self.lower_path_segment(segment, param_mode));
let segment = P(self.lower_path_segment(p.span, segment, param_mode, 0));
let qpath = hir::QPath::TypeRelative(ty, segment);

// It's finished, return the extension of the right node type.
Expand Down Expand Up @@ -443,7 +515,7 @@ impl<'a> LoweringContext<'a> {
hir::Path {
def: self.expect_full_def(id),
segments: segments.map(|segment| {
self.lower_path_segment(segment, param_mode)
self.lower_path_segment(p.span, segment, param_mode, 0)
}).chain(name.map(|name| {
hir::PathSegment {
name: name,
Expand All @@ -464,10 +536,12 @@ impl<'a> LoweringContext<'a> {
}

fn lower_path_segment(&mut self,
path_span: Span,
segment: &PathSegment,
param_mode: ParamMode)
param_mode: ParamMode,
expected_lifetimes: usize)
-> hir::PathSegment {
let parameters = if let Some(ref parameters) = segment.parameters {
let mut parameters = if let Some(ref parameters) = segment.parameters {
match **parameters {
PathParameters::AngleBracketed(ref data) => {
let data = self.lower_angle_bracketed_parameter_data(data, param_mode);
Expand All @@ -482,6 +556,14 @@ impl<'a> LoweringContext<'a> {
hir::AngleBracketedParameters(data)
};

if let hir::AngleBracketedParameters(ref mut data) = parameters {
if data.lifetimes.is_empty() {
data.lifetimes = (0..expected_lifetimes).map(|_| {
self.elided_lifetime(path_span)
}).collect();
}
}

hir::PathSegment {
name: segment.identifier.name,
parameters: parameters,
Expand Down Expand Up @@ -628,10 +710,6 @@ impl<'a> LoweringContext<'a> {
lts.iter().map(|l| self.lower_lifetime_def(l)).collect()
}

fn lower_opt_lifetime(&mut self, o_lt: &Option<Lifetime>) -> Option<hir::Lifetime> {
o_lt.as_ref().map(|lt| self.lower_lifetime(lt))
}

fn lower_generics(&mut self, g: &Generics) -> hir::Generics {
// Collect `?Trait` bounds in where clause and move them to parameter definitions.
let mut add_bounds = NodeMap();
Expand Down Expand Up @@ -751,8 +829,12 @@ impl<'a> LoweringContext<'a> {
}

fn lower_trait_ref(&mut self, p: &TraitRef) -> hir::TraitRef {
let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit) {
hir::QPath::Resolved(None, path) => path.and_then(|path| path),
qpath => bug!("lower_trait_ref: unexpected QPath `{:?}`", qpath)
};
hir::TraitRef {
path: self.lower_path(p.ref_id, &p.path, ParamMode::Explicit, false),
path: path,
ref_id: p.ref_id,
}
}
Expand Down Expand Up @@ -2276,4 +2358,12 @@ impl<'a> LoweringContext<'a> {
span: span,
})
}

fn elided_lifetime(&mut self, span: Span) -> hir::Lifetime {
hir::Lifetime {
id: self.next_id(),
span: span,
name: keywords::Invalid.name()
}
}
}
45 changes: 14 additions & 31 deletions src/librustc/hir/mod.rs
Expand Up @@ -77,6 +77,13 @@ pub mod svh;
pub struct Lifetime {
pub id: NodeId,
pub span: Span,

/// Either "'a", referring to a named lifetime definition,
/// or "" (aka keywords::Invalid), for elision placeholders.
///
/// HIR lowering inserts these placeholders in type paths that
/// refer to type definitions needing lifetime parameters,
/// `&T` and `&mut T`, and trait objects without `... + 'a`.
pub name: Name,
}

Expand All @@ -89,6 +96,12 @@ impl fmt::Debug for Lifetime {
}
}

impl Lifetime {
pub fn is_elided(&self) -> bool {
self.name == keywords::Invalid.name()
}
}

/// A lifetime definition, eg `'a: 'b+'c+'d`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct LifetimeDef {
Expand Down Expand Up @@ -165,30 +178,6 @@ impl PathParameters {
})
}

pub fn is_empty(&self) -> bool {
match *self {
AngleBracketedParameters(ref data) => data.is_empty(),

// Even if the user supplied no types, something like
// `X()` is equivalent to `X<(),()>`.
ParenthesizedParameters(..) => false,
}
}

pub fn has_lifetimes(&self) -> bool {
match *self {
AngleBracketedParameters(ref data) => !data.lifetimes.is_empty(),
ParenthesizedParameters(_) => false,
}
}

pub fn has_types(&self) -> bool {
match *self {
AngleBracketedParameters(ref data) => !data.types.is_empty(),
ParenthesizedParameters(..) => true,
}
}

/// Returns the types that the user wrote. Note that these do not necessarily map to the type
/// parameters in the parenthesized case.
pub fn types(&self) -> HirVec<&P<Ty>> {
Expand Down Expand Up @@ -245,12 +234,6 @@ pub struct AngleBracketedParameterData {
pub bindings: HirVec<TypeBinding>,
}

impl AngleBracketedParameterData {
fn is_empty(&self) -> bool {
self.lifetimes.is_empty() && self.types.is_empty() && self.bindings.is_empty()
}
}

/// A path like `Foo(A,B) -> C`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct ParenthesizedParameterData {
Expand Down Expand Up @@ -1208,7 +1191,7 @@ pub enum Ty_ {
/// A raw pointer (`*const T` or `*mut T`)
TyPtr(MutTy),
/// A reference (`&'a T` or `&'a mut T`)
TyRptr(Option<Lifetime>, MutTy),
TyRptr(Lifetime, MutTy),
/// A bare function (e.g. `fn(usize) -> bool`)
TyBareFn(P<BareFnTy>),
/// The never type (`!`)
Expand Down

0 comments on commit 7a2a669

Please sign in to comment.