Skip to content

Commit

Permalink
Allow using an undeclared '_ as an anonymous input or inference region.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Jul 1, 2015
1 parent bf3c979 commit fba7575
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 58 deletions.
76 changes: 41 additions & 35 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use util::nodemap::NodeMap;
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
pub enum DefRegion {
DefStaticRegion,
DefAnonRegion,
DefEarlyBoundRegion(/* space */ subst::ParamSpace,
/* index */ u32,
/* lifetime decl */ ast::NodeId),
Expand Down Expand Up @@ -72,6 +73,9 @@ struct LifetimeContext<'a> {
// I'm sorry.
trait_ref_hack: bool,

// Pre-interned "'_", if #[feature(anon_lifetime)] is enabled.
anon_lifetime_name: Option<ast::Name>,

// List of labels in the function/method currently under analysis.
labels_in_fn: Vec<(ast::Ident, Span)>,
}
Expand All @@ -95,12 +99,18 @@ static ROOT_SCOPE: ScopeChain<'static> = RootScope;

pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegionMap {
let mut named_region_map = NodeMap();
let anon_lifetime_name = if sess.features.borrow().anon_lifetime {
Some(token::intern("'_"))
} else {
None
};
visit::walk_crate(&mut LifetimeContext {
sess: sess,
named_region_map: &mut named_region_map,
scope: &ROOT_SCOPE,
def_map: def_map,
trait_ref_hack: false,
anon_lifetime_name: anon_lifetime_name,
labels_in_fn: vec![],
}, krate);
sess.abort_if_errors();
Expand Down Expand Up @@ -224,11 +234,28 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
}

fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
if lifetime_ref.name == special_idents::static_lifetime.name {
self.insert_lifetime(lifetime_ref, DefStaticRegion);
return;
if lifetime_ref.id == ast::DUMMY_NODE_ID {
self.sess.span_bug(lifetime_ref.span,
"lifetime reference not renumbered, \
probably a bug in syntax::fold");
}
self.resolve_lifetime_ref(lifetime_ref);

let def = if lifetime_ref.name == special_idents::static_lifetime.name {
DefStaticRegion
} else if let Ok(def) = self.resolve_lifetime_ref(lifetime_ref) {
def
} else if Some(lifetime_ref.name) == self.anon_lifetime_name {
DefAnonRegion
} else {
self.unresolved_lifetime_ref(lifetime_ref);
return;
};

debug!("lifetime_ref={:?} id={:?} resolved to {:?}",
lifetime_to_string(lifetime_ref),
lifetime_ref.id,
def);
self.named_region_map.insert(lifetime_ref.id, def);
}

fn visit_generics(&mut self, generics: &ast::Generics) {
Expand Down Expand Up @@ -478,6 +505,7 @@ impl<'a> LifetimeContext<'a> {
scope: &wrap_scope,
def_map: self.def_map,
trait_ref_hack: self.trait_ref_hack,
anon_lifetime_name: self.anon_lifetime_name,
labels_in_fn: self.labels_in_fn.clone(),
};
debug!("entering scope {:?}", this.scope);
Expand Down Expand Up @@ -525,7 +553,8 @@ impl<'a> LifetimeContext<'a> {
});
}

fn resolve_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
fn resolve_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime)
-> Result<DefRegion, ()> {
// Walk up the scope chain, tracking the number of fn scopes
// that we pass through, until we find a lifetime with the
// given name or we run out of scopes. If we encounter a code
Expand All @@ -548,9 +577,7 @@ impl<'a> LifetimeContext<'a> {
match search_lifetimes(lifetimes, lifetime_ref) {
Some((index, lifetime_def)) => {
let decl_id = lifetime_def.id;
let def = DefEarlyBoundRegion(space, index, decl_id);
self.insert_lifetime(lifetime_ref, def);
return;
return Ok(DefEarlyBoundRegion(space, index, decl_id));
}
None => {
scope = s;
Expand All @@ -563,9 +590,7 @@ impl<'a> LifetimeContext<'a> {
Some((_index, lifetime_def)) => {
let decl_id = lifetime_def.id;
let debruijn = ty::DebruijnIndex::new(late_depth + 1);
let def = DefLateBoundRegion(debruijn, decl_id);
self.insert_lifetime(lifetime_ref, def);
return;
return Ok(DefLateBoundRegion(debruijn, decl_id));
}

None => {
Expand All @@ -577,13 +602,13 @@ impl<'a> LifetimeContext<'a> {
}
}

self.unresolved_lifetime_ref(lifetime_ref);
Err(())
}

fn resolve_free_lifetime_ref(&mut self,
scope_data: region::DestructionScopeData,
lifetime_ref: &ast::Lifetime,
scope: Scope) {
scope: Scope) -> Result<DefRegion, ()> {
debug!("resolve_free_lifetime_ref \
scope_data: {:?} lifetime_ref: {:?} scope: {:?}",
scope_data, lifetime_ref, scope);
Expand Down Expand Up @@ -621,13 +646,10 @@ impl<'a> LifetimeContext<'a> {

match search_result {
Some((_depth, lifetime)) => {
let def = DefFreeRegion(scope_data, lifetime.id);
self.insert_lifetime(lifetime_ref, def);
Ok(DefFreeRegion(scope_data, lifetime.id))
}

None => {
self.unresolved_lifetime_ref(lifetime_ref);
}
None => Err(())
}

}
Expand Down Expand Up @@ -667,7 +689,7 @@ impl<'a> LifetimeContext<'a> {
self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime);

for bound in &lifetime_i.bounds {
self.resolve_lifetime_ref(bound);
self.visit_lifetime_ref(bound);
}
}
}
Expand Down Expand Up @@ -713,22 +735,6 @@ impl<'a> LifetimeContext<'a> {
}
}
}

fn insert_lifetime(&mut self,
lifetime_ref: &ast::Lifetime,
def: DefRegion) {
if lifetime_ref.id == ast::DUMMY_NODE_ID {
self.sess.span_bug(lifetime_ref.span,
"lifetime reference not renumbered, \
probably a bug in syntax::fold");
}

debug!("lifetime_ref={:?} id={:?} resolved to {:?}",
lifetime_to_string(lifetime_ref),
lifetime_ref.id,
def);
self.named_region_map.insert(lifetime_ref.id, def);
}
}

