1
Fork 0

add Issue32330 warning marker to bound regions

This indicates whether this `BoundRegion` will change from late to early
bound when issue 32330 is fixed. It also indicates the function on
which the lifetime is declared.
This commit is contained in:
Niko Matsakis 2016-04-21 05:10:10 -04:00
parent 11984340bf
commit 08034eb1a5
13 changed files with 315 additions and 172 deletions

View file

@ -132,6 +132,9 @@ pub trait Visitor<'v> : Sized {
fn visit_generics(&mut self, g: &'v Generics) { fn visit_generics(&mut self, g: &'v Generics) {
walk_generics(self, g) walk_generics(self, g)
} }
fn visit_where_predicate(&mut self, predicate: &'v WherePredicate) {
walk_where_predicate(self, predicate)
}
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, _: NodeId) { fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, _: NodeId) {
walk_fn(self, fk, fd, b, s) walk_fn(self, fk, fd, b, s)
} }
@ -529,29 +532,34 @@ pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics
walk_list!(visitor, visit_ty, &param.default); walk_list!(visitor, visit_ty, &param.default);
} }
walk_list!(visitor, visit_lifetime_def, &generics.lifetimes); walk_list!(visitor, visit_lifetime_def, &generics.lifetimes);
for predicate in &generics.where_clause.predicates { walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates);
match predicate { }
&WherePredicate::BoundPredicate(WhereBoundPredicate{ref bounded_ty,
ref bounds, pub fn walk_where_predicate<'v, V: Visitor<'v>>(
ref bound_lifetimes, visitor: &mut V,
..}) => { predicate: &'v WherePredicate)
visitor.visit_ty(bounded_ty); {
walk_list!(visitor, visit_ty_param_bound, bounds); match predicate {
walk_list!(visitor, visit_lifetime_def, bound_lifetimes); &WherePredicate::BoundPredicate(WhereBoundPredicate{ref bounded_ty,
} ref bounds,
&WherePredicate::RegionPredicate(WhereRegionPredicate{ref lifetime, ref bound_lifetimes,
ref bounds, ..}) => {
..}) => { visitor.visit_ty(bounded_ty);
visitor.visit_lifetime(lifetime); walk_list!(visitor, visit_ty_param_bound, bounds);
walk_list!(visitor, visit_lifetime, bounds); walk_list!(visitor, visit_lifetime_def, bound_lifetimes);
} }
&WherePredicate::EqPredicate(WhereEqPredicate{id, &WherePredicate::RegionPredicate(WhereRegionPredicate{ref lifetime,
ref path, ref bounds,
ref ty, ..}) => {
..}) => { visitor.visit_lifetime(lifetime);
visitor.visit_path(path, id); walk_list!(visitor, visit_lifetime, bounds);
visitor.visit_ty(ty); }
} &WherePredicate::EqPredicate(WhereEqPredicate{id,
ref path,
ref ty,
..}) => {
visitor.visit_path(path, id);
visitor.visit_ty(ty);
} }
} }
} }

View file

