rustc: move object default lifetimes to resolve_lifetimes.

This commit is contained in:
Eduard-Mihai Burtescu 2017-01-25 17:32:44 +02:00
parent c5befdc630
commit 4eac052a33
16 changed files with 445 additions and 459 deletions

View file

@ -28,6 +28,7 @@ use hir::map as hir_map;
use hir::map::definitions::{Definitions, DefKey, DisambiguatedDefPathData};
use hir::svh::Svh;
use middle::lang_items;
use middle::resolve_lifetime::ObjectLifetimeDefault;
use ty::{self, Ty, TyCtxt};
use mir::Mir;
use session::Session;
@ -183,6 +184,8 @@ pub trait CrateStore<'tcx> {
fn item_generics<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
-> ty::Generics<'tcx>;
fn item_generics_own_param_counts(&self, def: DefId) -> (usize, usize);
fn item_generics_object_lifetime_defaults(&self, def: DefId)
-> Vec<ObjectLifetimeDefault>;
fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute>;
fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)-> ty::TraitDef;
fn adt_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> &'tcx ty::AdtDef;
@ -334,6 +337,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
-> ty::Generics<'tcx> { bug!("item_generics") }
fn item_generics_own_param_counts(&self, def: DefId) -> (usize, usize)
{ bug!("item_generics_own_param_counts") }
fn item_generics_object_lifetime_defaults(&self, def: DefId)
-> Vec<ObjectLifetimeDefault>
{ bug!("item_generics_object_lifetime_defaults") }
fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute> { bug!("item_attrs") }
fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)-> ty::TraitDef
{ bug!("trait_def") }

View file