fn search_lifetimes<'a>(lifetimes: &'a Vec<ast::LifetimeDef>,
Expand Down
33 changes: 19 additions & 14 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ pub trait AstConv<'tcx> {
-> Ty<'tcx>;
}

pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
pub fn ast_region_to_region(tcx: &ty::ctxt,
rscope: &RegionScope,
lifetime: &ast::Lifetime)
-> ty::Region {
let r = match tcx.named_region_map.get(&lifetime.id) {
None => {
Expand All @@ -157,6 +159,10 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
ty::ReStatic
}

Some(&rl::DefAnonRegion) => {
opt_ast_region_to_region(tcx, rscope, lifetime.span, &None)
}

Some(&rl::DefLateBoundRegion(debruijn, id)) => {
ty::ReLateBound(debruijn, ty::BrNamed(ast_util::local_def(id), lifetime.name))
}
Expand Down Expand Up @@ -239,24 +245,23 @@ fn report_elision_failure(
}
}

pub fn opt_ast_region_to_region<'tcx>(
this: &AstConv<'tcx>,
rscope: &RegionScope,
default_span: Span,
opt_lifetime: &Option<ast::Lifetime>) -> ty::Region
{
pub fn opt_ast_region_to_region(tcx: &ty::ctxt,
rscope: &RegionScope,
default_span: Span,
opt_lifetime: &Option<ast::Lifetime>)
-> ty::Region {
let r = match *opt_lifetime {
Some(ref lifetime) => {
ast_region_to_region(this.tcx(), lifetime)
ast_region_to_region(tcx, rscope, lifetime)
}

None => match rscope.anon_regions(default_span, 1) {
Ok(rs) => rs[0],
Err(params) => {
span_err!(this.tcx().sess, default_span, E0106,
span_err!(tcx.sess, default_span, E0106,
"missing lifetime specifier");
if let Some(params) = params {
report_elision_failure(this.tcx(), default_span, params);
report_elision_failure(tcx, default_span, params);
}
ty::ReStatic
}
Expand Down Expand Up @@ -486,7 +491,7 @@ fn convert_angle_bracketed_parameters<'tcx>(this: &AstConv<'tcx>,
{
let regions: Vec<_> =
data.lifetimes.iter()
.map(|l| ast_region_to_region(this.tcx(), l))
.map(|l| ast_region_to_region(this.tcx(), rscope, l))
.collect();

let region_substs =
Expand Down Expand Up @@ -1544,7 +1549,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
})
}
ast::TyRptr(ref region, ref mt) => {
let r = opt_ast_region_to_region(this, rscope, ast_ty.span, region);
let r = opt_ast_region_to_region(tcx, rscope, ast_ty.span, region);
debug!("TyRef r={:?}", r);
let rscope1 =
&ObjectLifetimeDefaultRscope::new(
Expand Down Expand Up @@ -1813,7 +1818,7 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
ast::SelfValue(_) => ty::ByValueExplicitSelfCategory,
ast::SelfRegion(ref lifetime, mutability, _) => {
let region =
opt_ast_region_to_region(this,
opt_ast_region_to_region(this.tcx(),
rscope,
self_info.explicit_self.span,
lifetime);
Expand Down Expand Up @@ -2061,7 +2066,7 @@ fn compute_object_lifetime_bound<'tcx>(
if !explicit_region_bounds.is_empty() {
// Explicitly specified region bound. Use that.
let r = explicit_region_bounds[0];
return ast_region_to_region(tcx, r);
return ast_region_to_region(tcx, &ExplicitRscope, r);
}

if let Err(ErrorReported) = this.ensure_super_predicates(span,principal_trait_ref.def_id()) {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4598,7 +4598,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let region_count = region_defs.len(space);
assert_eq!(substs.regions().len(space), 0);
for (i, lifetime) in data.lifetimes.iter().enumerate() {
let r = ast_region_to_region(fcx.tcx(), lifetime);
let r = ast_region_to_region(fcx.tcx(), fcx, lifetime);
if i < region_count {
substs.mut_regions().push(space, r);
} else if i == region_count {
Expand Down
16 changes: 8 additions & 8 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1763,7 +1763,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
name: param.lifetime.name
});
for bound in &param.bounds {
let bound_region = ast_region_to_region(ccx.tcx, bound);
let bound_region = ast_region_to_region(tcx, &ExplicitRscope, bound);
let outlives = ty::Binder(ty::OutlivesPredicate(region, bound_region));
result.predicates.push(space, outlives.to_predicate());
}
Expand Down Expand Up @@ -1797,7 +1797,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
}

&ast::TyParamBound::RegionTyParamBound(ref lifetime) => {
let region = ast_region_to_region(tcx, lifetime);
let region = ast_region_to_region(tcx, &ExplicitRscope, lifetime);
let pred = ty::Binder(ty::OutlivesPredicate(ty, region));
result.predicates.push(space, ty::Predicate::TypeOutlives(pred))
}
Expand All @@ -1806,9 +1806,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
}

&ast::WherePredicate::RegionPredicate(ref region_pred) => {
let r1 = ast_region_to_region(tcx, &region_pred.lifetime);
let r1 = ast_region_to_region(tcx, &ExplicitRscope, &region_pred.lifetime);
for bound in &region_pred.bounds {
let r2 = ast_region_to_region(tcx, bound);
let r2 = ast_region_to_region(tcx, &ExplicitRscope, bound);
let pred = ty::Binder(ty::OutlivesPredicate(r1, r2));
result.predicates.push(space, ty::Predicate::RegionOutlives(pred))
}
Expand Down Expand Up @@ -1838,7 +1838,7 @@ fn ty_generics<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics);
for (i, l) in early_lifetimes.iter().enumerate() {
let bounds = l.bounds.iter()
.map(|l| ast_region_to_region(tcx, l))
.map(|l| ast_region_to_region(tcx, &ExplicitRscope, l))
.collect();
let def = ty::RegionParameterDef { name: l.lifetime.name,
space: space,
Expand Down Expand Up @@ -1954,7 +1954,7 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
ast::TraitTyParamBound(..) =>
None,
ast::RegionTyParamBound(ref lifetime) =>
Some(astconv::ast_region_to_region(ccx.tcx, lifetime)),
Some(ast_region_to_region(ccx.tcx, &ExplicitRscope, lifetime)),
}
})
.collect()
Expand Down Expand Up @@ -2037,7 +2037,7 @@ fn predicates_from_bound<'tcx>(astconv: &AstConv<'tcx>,
.collect()
}
ast::RegionTyParamBound(ref lifetime) => {
let region = ast_region_to_region(astconv.tcx(), lifetime);
let region = ast_region_to_region(astconv.tcx(), &ExplicitRscope, lifetime);
let pred = ty::Binder(ty::OutlivesPredicate(param_ty, region));
vec![ty::Predicate::TypeOutlives(pred)]
}
Expand Down Expand Up @@ -2085,7 +2085,7 @@ fn conv_param_bounds<'a,'tcx>(astconv: &AstConv<'tcx>,

let region_bounds: Vec<ty::Region> =
region_bounds.into_iter()
.map(|r| ast_region_to_region(tcx, r))
.map(|r| ast_region_to_region(tcx, &ExplicitRscope, r))
.collect();

astconv::Bounds {
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[

// Allows the definition of `const fn` functions.
("const_fn", "1.2.0", Active),

// Allows the usage of `'_` for function arguments and inference.
("anon_lifetime", "1.2.0", Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)

Expand Down Expand Up @@ -329,6 +332,7 @@ pub struct Features {
/// #![feature] attrs for non-language (library) features
pub declared_lib_features: Vec<(InternedString, Span)>,
pub const_fn: bool,
pub anon_lifetime: bool,
}

impl Features {
Expand All @@ -350,6 +354,7 @@ impl Features {
declared_stable_lang_features: Vec::new(),
declared_lib_features: Vec::new(),
const_fn: false,
anon_lifetime: false,
}
}
}
Expand Down Expand Up @@ -789,6 +794,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
declared_stable_lang_features: accepted_features,
declared_lib_features: unknown_features,
const_fn: cx.has_feature("const_fn"),
anon_lifetime: cx.has_feature("anon_lifetime"),
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/test/compile-fail/feature-gate-anon-lifetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2015 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.

fn main() {
let _: &'_ () = &(); //~ ERROR use of undeclared lifetime name `'_`
}
Loading

0 comments on commit fba7575

Please sign in to comment.