@ -1140,7 +1140,7 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
ty::BrAnon(i) => { ty::BrAnon(i) => {
anon_nums.insert(i); anon_nums.insert(i);
} }
ty::BrNamed(_, name) => { ty::BrNamed(_, name, _) => {
region_names.insert(name); region_names.insert(name);
} }
_ => () _ => ()
@ -1154,7 +1154,7 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
for sr in self.same_regions { for sr in self.same_regions {
for br in &sr.regions { for br in &sr.regions {
match *br { match *br {
ty::BrNamed(_, name) => { ty::BrNamed(_, name, _) => {
all_region_names.insert(name); all_region_names.insert(name);
} }
_ => () _ => ()

View file

@ -13,14 +13,12 @@
use super::{CombinedSnapshot, use super::{CombinedSnapshot,
InferCtxt, InferCtxt,
LateBoundRegion,
HigherRankedType, HigherRankedType,
SubregionOrigin, SubregionOrigin,
SkolemizationMap}; SkolemizationMap};
use super::combine::CombineFields; use super::combine::CombineFields;
use super::region_inference::{TaintDirections}; use super::region_inference::{TaintDirections};
use infer::error_reporting;
use ty::{self, TyCtxt, Binder, TypeFoldable}; use ty::{self, TyCtxt, Binder, TypeFoldable};
use ty::error::TypeError; use ty::error::TypeError;
use ty::relate::{Relate, RelateResult, TypeRelation}; use ty::relate::{Relate, RelateResult, TypeRelation};

View file

@ -22,6 +22,7 @@ use dep_graph::DepNode;
use hir::map::Map; use hir::map::Map;
use session::Session; use session::Session;
use hir::def::{Def, DefMap}; use hir::def::{Def, DefMap};
use hir::def_id::DefId;
use middle::region; use middle::region;
use ty::subst; use ty::subst;
use ty; use ty;
@ -32,6 +33,7 @@ use syntax::codemap::Span;
use syntax::parse::token::keywords; use syntax::parse::token::keywords;
use util::nodemap::NodeMap; use util::nodemap::NodeMap;
use rustc_data_structures::fnv::FnvHashSet;
use hir; use hir;
use hir::print::lifetime_to_string; use hir::print::lifetime_to_string;
use hir::intravisit::{self, Visitor, FnKind}; use hir::intravisit::{self, Visitor, FnKind};
@ -50,11 +52,21 @@ pub enum DefRegion {
// 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 type NamedRegionMap = NodeMap<DefRegion>; pub struct NamedRegionMap {
// maps from every use of a named (not anonymous) lifetime to a
// `DefRegion` describing how that region is bound
pub defs: NodeMap<DefRegion>,
struct LifetimeContext<'a> { // the set of lifetime def ids that are late-bound; late-bound ids
// are named regions appearing in fn arguments that do not appear
// in where-clauses
pub late_bound: NodeMap<ty::Issue32330>,
}
struct LifetimeContext<'a, 'tcx: 'a> {
sess: &'a Session, sess: &'a Session,
named_region_map: &'a mut NamedRegionMap, hir_map: &'a Map<'tcx>,
map: &'a mut NamedRegionMap,
scope: Scope<'a>, scope: Scope<'a>,
def_map: &'a DefMap, def_map: &'a DefMap,
// Deep breath. Our representation for poly trait refs contains a single // Deep breath. Our representation for poly trait refs contains a single
@ -101,21 +113,25 @@ pub fn krate(sess: &Session,
-> Result<NamedRegionMap, usize> { -> Result<NamedRegionMap, usize> {
let _task = hir_map.dep_graph.in_task(DepNode::ResolveLifetimes); let _task = hir_map.dep_graph.in_task(DepNode::ResolveLifetimes);
let krate = hir_map.krate(); let krate = hir_map.krate();
let mut named_region_map = NodeMap(); let mut map = NamedRegionMap {
defs: NodeMap(),
late_bound: NodeMap(),
};
sess.track_errors(|| { sess.track_errors(|| {
krate.visit_all_items(&mut LifetimeContext { krate.visit_all_items(&mut LifetimeContext {
sess: sess, sess: sess,
named_region_map: &mut named_region_map, hir_map: hir_map,
map: &mut map,
scope: &ROOT_SCOPE, scope: &ROOT_SCOPE,
def_map: def_map, def_map: def_map,
trait_ref_hack: false, trait_ref_hack: false,
labels_in_fn: vec![], labels_in_fn: vec![],
}); });
})?; })?;
Ok(named_region_map) Ok(map)
} }
impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { impl<'a, 'tcx, 'v> Visitor<'v> for LifetimeContext<'a, 'tcx> {
fn visit_item(&mut self, item: &hir::Item) { fn visit_item(&mut self, item: &hir::Item) {
assert!(self.labels_in_fn.is_empty()); assert!(self.labels_in_fn.is_empty());
@ -164,8 +180,12 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
// Items always introduce a new root scope // Items always introduce a new root scope
self.with(RootScope, |_, this| { self.with(RootScope, |_, this| {
match item.node { match item.node {
hir::ForeignItemFn(_, ref generics) => { hir::ForeignItemFn(ref decl, ref generics) => {
this.visit_early_late(subst::FnSpace, generics, |this| { this.visit_early_late(item.id,
subst::FnSpace,
decl,
generics,
|this| {
intravisit::walk_foreign_item(this, item); intravisit::walk_foreign_item(this, item);
}) })
} }
@ -179,24 +199,27 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
replace(&mut self.labels_in_fn, saved); replace(&mut self.labels_in_fn, saved);
} }
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl, fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v hir::FnDecl,
b: &'v hir::Block, s: Span, fn_id: ast::NodeId) { b: &'v hir::Block, s: Span, fn_id: ast::NodeId) {
match fk { match fk {
FnKind::ItemFn(_, generics, _, _, _, _, _) => { FnKind::ItemFn(_, generics, _, _, _, _, _) => {
self.visit_early_late(subst::FnSpace, generics, |this| { self.visit_early_late(fn_id, subst::FnSpace, decl, generics, |this| {
this.add_scope_and_walk_fn(fk, fd, b, s, fn_id) this.add_scope_and_walk_fn(fk, decl, b, s, fn_id)
}) })
} }
FnKind::Method(_, sig, _, _) => { FnKind::Method(_, sig, _, _) => {
self.visit_early_late(subst::FnSpace, &sig.generics, |this| { self.visit_early_late(
this.add_scope_and_walk_fn(fk, fd, b, s, fn_id) fn_id,
}) subst::FnSpace,
decl,
&sig.generics,
|this| this.add_scope_and_walk_fn(fk, decl, b, s, fn_id));
} }
FnKind::Closure(_) => { FnKind::Closure(_) => {
// Closures have their own set of labels, save labels just // Closures have their own set of labels, save labels just
// like for foreign items above. // like for foreign items above.
let saved = replace(&mut self.labels_in_fn, vec![]); let saved = replace(&mut self.labels_in_fn, vec![]);
let result = self.add_scope_and_walk_fn(fk, fd, b, s, fn_id); let result = self.add_scope_and_walk_fn(fk, decl, b, s, fn_id);
replace(&mut self.labels_in_fn, saved); replace(&mut self.labels_in_fn, saved);
result result
} }
@ -240,7 +263,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
if let hir::MethodTraitItem(ref sig, None) = trait_item.node { if let hir::MethodTraitItem(ref sig, None) = trait_item.node {
self.visit_early_late( self.visit_early_late(
subst::FnSpace, &sig.generics, trait_item.id, subst::FnSpace,
&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);
@ -380,8 +404,7 @@ 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<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) { fn extract_labels(ctxt: &mut LifetimeContext, b: &hir::Block) {
struct GatherLabels<'a> { struct GatherLabels<'a> {
sess: &'a Session, sess: &'a Session,
scope: Scope<'a>, scope: Scope<'a>,
@ -468,7 +491,7 @@ fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) {
} }
} }
impl<'a> LifetimeContext<'a> { impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
fn add_scope_and_walk_fn<'b>(&mut self, fn add_scope_and_walk_fn<'b>(&mut self,
fk: FnKind, fk: FnKind,
fd: &hir::FnDecl, fd: &hir::FnDecl,
@ -501,10 +524,11 @@ impl<'a> LifetimeContext<'a> {
fn with<F>(&mut self, wrap_scope: ScopeChain, f: F) where fn with<F>(&mut self, wrap_scope: ScopeChain, f: F) where
F: FnOnce(Scope, &mut LifetimeContext), F: FnOnce(Scope, &mut LifetimeContext),
{ {
let LifetimeContext {sess, ref mut named_region_map, ..} = *self; let LifetimeContext {sess, hir_map, ref mut map, ..} = *self;
let mut this = LifetimeContext { let mut this = LifetimeContext {
sess: sess, sess: sess,
named_region_map: *named_region_map, hir_map: hir_map,
map: *map,
scope: &wrap_scope, scope: &wrap_scope,
def_map: self.def_map, def_map: self.def_map,
trait_ref_hack: self.trait_ref_hack, trait_ref_hack: self.trait_ref_hack,
@ -534,20 +558,27 @@ impl<'a> LifetimeContext<'a> {
/// bound lifetimes are resolved by name and associated with a binder id (`binder_id`), so the /// bound lifetimes are resolved by name and associated with a binder id (`binder_id`), so the
/// 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,
early_space: subst::ParamSpace, early_space: subst::ParamSpace,
decl: &hir::FnDecl,
generics: &hir::Generics, generics: &hir::Generics,
walk: F) where walk: F) where
F: FnOnce(&mut LifetimeContext), F: FnOnce(&mut LifetimeContext),
{ {
let referenced_idents = early_bound_lifetime_names(generics); let fn_def_id = self.hir_map.local_def_id(fn_id);
insert_late_bound_lifetimes(self.map,
fn_def_id,
decl,
generics);
debug!("visit_early_late: referenced_idents={:?}", let (late, early): (Vec<_>, _) =
referenced_idents); generics.lifetimes
.iter()
.cloned()
.partition(|l| self.map.late_bound.contains_key(&l.lifetime.id));
let (early, late): (Vec<_>, _) = generics.lifetimes.iter().cloned().partition( let this = self;
|l| referenced_idents.iter().any(|&i| i == l.lifetime.name)); this.with(EarlyScope(early_space, &early, this.scope), move |old_scope, this| {
self.with(EarlyScope(early_space, &early, self.scope), move |old_scope, this| {
this.with(LateScope(&late, this.scope), move |_, this| { this.with(LateScope(&late, this.scope), move |_, this| {
this.check_lifetime_defs(old_scope, &generics.lifetimes); this.check_lifetime_defs(old_scope, &generics.lifetimes);
walk(this); walk(this);
@ -756,11 +787,12 @@ impl<'a> LifetimeContext<'a> {
probably a bug in syntax::fold"); probably a bug in syntax::fold");
} }
debug!("lifetime_ref={:?} id={:?} resolved to {:?}", debug!("lifetime_ref={:?} id={:?} resolved to {:?} span={:?}",
lifetime_to_string(lifetime_ref), lifetime_to_string(lifetime_ref),
lifetime_ref.id, lifetime_ref.id,
def); def,
self.named_region_map.insert(lifetime_ref.id, def); self.sess.codemap().span_to_string(lifetime_ref.span));
self.map.defs.insert(lifetime_ref.id, def);
} }
} }
@ -777,95 +809,132 @@ fn search_lifetimes<'a>(lifetimes: &'a [hir::LifetimeDef],
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
pub fn early_bound_lifetimes<'a>(generics: &'a hir::Generics) -> Vec<hir::LifetimeDef> { /// Detects late-bound lifetimes and inserts them into
let referenced_idents = early_bound_lifetime_names(generics); /// `map.late_bound`.
if referenced_idents.is_empty() { ///
return Vec::new(); /// A region declared on a fn is **late-bound** if:
/// - it is constrained by an argument type;
/// - it does not appear in a where-clause.
///
/// "Constrained" basically means that it appears in any type but
/// not amongst the inputs to a projection. In other words, `<&'a
/// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`.
fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
fn_def_id: DefId,
decl: &hir::FnDecl,
generics: &hir::Generics) {
debug!("insert_late_bound_lifetimes(decl={:?}, generics={:?})", decl, generics);
let mut constrained_by_input = ConstrainedCollector { regions: FnvHashSet() };
for arg in &decl.inputs {
constrained_by_input.visit_ty(&arg.ty);
} }
generics.lifetimes.iter() let mut appears_in_output = AllCollector { regions: FnvHashSet() };
.filter(|l| referenced_idents.iter().any(|&i| i == l.lifetime.name)) intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
.cloned()
.collect()
}
/// Given a set of generic declarations, returns a list of names containing all early bound debug!("insert_late_bound_lifetimes: constrained_by_input={:?}",
/// lifetime names for those generics. (In fact, this list may also contain other names.) constrained_by_input.regions);
fn early_bound_lifetime_names(generics: &hir::Generics) -> Vec<ast::Name> {
// Create two lists, dividing the lifetimes into early/late bound.
// Initially, all of them are considered late, but we will move
// things from late into early as we go if we find references to
// them.
let mut early_bound = Vec::new();
let mut late_bound = generics.lifetimes.iter()
.map(|l| l.lifetime.name)
.collect();
// Any lifetime that appears in a type bound is early. // Walk the lifetimes that appear in where clauses.
{ //
let mut collector = // Subtle point: because we disallow nested bindings, we can just
FreeLifetimeCollector { early_bound: &mut early_bound, // ignore binders here and scrape up all names we see.
late_bound: &mut late_bound }; let mut appears_in_where_clause = AllCollector { regions: FnvHashSet() };
for ty_param in generics.ty_params.iter() { for ty_param in generics.ty_params.iter() {
walk_list!(&mut collector, visit_ty_param_bound, &ty_param.bounds); walk_list!(&mut appears_in_where_clause,
} visit_ty_param_bound,
for predicate in &generics.where_clause.predicates { &ty_param.bounds);
match predicate {
&hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate{ref bounds,
ref bounded_ty,
..}) => {
collector.visit_ty(&bounded_ty);
walk_list!(&mut collector, visit_ty_param_bound, bounds);
}
&hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate{ref lifetime,
ref bounds,
..}) => {
collector.visit_lifetime(lifetime);
for bound in bounds {
collector.visit_lifetime(bound);
}
}
&hir::WherePredicate::EqPredicate(_) => bug!("unimplemented")
}
}
} }
walk_list!(&mut appears_in_where_clause,
// Any lifetime that either has a bound or is referenced by a visit_where_predicate,
// bound is early. &generics.where_clause.predicates);
for lifetime_def in &generics.lifetimes { for lifetime_def in &generics.lifetimes {
if !lifetime_def.bounds.is_empty() { if !lifetime_def.bounds.is_empty() {
shuffle(&mut early_bound, &mut late_bound, // `'a: 'b` means both `'a` and `'b` are referenced
lifetime_def.lifetime.name); appears_in_where_clause.visit_lifetime_def(lifetime_def);
for bound in &lifetime_def.bounds {
shuffle(&mut early_bound, &mut late_bound,
bound.name);
}
}
}
return early_bound;
struct FreeLifetimeCollector<'a> {
early_bound: &'a mut Vec<ast::Name>,
late_bound: &'a mut Vec<ast::Name>,
}
impl<'a, 'v> Visitor<'v> for FreeLifetimeCollector<'a> {
fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
shuffle(self.early_bound, self.late_bound,
lifetime_ref.name);
} }
} }
fn shuffle(early_bound: &mut Vec<ast::Name>, debug!("insert_late_bound_lifetimes: appears_in_where_clause={:?}",
late_bound: &mut Vec<ast::Name>, appears_in_where_clause.regions);
name: ast::Name) {
match late_bound.iter().position(|n| *n == name) { // Late bound regions are those that:
Some(index) => { // - appear in the inputs
late_bound.swap_remove(index); // - do not appear in the where-clauses
early_bound.push(name); for lifetime in &generics.lifetimes {
let name = lifetime.lifetime.name;
// appears in the where clauses? early-bound.
if appears_in_where_clause.regions.contains(&name) { continue; }
// does not appear in the inputs, but appears in the return
// type? eventually this will be early-bound, but for now we
// just mark it so we can issue warnings.
let constrained_by_input = constrained_by_input.regions.contains(&name);
let appears_in_output = appears_in_output.regions.contains(&name);
let will_change = !constrained_by_input && appears_in_output;
let issue_32330 = if will_change {
ty::Issue32330::WillChange {
fn_def_id: fn_def_id,
region_name: name,
} }
None => { } } else {
ty::Issue32330::WontChange
};
debug!("insert_late_bound_lifetimes: \
lifetime {:?} with id {:?} is late-bound ({:?}",
lifetime.lifetime.name, lifetime.lifetime.id, issue_32330);
let prev = map.late_bound.insert(lifetime.lifetime.id, issue_32330);
assert!(prev.is_none(), "visited lifetime {:?} twice", lifetime.lifetime.id);
}
return;
struct ConstrainedCollector {
regions: FnvHashSet<ast::Name>,
}
impl<'v> Visitor<'v> for ConstrainedCollector {
fn visit_ty(&mut self, ty: &'v hir::Ty) {
match ty.node {
hir::TyPath(Some(_), _) => {
// ignore lifetimes appearing in associated type
// projections, as they are not *constrained*
// (defined above)
}
hir::TyPath(None, ref path) => {
// consider only the lifetimes on the final
// segment; I am not sure it's even currently
// valid to have them elsewhere, but even if it
// is, those would be potentially inputs to
// projections
if let Some(last_segment) = path.segments.last() {
self.visit_path_segment(path.span, last_segment);
}
}
_ => {
intravisit::walk_ty(self, ty);
}
}
}
fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
self.regions.insert(lifetime_ref.name);
}
}
struct AllCollector {
regions: FnvHashSet<ast::Name>,
}
impl<'v> Visitor<'v> for AllCollector {
fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
self.regions.insert(lifetime_ref.name);
} }
} }
} }

View file

@ -60,6 +60,7 @@ pub use self::sty::{ClosureTy, InferTy, ParamTy, ProjectionTy, TraitTy};
pub use self::sty::{ClosureSubsts, TypeAndMut}; pub use self::sty::{ClosureSubsts, TypeAndMut};
pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef}; pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef};
pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
pub use self::sty::Issue32330;
pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid, SkolemizedRegionVid}; pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid, SkolemizedRegionVid};
pub use self::sty::BoundRegion::*; pub use self::sty::BoundRegion::*;
pub use self::sty::FnOutput::*; pub use self::sty::FnOutput::*;
@ -527,7 +528,7 @@ bitflags! {
// Present if the type belongs in a local type context. // Present if the type belongs in a local type context.
// Only set for TyInfer other than Fresh. // Only set for TyInfer other than Fresh.
const KEEP_IN_LOCAL_TCX = 1 << 10, const KEEP_IN_LOCAL_TCX = 1 << 11,
const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits |
TypeFlags::HAS_SELF.bits | TypeFlags::HAS_SELF.bits |
@ -740,7 +741,8 @@ impl RegionParameterDef {
}) })
} }
pub fn to_bound_region(&self) -> ty::BoundRegion { pub fn to_bound_region(&self) -> ty::BoundRegion {
ty::BoundRegion::BrNamed(self.def_id, self.name) // this is an early bound region, so unaffected by #32330
ty::BoundRegion::BrNamed(self.def_id, self.name, Issue32330::WontChange)
} }
} }
@ -2836,7 +2838,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
for def in generics.regions.as_slice() { for def in generics.regions.as_slice() {
let region = let region =
ReFree(FreeRegion { scope: free_id_outlive, ReFree(FreeRegion { scope: free_id_outlive,
bound_region: BrNamed(def.def_id, def.name) }); bound_region: def.to_bound_region() });
debug!("push_region_params {:?}", region); debug!("push_region_params {:?}", region);
regions.push(def.space, region); regions.push(def.space, region);
} }

View file

@ -58,7 +58,7 @@ pub enum BoundRegion {
/// ///
/// The def-id is needed to distinguish free regions in /// The def-id is needed to distinguish free regions in
/// the event of shadowing. /// the event of shadowing.
BrNamed(DefId, Name), BrNamed(DefId, Name, Issue32330),
/// Fresh bound identifiers created during GLB computations. /// Fresh bound identifiers created during GLB computations.
BrFresh(u32), BrFresh(u32),
@ -68,6 +68,25 @@ pub enum BoundRegion {
BrEnv BrEnv
} }
/// True if this late-bound region is unconstrained, and hence will
/// become early-bound once #32330 is fixed.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash,
RustcEncodable, RustcDecodable)]
pub enum Issue32330 {
WontChange,
/// this region will change from late-bound to early-bound once
/// #32330 is fixed.
WillChange {
/// fn where is region declared
fn_def_id: DefId,
/// name of region; duplicates the info in BrNamed but convenient
/// to have it here, and this code is only temporary
region_name: ast::Name,
}
}
// NB: If you change this, you'll probably want to change the corresponding // NB: If you change this, you'll probably want to change the corresponding
// AST structure in libsyntax/ast.rs as well. // AST structure in libsyntax/ast.rs as well.
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]

View file

@ -261,7 +261,7 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
let new_value = tcx.replace_late_bound_regions(&value, |br| { let new_value = tcx.replace_late_bound_regions(&value, |br| {
let _ = start_or_continue(f, "for<", ", "); let _ = start_or_continue(f, "for<", ", ");
ty::ReLateBound(ty::DebruijnIndex::new(1), match br { ty::ReLateBound(ty::DebruijnIndex::new(1), match br {
ty::BrNamed(_, name) => { ty::BrNamed(_, name, _) => {
let _ = write!(f, "{}", name); let _ = write!(f, "{}", name);
br br
} }
@ -270,7 +270,9 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
ty::BrEnv => { ty::BrEnv => {
let name = token::intern("'r"); let name = token::intern("'r");
let _ = write!(f, "{}", name); let _ = write!(f, "{}", name);
ty::BrNamed(tcx.map.local_def_id(CRATE_NODE_ID), name) ty::BrNamed(tcx.map.local_def_id(CRATE_NODE_ID),
name,
ty::Issue32330::WontChange)
} }
}) })
}).0; }).0;
@ -485,7 +487,7 @@ impl fmt::Display for ty::BoundRegion {
} }
match *self { match *self {
BrNamed(_, name) => write!(f, "{}", name), BrNamed(_, name, _) => write!(f, "{}", name),
BrAnon(_) | BrFresh(_) | BrEnv => Ok(()) BrAnon(_) | BrFresh(_) | BrEnv => Ok(())
} }
} }
@ -496,8 +498,9 @@ impl fmt::Debug for ty::BoundRegion {
match *self { match *self {
BrAnon(n) => write!(f, "BrAnon({:?})", n), BrAnon(n) => write!(f, "BrAnon({:?})", n),
BrFresh(n) => write!(f, "BrFresh({:?})", n), BrFresh(n) => write!(f, "BrFresh({:?})", n),
BrNamed(did, name) => { BrNamed(did, name, issue32330) => {
write!(f, "BrNamed({:?}:{:?}, {:?})", did.krate, did.index, name) write!(f, "BrNamed({:?}:{:?}, {:?}, {:?})",
did.krate, did.index, name, issue32330)
} }
BrEnv => "BrEnv".fmt(f), BrEnv => "BrEnv".fmt(f),
} }

View file

@ -158,8 +158,21 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
} }
'[' => { '[' => {
let def = self.parse_def(); let def = self.parse_def();
let name = token::intern(&self.parse_str(']')); let name = token::intern(&self.parse_str('|'));
ty::BrNamed(def, name) let issue32330 = match self.next() {
'n' => {
assert_eq!(self.next(), ']');
ty::Issue32330::WontChange
}
'y' => {
ty::Issue32330::WillChange {
fn_def_id: self.parse_def(),
region_name: token::intern(&self.parse_str(']')),
}
}
c => panic!("expected n or y not {}", c)
};
ty::BrNamed(def, name, issue32330)
} }
'f' => { 'f' => {
let id = self.parse_u32(); let id = self.parse_u32();

View file

@ -308,10 +308,17 @@ fn enc_bound_region(w: &mut Cursor<Vec<u8>>, cx: &ctxt, br: ty::BoundRegion) {
ty::BrAnon(idx) => { ty::BrAnon(idx) => {
write!(w, "a{}|", idx); write!(w, "a{}|", idx);
} }
ty::BrNamed(d, name) => { ty::BrNamed(d, name, issue32330) => {
write!(w, "[{}|{}]", write!(w, "[{}|{}|",
(cx.ds)(cx.tcx, d), (cx.ds)(cx.tcx, d),
name); name);
match issue32330 {
ty::Issue32330::WontChange =>
write!(w, "n]"),
ty::Issue32330::WillChange { fn_def_id, region_name } =>
write!(w, "y{}|{}]", (cx.ds)(cx.tcx, fn_def_id), region_name),
};
} }
ty::BrFresh(id) => { ty::BrFresh(id) => {
write!(w, "f{}|", id); write!(w, "f{}|", id);

View file

@ -170,7 +170,7 @@ type TraitAndProjections<'tcx> = (ty::PolyTraitRef<'tcx>, Vec<ty::PolyProjection
pub fn ast_region_to_region(tcx: TyCtxt, lifetime: &hir::Lifetime) pub fn ast_region_to_region(tcx: TyCtxt, lifetime: &hir::Lifetime)
-> ty::Region { -> ty::Region {
let r = match tcx.named_region_map.get(&lifetime.id) { let r = match tcx.named_region_map.defs.get(&lifetime.id) {
None => { None => {
// should have been recorded by the `resolve_lifetime` pass // should have been recorded by the `resolve_lifetime` pass
span_bug!(lifetime.span, "unresolved lifetime"); span_bug!(lifetime.span, "unresolved lifetime");
@ -181,7 +181,20 @@ pub fn ast_region_to_region(tcx: TyCtxt, lifetime: &hir::Lifetime)
} }
Some(&rl::DefLateBoundRegion(debruijn, id)) => { Some(&rl::DefLateBoundRegion(debruijn, id)) => {
ty::ReLateBound(debruijn, ty::BrNamed(tcx.map.local_def_id(id), lifetime.name)) // If this region is declared on a function, it will have
// an entry in `late_bound`, but if it comes from
// `for<'a>` in some type or something, it won't
// necessarily have one. In that case though, we won't be
// changed from late to early bound, so we can just
// substitute false.
let issue_32330 = tcx.named_region_map
.late_bound
.get(&id)
.cloned()
.unwrap_or(ty::Issue32330::WontChange);
ty::ReLateBound(debruijn, ty::BrNamed(tcx.map.local_def_id(id),
lifetime.name,
issue_32330))
} }
Some(&rl::DefEarlyBoundRegion(space, index, _)) => { Some(&rl::DefEarlyBoundRegion(space, index, _)) => {
@ -193,11 +206,21 @@ pub fn ast_region_to_region(tcx: TyCtxt, lifetime: &hir::Lifetime)
} }
Some(&rl::DefFreeRegion(scope, id)) => { Some(&rl::DefFreeRegion(scope, id)) => {
// As in DefLateBoundRegion above, could be missing for some late-bound
// regions, but also for early-bound regions.
let issue_32330 = tcx.named_region_map
.late_bound
.get(&id)
.cloned()
.unwrap_or(ty::Issue32330::WontChange);
ty::ReFree(ty::FreeRegion { ty::ReFree(ty::FreeRegion {
scope: scope.to_code_extent(&tcx.region_maps), scope: scope.to_code_extent(&tcx.region_maps),
bound_region: ty::BrNamed(tcx.map.local_def_id(id), bound_region: ty::BrNamed(tcx.map.local_def_id(id),
lifetime.name) lifetime.name,
}) issue_32330)
})
// (*) -- not late-bound, won't change
} }
}; };
@ -911,7 +934,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
debug!("late_bound_in_ty = {:?}", late_bound_in_ty); debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) { for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
let br_name = match *br { let br_name = match *br {
ty::BrNamed(_, name) => name, ty::BrNamed(_, name, _) => name,
_ => { _ => {
span_bug!( span_bug!(
binding.span, binding.span,
@ -1675,7 +1698,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output); let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
for br in late_bound_in_ret.difference(&late_bound_in_args) { for br in late_bound_in_ret.difference(&late_bound_in_args) {
let br_name = match *br { let br_name = match *br {
ty::BrNamed(_, name) => name, ty::BrNamed(_, name, _) => name,
_ => { _ => {
span_bug!( span_bug!(
bf.decl.output.span(), bf.decl.output.span(),

View file

@ -64,7 +64,6 @@ use hir::def::Def;
use hir::def_id::DefId; use hir::def_id::DefId;
use constrained_type_params as ctp; use constrained_type_params as ctp;
use middle::lang_items::SizedTraitLangItem; use middle::lang_items::SizedTraitLangItem;
use middle::resolve_lifetime;
use middle::const_val::ConstVal; use middle::const_val::ConstVal;
use rustc_const_eval::EvalHint::UncheckedExprHint; use rustc_const_eval::EvalHint::UncheckedExprHint;
use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr}; use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr};
@ -1745,14 +1744,16 @@ fn add_unsized_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>,
/// the lifetimes that are declared. For fns or methods, we have to /// the lifetimes that are declared. For fns or methods, we have to
/// screen out those that do not appear in any where-clauses etc using /// screen out those that do not appear in any where-clauses etc using
/// `resolve_lifetime::early_bound_lifetimes`. /// `resolve_lifetime::early_bound_lifetimes`.
fn early_bound_lifetimes_from_generics(space: ParamSpace, fn early_bound_lifetimes_from_generics<'a, 'tcx, 'hir>(
ast_generics: &hir::Generics) ccx: &CrateCtxt<'a, 'tcx>,
-> Vec<hir::LifetimeDef> ast_generics: &'hir hir::Generics)
-> Vec<&'hir hir::LifetimeDef>
{ {
match space { ast_generics
SelfSpace | TypeSpace => ast_generics.lifetimes.to_vec(), .lifetimes
FnSpace => resolve_lifetime::early_bound_lifetimes(ast_generics), .iter()
} .filter(|l| !ccx.tcx.named_region_map.late_bound.contains_key(&l.lifetime.id))
.collect()
} }
fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
@ -1781,7 +1782,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
// Collect the region predicates that were declared inline as // Collect the region predicates that were declared inline as
// well. In the case of parameters declared on a fn or method, we // well. In the case of parameters declared on a fn or method, we
// have to be careful to only iterate over early-bound regions. // have to be careful to only iterate over early-bound regions.
let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics); let early_lifetimes = early_bound_lifetimes_from_generics(ccx, ast_generics);
for (index, param) in early_lifetimes.iter().enumerate() { for (index, param) in early_lifetimes.iter().enumerate() {
let index = index as u32; let index = index as u32;
let region = let region =
@ -1864,7 +1865,7 @@ fn ty_generics<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
let tcx = ccx.tcx; let tcx = ccx.tcx;
let mut result = base_generics.clone(); let mut result = base_generics.clone();
let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics); let early_lifetimes = early_bound_lifetimes_from_generics(ccx, ast_generics);
for (i, l) in early_lifetimes.iter().enumerate() { for (i, l) in early_lifetimes.iter().enumerate() {
let bounds = l.bounds.iter() let bounds = l.bounds.iter()
.map(|l| ast_region_to_region(tcx, l)) .map(|l| ast_region_to_region(tcx, l))

View file

@ -144,7 +144,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId { fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
let tcx = self.terms_cx.tcx; let tcx = self.terms_cx.tcx;
assert!(is_lifetime(&tcx.map, param_id)); assert!(is_lifetime(&tcx.map, param_id));
match tcx.named_region_map.get(&param_id) { match tcx.named_region_map.defs.get(&param_id) {
Some(&rl::DefEarlyBoundRegion(_, _, lifetime_decl_id)) Some(&rl::DefEarlyBoundRegion(_, _, lifetime_decl_id))
=> lifetime_decl_id, => lifetime_decl_id,
Some(_) => bug!("should not encounter non early-bound cases"), Some(_) => bug!("should not encounter non early-bound cases"),

View file

@ -819,7 +819,7 @@ impl Clean<Option<Lifetime>> for ty::Region {
fn clean(&self, cx: &DocContext) -> Option<Lifetime> { fn clean(&self, cx: &DocContext) -> Option<Lifetime> {
match *self { match *self {
ty::ReStatic => Some(Lifetime::statik()), ty::ReStatic => Some(Lifetime::statik()),
ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(name.to_string())), ty::ReLateBound(_, ty::BrNamed(_, name, _)) => Some(Lifetime(name.to_string())),
ty::ReEarlyBound(ref data) => Some(Lifetime(data.name.clean(cx))), ty::ReEarlyBound(ref data) => Some(Lifetime(data.name.clean(cx))),
ty::ReLateBound(..) | ty::ReLateBound(..) |