Skip to content

Allow using an undeclared '_ as an anonymous input or inference region. #26598

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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