From fba757520bf73af8f60b3fc9ea0228bd1e49151e Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Fri, 26 Jun 2015 20:17:00 +0300 Subject: [PATCH] Allow using an undeclared '_ as an anonymous input or inference region. --- src/librustc/middle/resolve_lifetime.rs | 76 ++++++++++--------- src/librustc_typeck/astconv.rs | 33 ++++---- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/collect.rs | 16 ++-- src/libsyntax/feature_gate.rs | 6 ++ .../feature-gate-anon-lifetime.rs | 13 ++++ src/test/run-pass/regions-explicit-anon.rs | 31 ++++++++ 7 files changed, 119 insertions(+), 58 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-anon-lifetime.rs create mode 100644 src/test/run-pass/regions-explicit-anon.rs diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 9e5ad7b42f5c5..c8a718f02c65b 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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), @@ -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, + // List of labels in the function/method currently under analysis. labels_in_fn: Vec<(ast::Ident, Span)>, } @@ -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(); @@ -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) { @@ -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); @@ -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 { // 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 @@ -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; @@ -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 => { @@ -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 { debug!("resolve_free_lifetime_ref \ scope_data: {:?} lifetime_ref: {:?} scope: {:?}", scope_data, lifetime_ref, scope); @@ -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(()) } } @@ -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); } } } @@ -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, diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 00b7f42061405..ba2b69b80b8e7 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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 => { @@ -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)) } @@ -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) -> ty::Region -{ +pub fn opt_ast_region_to_region(tcx: &ty::ctxt, + rscope: &RegionScope, + default_span: Span, + opt_lifetime: &Option) + -> 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 } @@ -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 = @@ -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( @@ -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); @@ -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()) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 2db1598db4bee..4d346a76dddf6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7909908079fb3..2c72641831f47 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1763,7 +1763,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, name: param.lifetime.name }); for bound in ¶m.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()); } @@ -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)) } @@ -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, ®ion_pred.lifetime); + let r1 = ast_region_to_region(tcx, &ExplicitRscope, ®ion_pred.lifetime); for bound in ®ion_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)) } @@ -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, @@ -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() @@ -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)] } @@ -2085,7 +2085,7 @@ fn conv_param_bounds<'a,'tcx>(astconv: &AstConv<'tcx>, let region_bounds: Vec = region_bounds.into_iter() - .map(|r| ast_region_to_region(tcx, r)) + .map(|r| ast_region_to_region(tcx, &ExplicitRscope, r)) .collect(); astconv::Bounds { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 3d0cf9236c25c..ea10a3e7db13f 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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) @@ -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 { @@ -350,6 +354,7 @@ impl Features { declared_stable_lang_features: Vec::new(), declared_lib_features: Vec::new(), const_fn: false, + anon_lifetime: false, } } } @@ -789,6 +794,7 @@ fn check_crate_inner(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"), } } diff --git a/src/test/compile-fail/feature-gate-anon-lifetime.rs b/src/test/compile-fail/feature-gate-anon-lifetime.rs new file mode 100644 index 0000000000000..634dc02614fb8 --- /dev/null +++ b/src/test/compile-fail/feature-gate-anon-lifetime.rs @@ -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 or the MIT license +// , 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 `'_` +} diff --git a/src/test/run-pass/regions-explicit-anon.rs b/src/test/run-pass/regions-explicit-anon.rs new file mode 100644 index 0000000000000..2781973e5a7ef --- /dev/null +++ b/src/test/run-pass/regions-explicit-anon.rs @@ -0,0 +1,31 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pretty-expanded FIXME #23616 + +#![feature(anon_lifetime)] + +struct Foo<'a, 'b: 'a, T: 'b>(&'a Vec<&'b T>); + +fn from<'c, 'd, T>(foo: &'c Foo<'_, 'd, T>) -> Result<&'c T, &'d T> { + if 1 == 1 { + Ok(&foo.0[0]) + } else { + Err(&foo.0[1]) + } +} + +impl<'a, 'b, T> Foo<'a, 'b, T> { + fn to(&self) { + let _: Result<&'_ T, &'_ _> = from/*::<'_, 'b>*/(self); + } +} + +pub fn main() {}