Skip to content

Commit fba7575

Browse files
committed
Allow using an undeclared '_ as an anonymous input or inference region.
1 parent bf3c979 commit fba7575

File tree

7 files changed

+119
-58
lines changed

7 files changed

+119
-58
lines changed

src/librustc/middle/resolve_lifetime.rs

+41-35
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use util::nodemap::NodeMap;
3737
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
3838
pub enum DefRegion {
3939
DefStaticRegion,
40+
DefAnonRegion,
4041
DefEarlyBoundRegion(/* space */ subst::ParamSpace,
4142
/* index */ u32,
4243
/* lifetime decl */ ast::NodeId),
@@ -72,6 +73,9 @@ struct LifetimeContext<'a> {
7273
// I'm sorry.
7374
trait_ref_hack: bool,
7475

76+
// Pre-interned "'_", if #[feature(anon_lifetime)] is enabled.
77+
anon_lifetime_name: Option<ast::Name>,
78+
7579
// List of labels in the function/method currently under analysis.
7680
labels_in_fn: Vec<(ast::Ident, Span)>,
7781
}
@@ -95,12 +99,18 @@ static ROOT_SCOPE: ScopeChain<'static> = RootScope;
9599

96100
pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegionMap {
97101
let mut named_region_map = NodeMap();
102+
let anon_lifetime_name = if sess.features.borrow().anon_lifetime {
103+
Some(token::intern("'_"))
104+
} else {
105+
None
106+
};
98107
visit::walk_crate(&mut LifetimeContext {
99108
sess: sess,
100109
named_region_map: &mut named_region_map,
101110
scope: &ROOT_SCOPE,
102111
def_map: def_map,
103112
trait_ref_hack: false,
113+
anon_lifetime_name: anon_lifetime_name,
104114
labels_in_fn: vec![],
105115
}, krate);
106116
sess.abort_if_errors();
@@ -224,11 +234,28 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
224234
}
225235

226236
fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
227-
if lifetime_ref.name == special_idents::static_lifetime.name {
228-
self.insert_lifetime(lifetime_ref, DefStaticRegion);
229-
return;
237+
if lifetime_ref.id == ast::DUMMY_NODE_ID {
238+
self.sess.span_bug(lifetime_ref.span,
239+
"lifetime reference not renumbered, \
240+
probably a bug in syntax::fold");
230241
}
231-
self.resolve_lifetime_ref(lifetime_ref);
242+
243+
let def = if lifetime_ref.name == special_idents::static_lifetime.name {
244+
DefStaticRegion
245+
} else if let Ok(def) = self.resolve_lifetime_ref(lifetime_ref) {
246+
def
247+
} else if Some(lifetime_ref.name) == self.anon_lifetime_name {
248+
DefAnonRegion
249+
} else {
250+
self.unresolved_lifetime_ref(lifetime_ref);
251+
return;
252+
};
253+
254+
debug!("lifetime_ref={:?} id={:?} resolved to {:?}",
255+
lifetime_to_string(lifetime_ref),
256+
lifetime_ref.id,
257+
def);
258+
self.named_region_map.insert(lifetime_ref.id, def);
232259
}
233260

234261
fn visit_generics(&mut self, generics: &ast::Generics) {
@@ -478,6 +505,7 @@ impl<'a> LifetimeContext<'a> {
478505
scope: &wrap_scope,
479506
def_map: self.def_map,
480507
trait_ref_hack: self.trait_ref_hack,
508+
anon_lifetime_name: self.anon_lifetime_name,
481509
labels_in_fn: self.labels_in_fn.clone(),
482510
};
483511
debug!("entering scope {:?}", this.scope);
@@ -525,7 +553,8 @@ impl<'a> LifetimeContext<'a> {
525553
});
526554
}
527555