@ -26,11 +26,12 @@ use ty;
use std::cell::Cell;
use std::mem::replace;
use syntax::ast;
use syntax::attr;
use syntax::ptr::P;
use syntax::symbol::keywords;
use syntax_pos::Span;
use errors::DiagnosticBuilder;
use util::nodemap::{NodeMap, FxHashSet, FxHashMap};
use util::nodemap::{NodeMap, FxHashSet, FxHashMap, DefIdMap};
use rustc_back::slice;
use hir;
@ -102,8 +103,46 @@ impl Region {
_ => self
}
}
fn subst(self, params: &[hir::Lifetime], map: &NamedRegionMap)
-> Option<Region> {
if let Region::EarlyBound(index, _) = self {
params.get(index as usize).and_then(|lifetime| {
map.defs.get(&lifetime.id).cloned()
})
} else {
Some(self)
}
}
}
/// 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)]
pub enum Set1<T> {
Empty,
One(T),
Many
}
impl<T: PartialEq> Set1<T> {
pub fn insert(&mut self, value: T) {
if let Set1::Empty = *self {
*self = Set1::One(value);
return;
}
if let Set1::One(ref old) = *self {
if *old == value {
return;
}
}
*self = Set1::Many;
}
}
pub type ObjectLifetimeDefault = Set1<Region>;
// Maps the id of each lifetime reference to the lifetime decl
// that it corresponds to.
pub struct NamedRegionMap {
@ -115,6 +154,10 @@ pub struct NamedRegionMap {
// are named regions appearing in fn arguments that do not appear
// in where-clauses
pub late_bound: NodeMap<ty::Issue32330>,
// For each type and trait definition, maps type parameters
// to the trait object lifetime defaults computed from them.
pub object_lifetime_defaults: NodeMap<Vec<ObjectLifetimeDefault>>,
}
struct LifetimeContext<'a, 'tcx: 'a> {
@ -141,6 +184,9 @@ struct LifetimeContext<'a, 'tcx: 'a> {
// List of labels in the function/method currently under analysis.
labels_in_fn: Vec<(ast::Name, Span)>,
// Cache for cross-crate per-definition object lifetime defaults.
xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
}
#[derive(Debug)]
@ -170,6 +216,14 @@ enum Scope<'a> {
s: ScopeRef<'a>
},
/// Use a specific lifetime (if `Some`) or leave it unset (to be
/// inferred in a function body or potentially error outside one),
/// for the default choice of lifetime in a trait object type.
ObjectLifetimeDefault {
lifetime: Option<Region>,
s: ScopeRef<'a>
},
Root
}
@ -208,6 +262,7 @@ pub fn krate(sess: &Session,
let mut map = NamedRegionMap {
defs: NodeMap(),
late_bound: NodeMap(),
object_lifetime_defaults: compute_object_lifetime_defaults(sess, hir_map),
};
sess.track_errors(|| {
let mut visitor = LifetimeContext {
@ -217,6 +272,7 @@ pub fn krate(sess: &Session,
scope: ROOT_SCOPE,
trait_ref_hack: false,
labels_in_fn: vec![],
xcrate_object_lifetime_defaults: DefIdMap(),
};
for (_, item) in &krate.items {
visitor.visit_item(item);
@ -326,10 +382,20 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
for bound in bounds {
self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
}
if !lifetime.is_elided() {
if lifetime.is_elided() {
self.resolve_object_lifetime_default(lifetime)
} else {
self.visit_lifetime(lifetime);
}
}
hir::TyRptr(ref lifetime_ref, ref mt) => {
self.visit_lifetime(lifetime_ref);
let scope = Scope::ObjectLifetimeDefault {
lifetime: self.map.defs.get(&lifetime_ref.id).cloned(),
s: self.scope
};
self.with(scope, |_, this| this.visit_ty(&mt.ty));
}
_ => {
intravisit::walk_ty(self, ty)
}
@ -372,20 +438,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
self.resolve_lifetime_ref(lifetime_ref);
}
fn visit_path_parameters(&mut self, _: Span, params: &'tcx hir::PathParameters) {
match *params {
hir::AngleBracketedParameters(ref data) => {
if data.lifetimes.iter().all(|l| l.is_elided()) {
self.resolve_elided_lifetimes(&data.lifetimes);
} else {
for l in &data.lifetimes { self.visit_lifetime(l); }
}
for ty in &data.types { self.visit_ty(ty); }
for b in &data.bindings { self.visit_assoc_type_binding(b); }
}
hir::ParenthesizedParameters(ref data) => {
self.visit_fn_like_elision(&data.inputs, data.output.as_ref());
}
fn visit_path(&mut self, path: &'tcx hir::Path, _: ast::NodeId) {
for (i, segment) in path.segments.iter().enumerate() {
let depth = path.segments.len() - i - 1;
self.visit_segment_parameters(path.def, depth, &segment.parameters);
}
}
@ -466,7 +522,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
for lifetime in &trait_ref.bound_lifetimes {
this.visit_lifetime_def(lifetime);
}
intravisit::walk_path(this, &trait_ref.trait_ref.path)
this.visit_trait_ref(&trait_ref.trait_ref)
})
} else {
self.visit_trait_ref(&trait_ref.trait_ref)
@ -585,7 +641,8 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
loop {
match *scope {
Scope::Body { s, .. } |
Scope::Elision { s, .. } => { scope = s; }
Scope::Elision { s, .. } |
Scope::ObjectLifetimeDefault { s, .. } => { scope = s; }
Scope::Root => { return; }
@ -606,6 +663,103 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
}
}
fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map)
-> NodeMap<Vec<ObjectLifetimeDefault>> {
let mut map = NodeMap();
for item in hir_map.krate().items.values() {
match item.node {
hir::ItemStruct(_, ref generics) |
hir::ItemUnion(_, ref generics) |
hir::ItemEnum(_, ref generics) |
hir::ItemTy(_, ref generics) |
hir::ItemTrait(_, ref generics, ..) => {
let result = object_lifetime_defaults_for_item(hir_map, generics);
// Debugging aid.
if attr::contains_name(&item.attrs, "rustc_object_lifetime_default") {
let object_lifetime_default_reprs: String =
result.iter().map(|set| {
match *set {
Set1::Empty => "BaseDefault".to_string(),
Set1::One(Region::Static) => "'static".to_string(),
Set1::One(Region::EarlyBound(i, _)) => {
generics.lifetimes[i as usize].lifetime.name.to_string()
}
Set1::One(_) => bug!(),
Set1::Many => "Ambiguous".to_string(),
}
}).collect::<Vec<String>>().join(",");
sess.span_err(item.span, &object_lifetime_default_reprs);
}
map.insert(item.id, result);
}
_ => {}
}
}
map
}
/// Scan the bounds and where-clauses on parameters to extract bounds
/// of the form `T:'a` so as to determine the `ObjectLifetimeDefault`
/// for each type parameter.
fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics)
-> Vec<ObjectLifetimeDefault> {
fn add_bounds(set: &mut Set1<ast::Name>, bounds: &[hir::TyParamBound]) {
for bound in bounds {
if let hir::RegionTyParamBound(ref lifetime) = *bound {
set.insert(lifetime.name);
}
}
}
generics.ty_params.iter().map(|param| {
let mut set = Set1::Empty;
add_bounds(&mut set, &param.bounds);
let param_def_id = hir_map.local_def_id(param.id);
for predicate in &generics.where_clause.predicates {
// Look for `type: ...` where clauses.
let data = match *predicate {
hir::WherePredicate::BoundPredicate(ref data) => data,
_ => continue
};
// Ignore `for<'a> type: ...` as they can change what
// lifetimes mean (although we could "just" handle it).
if !data.bound_lifetimes.is_empty() {
continue;
}
let def = match data.bounded_ty.node {
hir::TyPath(hir::QPath::Resolved(None, ref path)) => path.def,
_ => continue
};
if def == Def::TyParam(param_def_id) {
add_bounds(&mut set, &data.bounds);
}
}
match set {
Set1::Empty => Set1::Empty,
Set1::One(name) => {
if name == keywords::StaticLifetime.name() {
Set1::One(Region::Static)
} else {
generics.lifetimes.iter().enumerate().find(|&(_, def)| {
def.lifetime.name == name
}).map_or(Set1::Many, |(i, def)| {
Set1::One(Region::EarlyBound(i as u32, def.lifetime.id))
})
}
}
Set1::Many => Set1::Many
}
}).collect()
}
impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
// FIXME(#37666) this works around a limitation in the region inferencer
fn hack<F>(&mut self, f: F) where
@ -619,6 +773,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
{
let LifetimeContext {sess, hir_map, ref mut map, ..} = *self;
let labels_in_fn = replace(&mut self.labels_in_fn, vec![]);
let xcrate_object_lifetime_defaults =
replace(&mut self.xcrate_object_lifetime_defaults, DefIdMap());
let mut this = LifetimeContext {
sess: sess,
hir_map: hir_map,
@ -626,11 +782,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
scope: &wrap_scope,
trait_ref_hack: self.trait_ref_hack,
labels_in_fn: labels_in_fn,
xcrate_object_lifetime_defaults: xcrate_object_lifetime_defaults,
};
debug!("entering scope {:?}", this.scope);
f(self.scope, &mut this);
debug!("exiting scope {:?}", this.scope);
self.labels_in_fn = this.labels_in_fn;
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
}
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
@ -727,7 +885,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}
}
Scope::Elision { s, .. } => {
Scope::Elision { s, .. } |
Scope::ObjectLifetimeDefault { s, .. } => {
scope = s;
}
}
@ -763,6 +922,109 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}
}
fn visit_segment_parameters(&mut self,
def: Def,
depth: usize,
params: &'tcx hir::PathParameters) {
let data = match *params {
hir::ParenthesizedParameters(ref data) => {
self.visit_fn_like_elision(&data.inputs, data.output.as_ref());
return;
}
hir::AngleBracketedParameters(ref data) => data
};
if data.lifetimes.iter().all(|l| l.is_elided()) {
self.resolve_elided_lifetimes(&data.lifetimes);
} else {
for l in &data.lifetimes { self.visit_lifetime(l); }
}
// Figure out if this is a type/trait segment,
// which requires object lifetime defaults.
let parent_def_id = |this: &mut Self, def_id: DefId| {
let def_key = if def_id.is_local() {
this.hir_map.def_key(def_id)
} else {
this.sess.cstore.def_key(def_id)
};
DefId {
krate: def_id.krate,
index: def_key.parent.expect("missing parent")
}
};
let type_def_id = match def {
Def::AssociatedTy(def_id) if depth == 1 => {
Some(parent_def_id(self, def_id))
}
Def::Variant(def_id) if depth == 0 => {
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 depth == 0 => Some(def_id),
_ => None
};
let object_lifetime_defaults = type_def_id.map_or(vec![], |def_id| {
let in_body = {
let mut scope = self.scope;
loop {
match *scope {
Scope::Root => break false,
Scope::Body { .. } => break true,
Scope::Binder { s, .. } |
Scope::Elision { s, .. } |
Scope::ObjectLifetimeDefault { s, .. } => {
scope = s;
}
}
}
};
let map = &self.map;
let unsubst = if let Some(id) = self.hir_map.as_local_node_id(def_id) {
&map.object_lifetime_defaults[&id]
} else {
let cstore = &self.sess.cstore;
self.xcrate_object_lifetime_defaults.entry(def_id).or_insert_with(|| {
cstore.item_generics_object_lifetime_defaults(def_id)
})
};
unsubst.iter().map(|set| {
match *set {
Set1::Empty => {
if in_body {
None
} else {
Some(Region::Static)
}
}
Set1::One(r) => r.subst(&data.lifetimes, map),
Set1::Many => None
}
}).collect()
});
for (i, ty) in data.types.iter().enumerate() {
if let Some(&lt) = object_lifetime_defaults.get(i) {
let scope = Scope::ObjectLifetimeDefault {
lifetime: lt,
s: self.scope
};
self.with(scope, |_, this| this.visit_ty(ty));
} else {
self.visit_ty(ty);
}
}
for b in &data.bindings { self.visit_assoc_type_binding(b); }
}
fn visit_fn_like_elision(&mut self, inputs: &'tcx [P<hir::Ty>],
output: Option<&'tcx P<hir::Ty>>) {
let mut arg_elide = Elide::FreshLateAnon(Cell::new(0));
@ -962,7 +1224,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
if let hir::TyBareFn(_) = ty.node {
self.binder_depth += 1;
}
intravisit::walk_ty(self, ty);
if let hir::TyTraitObject(ref bounds, ref lifetime) = ty.node {
for bound in bounds {
self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
}
// Stay on the safe side and don't include the object
// lifetime default (which may not end up being used).
if !lifetime.is_elided() {
self.visit_lifetime(lifetime);
}
} else {
intravisit::walk_ty(self, ty);
}
if let hir::TyBareFn(_) = ty.node {
self.binder_depth -= 1;
}
@ -1045,6 +1319,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}
return;
}
Scope::ObjectLifetimeDefault { s, .. } => {
scope = s;
}
}
};
@ -1134,6 +1412,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}
}
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &hir::Lifetime) {
let mut late_depth = 0;
let mut scope = self.scope;
let lifetime = loop {
match *scope {
Scope::Binder { s, .. } => {
late_depth += 1;
scope = s;
}
Scope::Root |
Scope::Elision { .. } => break Region::Static,
Scope::Body { .. } |
Scope::ObjectLifetimeDefault { lifetime: None, .. } => return,
Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l
}
};
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
}
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) {
for i in 0..lifetimes.len() {
let lifetime_i = &lifetimes[i];
@ -1192,7 +1492,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
loop {
match *old_scope {
Scope::Body { s, .. } |
Scope::Elision { s, .. } => {
Scope::Elision { s, .. } |
Scope::ObjectLifetimeDefault { s, .. } => {
old_scope = s;
}

View file

@ -592,24 +592,6 @@ pub enum IntVarValue {
UintType(ast::UintTy),
}
/// Default region to use for the bound of objects that are
/// supplied as the value for this type parameter. This is derived
/// from `T:'a` annotations appearing in the type definition. If
/// this is `None`, then the default is inherited from the
/// surrounding context. See RFC #599 for details.
#[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
pub enum ObjectLifetimeDefault<'tcx> {
/// Require an explicit annotation. Occurs when multiple
/// `T:'a` constraints are found.
Ambiguous,
/// Use the base default, typically 'static, but in a fn body it is a fresh variable
BaseDefault,
/// Use the given region as the default.
Specific(&'tcx Region),
}
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub struct TypeParameterDef<'tcx> {
pub name: Name,
@ -617,7 +599,6 @@ pub struct TypeParameterDef<'tcx> {
pub index: u32,
pub default_def_id: DefId, // for use in error reporing about defaults
pub default: Option<Ty<'tcx>>,
pub object_lifetime_default: ObjectLifetimeDefault<'tcx>,
/// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
/// on generic parameter `T`, asserts data behind the parameter

View file

@ -726,36 +726,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
index: self.index,
default: self.default.fold_with(folder),
default_def_id: self.default_def_id,
object_lifetime_default: self.object_lifetime_default.fold_with(folder),
pure_wrt_drop: self.pure_wrt_drop,
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.default.visit_with(visitor) ||
self.object_lifetime_default.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
match *self {
ty::ObjectLifetimeDefault::Ambiguous =>
ty::ObjectLifetimeDefault::Ambiguous,
ty::ObjectLifetimeDefault::BaseDefault =>
ty::ObjectLifetimeDefault::BaseDefault,
ty::ObjectLifetimeDefault::Specific(r) =>
ty::ObjectLifetimeDefault::Specific(r.fold_with(folder)),
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
match *self {
ty::ObjectLifetimeDefault::Specific(r) => r.visit_with(visitor),
_ => false,
}
self.default.visit_with(visitor)
}
}

View file

@ -523,16 +523,6 @@ impl<'tcx> fmt::Debug for ty::ParameterEnvironment<'tcx> {
}
}
impl<'tcx> fmt::Debug for ty::ObjectLifetimeDefault<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ty::ObjectLifetimeDefault::Ambiguous => write!(f, "Ambiguous"),
ty::ObjectLifetimeDefault::BaseDefault => write!(f, "BaseDefault"),
ty::ObjectLifetimeDefault::Specific(ref r) => write!(f, "{:?}", r),
}
}
}
impl fmt::Display for ty::Region {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if verbose() {