528-
fn resolve_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
556+
fn resolve_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime)
557+
-> Result<DefRegion, ()> {
529558
// Walk up the scope chain, tracking the number of fn scopes
530559
// that we pass through, until we find a lifetime with the
531560
// given name or we run out of scopes. If we encounter a code
@@ -548,9 +577,7 @@ impl<'a> LifetimeContext<'a> {
548577
match search_lifetimes(lifetimes, lifetime_ref) {
549578
Some((index, lifetime_def)) => {
550579
let decl_id = lifetime_def.id;
551-
let def = DefEarlyBoundRegion(space, index, decl_id);
552-
self.insert_lifetime(lifetime_ref, def);
553-
return;
580+
return Ok(DefEarlyBoundRegion(space, index, decl_id));
554581
}
555582
None => {
556583
scope = s;
@@ -563,9 +590,7 @@ impl<'a> LifetimeContext<'a> {
563590
Some((_index, lifetime_def)) => {
564591
let decl_id = lifetime_def.id;
565592
let debruijn = ty::DebruijnIndex::new(late_depth + 1);
566-
let def = DefLateBoundRegion(debruijn, decl_id);
567-
self.insert_lifetime(lifetime_ref, def);
568-
return;
593+
return Ok(DefLateBoundRegion(debruijn, decl_id));
569594
}
570595

571596
None => {
@@ -577,13 +602,13 @@ impl<'a> LifetimeContext<'a> {
577602
}
578603
}
579604

580-
self.unresolved_lifetime_ref(lifetime_ref);
605+
Err(())
581606
}
582607

583608
fn resolve_free_lifetime_ref(&mut self,
584609
scope_data: region::DestructionScopeData,
585610
lifetime_ref: &ast::Lifetime,
586-
scope: Scope) {
611+
scope: Scope) -> Result<DefRegion, ()> {
587612
debug!("resolve_free_lifetime_ref \
588613
scope_data: {:?} lifetime_ref: {:?} scope: {:?}",
589614
scope_data, lifetime_ref, scope);
@@ -621,13 +646,10 @@ impl<'a> LifetimeContext<'a> {
621646

622647
match search_result {
623648
Some((_depth, lifetime)) => {
624-
let def = DefFreeRegion(scope_data, lifetime.id);
625-
self.insert_lifetime(lifetime_ref, def);
649+
Ok(DefFreeRegion(scope_data, lifetime.id))
626650
}
627651

628-
None => {
629-
self.unresolved_lifetime_ref(lifetime_ref);
630-
}
652+
None => Err(())
631653
}
632654

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

669691
for bound in &lifetime_i.bounds {
670-
self.resolve_lifetime_ref(bound);
692+
self.visit_lifetime_ref(bound);
671693
}
672694
}
673695
}
@@ -713,22 +735,6 @@ impl<'a> LifetimeContext<'a> {
713735
}
714736
}
715737
}
716-
717-
fn insert_lifetime(&mut self,
718-
lifetime_ref: &ast::Lifetime,
719-
def: DefRegion) {
720-
if lifetime_ref.id == ast::DUMMY_NODE_ID {
721-
self.sess.span_bug(lifetime_ref.span,
722-
"lifetime reference not renumbered, \
723-
probably a bug in syntax::fold");
724-
}
725-
726-
debug!("lifetime_ref={:?} id={:?} resolved to {:?}",
727-
lifetime_to_string(lifetime_ref),
728-
lifetime_ref.id,
729-
def);
730-
self.named_region_map.insert(lifetime_ref.id, def);
731-
}
732738
}
733739

734740
fn search_lifetimes<'a>(lifetimes: &'a Vec<ast::LifetimeDef>,

src/librustc_typeck/astconv.rs

+19-14
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ pub trait AstConv<'tcx> {
145145
-> Ty<'tcx>;
146146
}
147147

148-
pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
148+
pub fn ast_region_to_region(tcx: &ty::ctxt,
149+
rscope: &RegionScope,
150+
lifetime: &ast::Lifetime)
149151
-> ty::Region {
150152
let r = match tcx.named_region_map.get(&lifetime.id) {
151153
None => {
@@ -157,6 +159,10 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
157159
ty::ReStatic
158160
}
159161

162+
Some(&rl::DefAnonRegion) => {
163+
opt_ast_region_to_region(tcx, rscope, lifetime.span, &None)
164+
}
165+
160166
Some(&rl::DefLateBoundRegion(debruijn, id)) => {
161167
ty::ReLateBound(debruijn, ty::BrNamed(ast_util::local_def(id), lifetime.name))
162168
}
@@ -239,24 +245,23 @@ fn report_elision_failure(
239245
}
240246
}
241247

242-
pub fn opt_ast_region_to_region<'tcx>(
243-
this: &AstConv<'tcx>,
244-
rscope: &RegionScope,
245-
default_span: Span,
246-
opt_lifetime: &Option<ast::Lifetime>) -> ty::Region
247-
{
248+
pub fn opt_ast_region_to_region(tcx: &ty::ctxt,
249+
rscope: &RegionScope,
250+
default_span: Span,
251+
opt_lifetime: &Option<ast::Lifetime>)
252+
-> ty::Region {
248253
let r = match *opt_lifetime {
249254
Some(ref lifetime) => {
250-
ast_region_to_region(this.tcx(), lifetime)
255+
ast_region_to_region(tcx, rscope, lifetime)
251256
}
252257

253258
None => match rscope.anon_regions(default_span, 1) {
254259
Ok(rs) => rs[0],
255260
Err(params) => {
256-
span_err!(this.tcx().sess, default_span, E0106,
261+
span_err!(tcx.sess, default_span, E0106,
257262
"missing lifetime specifier");
258263
if let Some(params) = params {
259-
report_elision_failure(this.tcx(), default_span, params);
264+
report_elision_failure(tcx, default_span, params);
260265
}
261266
ty::ReStatic
262267
}
@@ -486,7 +491,7 @@ fn convert_angle_bracketed_parameters<'tcx>(this: &AstConv<'tcx>,
486491
{
487492
let regions: Vec<_> =
488493
data.lifetimes.iter()
489-
.map(|l| ast_region_to_region(this.tcx(), l))
494+
.map(|l| ast_region_to_region(this.tcx(), rscope, l))
490495
.collect();
491496

492497
let region_substs =
@@ -1544,7 +1549,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
15441549
})
15451550
}
15461551
ast::TyRptr(ref region, ref mt) => {
1547-
let r = opt_ast_region_to_region(this, rscope, ast_ty.span, region);
1552+
let r = opt_ast_region_to_region(tcx, rscope, ast_ty.span, region);
15481553
debug!("TyRef r={:?}", r);
15491554
let rscope1 =
15501555
&ObjectLifetimeDefaultRscope::new(
@@ -1813,7 +1818,7 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
18131818
ast::SelfValue(_) => ty::ByValueExplicitSelfCategory,
18141819
ast::SelfRegion(ref lifetime, mutability, _) => {
18151820
let region =
1816-
opt_ast_region_to_region(this,
1821+
opt_ast_region_to_region(this.tcx(),
18171822
rscope,
18181823
self_info.explicit_self.span,
18191824
lifetime);
@@ -2061,7 +2066,7 @@ fn compute_object_lifetime_bound<'tcx>(
20612066
if !explicit_region_bounds.is_empty() {
20622067
// Explicitly specified region bound. Use that.
20632068
let r = explicit_region_bounds[0];
2064-
return ast_region_to_region(tcx, r);
2069+
return ast_region_to_region(tcx, &ExplicitRscope, r);
20652070
}
20662071

20672072
if let Err(ErrorReported) = this.ensure_super_predicates(span,principal_trait_ref.def_id()) {

src/librustc_typeck/check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4598,7 +4598,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
45984598
let region_count = region_defs.len(space);
45994599
assert_eq!(substs.regions().len(space), 0);
46004600
for (i, lifetime) in data.lifetimes.iter().enumerate() {
4601-
let r = ast_region_to_region(fcx.tcx(), lifetime);
4601+
let r = ast_region_to_region(fcx.tcx(), fcx, lifetime);
46024602
if i < region_count {
46034603
substs.mut_regions().push(space, r);
46044604
} else if i == region_count {

src/librustc_typeck/collect.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -1763,7 +1763,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
17631763
name: param.lifetime.name
17641764
});
17651765
for bound in &param.bounds {
1766-
let bound_region = ast_region_to_region(ccx.tcx, bound);
1766+
let bound_region = ast_region_to_region(tcx, &ExplicitRscope, bound);
17671767
let outlives = ty::Binder(ty::OutlivesPredicate(region, bound_region));
17681768
result.predicates.push(space, outlives.to_predicate());
17691769
}
@@ -1797,7 +1797,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
17971797
}
17981798

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

18081808
&ast::WherePredicate::RegionPredicate(ref region_pred) => {
1809-
let r1 = ast_region_to_region(tcx, &region_pred.lifetime);
1809+
let r1 = ast_region_to_region(tcx, &ExplicitRscope, &region_pred.lifetime);
18101810
for bound in &region_pred.bounds {
1811-
let r2 = ast_region_to_region(tcx, bound);
1811+
let r2 = ast_region_to_region(tcx, &ExplicitRscope, bound);
18121812
let pred = ty::Binder(ty::OutlivesPredicate(r1, r2));
18131813
result.predicates.push(space, ty::Predicate::RegionOutlives(pred))
18141814
}
@@ -1838,7 +1838,7 @@ fn ty_generics<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
18381838
let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics);
18391839
for (i, l) in early_lifetimes.iter().enumerate() {
18401840
let bounds = l.bounds.iter()
1841-
.map(|l| ast_region_to_region(tcx, l))
1841+
.map(|l| ast_region_to_region(tcx, &ExplicitRscope, l))
18421842
.collect();
18431843
let def = ty::RegionParameterDef { name: l.lifetime.name,
18441844
space: space,
@@ -1954,7 +1954,7 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
19541954
ast::TraitTyParamBound(..) =>
19551955
None,
19561956
ast::RegionTyParamBound(ref lifetime) =>
1957-
Some(astconv::ast_region_to_region(ccx.tcx, lifetime)),
1957+
Some(ast_region_to_region(ccx.tcx, &ExplicitRscope, lifetime)),
19581958
}
19591959
})
19601960
.collect()
@@ -2037,7 +2037,7 @@ fn predicates_from_bound<'tcx>(astconv: &AstConv<'tcx>,
20372037
.collect()
20382038
}
20392039
ast::RegionTyParamBound(ref lifetime) => {
2040-
let region = ast_region_to_region(astconv.tcx(), lifetime);
2040+
let region = ast_region_to_region(astconv.tcx(), &ExplicitRscope, lifetime);
20412041
let pred = ty::Binder(ty::OutlivesPredicate(param_ty, region));
20422042
vec![ty::Predicate::TypeOutlives(pred)]
20432043
}
@@ -2085,7 +2085,7 @@ fn conv_param_bounds<'a,'tcx>(astconv: &AstConv<'tcx>,
20852085

20862086
let region_bounds: Vec<ty::Region> =
20872087
region_bounds.into_iter()
2088-
.map(|r| ast_region_to_region(tcx, r))
2088+
.map(|r| ast_region_to_region(tcx, &ExplicitRscope, r))
20892089
.collect();
20902090

20912091
astconv::Bounds {

src/libsyntax/feature_gate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
155155

156156
// Allows the definition of `const fn` functions.
157157
("const_fn", "1.2.0", Active),
158+
159+
// Allows the usage of `'_` for function arguments and inference.
160+
("anon_lifetime", "1.2.0", Active),
158161
];
159162
// (changing above list without updating src/doc/reference.md makes @cmr sad)
160163

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

334338
impl Features {
@@ -350,6 +354,7 @@ impl Features {
350354
declared_stable_lang_features: Vec::new(),
351355
declared_lib_features: Vec::new(),
352356
const_fn: false,
357+
anon_lifetime: false,
353358
}
354359
}
355360
}
@@ -789,6 +794,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
789794
declared_stable_lang_features: accepted_features,
790795
declared_lib_features: unknown_features,
791796
const_fn: cx.has_feature("const_fn"),
797+
anon_lifetime: cx.has_feature("anon_lifetime"),
792798
}
793799
}
794800

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let _: &'_ () = &(); //~ ERROR use of undeclared lifetime name `'_`
13+
}

0 commit comments

Comments
 (0)