Auto merge of #44691 - cramertj:underscore-lifetimes, r=nikomatsakis

Implement underscore lifetimes

Part of https://github.com/rust-lang/rust/issues/44524
This commit is contained in:
bors 2017-09-22 14:05:16 +00:00
commit 3eb19bf9b1
21 changed files with 259 additions and 62 deletions

View File

@ -2086,4 +2086,5 @@ register_diagnostics! {
E0566, // conflicting representation hints
E0623, // lifetime mismatch where both parameters are anonymous regions
E0628, // generators cannot have explicit arguments
E0637, // "'_" is not a valid lifetime bound
}

View File

@ -422,7 +422,12 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) {
pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) {
visitor.visit_id(lifetime.id);
visitor.visit_name(lifetime.span, lifetime.name);
match lifetime.name {
LifetimeName::Name(name) => {
visitor.visit_name(lifetime.span, name);
}
LifetimeName::Static | LifetimeName::Implicit | LifetimeName::Underscore => {}
}
}
pub fn walk_lifetime_def<'v, V: Visitor<'v>>(visitor: &mut V, lifetime_def: &'v LifetimeDef) {

View File

@ -1121,7 +1121,11 @@ impl<'a> LoweringContext<'a> {
fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
hir::Lifetime {
id: self.lower_node_id(l.id).node_id,
name: self.lower_ident(l.ident),
name: match self.lower_ident(l.ident) {
x if x == "'_" => hir::LifetimeName::Underscore,
x if x == "'static" => hir::LifetimeName::Static,
name => hir::LifetimeName::Name(name),
},
span: l.span,
}
}
@ -3005,7 +3009,7 @@ impl<'a> LoweringContext<'a> {
hir::Lifetime {
id: self.next_id().node_id,
span,
name: keywords::Invalid.name()
name: hir::LifetimeName::Implicit,
}
}
}

View File

@ -805,7 +805,7 @@ impl<'hir> Map<'hir> {
NodeTraitItem(ti) => ti.name,
NodeVariant(v) => v.node.name,
NodeField(f) => f.name,
NodeLifetime(lt) => lt.name,
NodeLifetime(lt) => lt.name.name(),
NodeTyParam(tp) => tp.name,
NodeBinding(&Pat { node: PatKind::Binding(_,_,l,_), .. }) => l.node,
NodeStructCtor(_) => self.name(self.get_parent(id)),

View File

@ -145,7 +145,27 @@ pub struct Lifetime {
/// HIR lowering inserts these placeholders in type paths that
/// refer to type definitions needing lifetime parameters,
/// `&T` and `&mut T`, and trait objects without `... + 'a`.
pub name: Name,
pub name: LifetimeName,
}
#[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
pub enum LifetimeName {
Implicit,
Underscore,
Static,
Name(Name),
}
impl LifetimeName {
pub fn name(&self) -> Name {
use self::LifetimeName::*;
match *self {
Implicit => keywords::Invalid.name(),
Underscore => Symbol::intern("'_"),
Static => keywords::StaticLifetime.name(),
Name(name) => name,
}
}
}
impl fmt::Debug for Lifetime {
@ -159,11 +179,15 @@ impl fmt::Debug for Lifetime {
impl Lifetime {
pub fn is_elided(&self) -> bool {
self.name == keywords::Invalid.name()
use self::LifetimeName::*;
match self.name {
Implicit | Underscore => true,
Static | Name(_) => false,
}
}
pub fn is_static(&self) -> bool {
self.name == "'static"
self.name == LifetimeName::Static
}
}

View File

@ -1975,7 +1975,7 @@ impl<'a> State<'a> {
}
pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) -> io::Result<()> {
self.print_name(lifetime.name)
self.print_name(lifetime.name.name())
}
pub fn print_lifetime_def(&mut self, lifetime: &hir::LifetimeDef) -> io::Result<()> {

View File

@ -123,6 +123,13 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::ImplItemId {
}
}
impl_stable_hash_for!(enum hir::LifetimeName {
Implicit,
Underscore,
Static,
Name(name)
});
impl_stable_hash_for!(struct hir::Lifetime {
id,
span,

View File

@ -46,14 +46,16 @@ pub enum Region {
}
impl Region {
fn early(hir_map: &Map, index: &mut u32, def: &hir::LifetimeDef) -> (ast::Name, Region) {
fn early(hir_map: &Map, index: &mut u32, def: &hir::LifetimeDef)
-> (hir::LifetimeName, Region)
{
let i = *index;
*index += 1;
let def_id = hir_map.local_def_id(def.lifetime.id);
(def.lifetime.name, Region::EarlyBound(i, def_id))
}
fn late(hir_map: &Map, def: &hir::LifetimeDef) -> (ast::Name, Region) {
fn late(hir_map: &Map, def: &hir::LifetimeDef) -> (hir::LifetimeName, Region) {
let depth = ty::DebruijnIndex::new(1);
let def_id = hir_map.local_def_id(def.lifetime.id);
(def.lifetime.name, Region::LateBound(depth, def_id))
@ -198,7 +200,7 @@ enum Scope<'a> {
/// it should be shifted by the number of `Binder`s in between the
/// declaration `Binder` and the location it's referenced from.
Binder {
lifetimes: FxHashMap<ast::Name, Region>,
lifetimes: FxHashMap<hir::LifetimeName, Region>,
s: ScopeRef<'a>
},
@ -654,7 +656,7 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
Scope::Binder { ref lifetimes, s } => {
// FIXME (#24278): non-hygienic comparison
if let Some(def) = lifetimes.get(&label) {
if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) {
let node_id = hir_map.as_local_node_id(def.id().unwrap())
.unwrap();
@ -692,7 +694,7 @@ fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map)
Set1::Empty => "BaseDefault".to_string(),
Set1::One(Region::Static) => "'static".to_string(),
Set1::One(Region::EarlyBound(i, _)) => {
generics.lifetimes[i as usize].lifetime.name.to_string()
generics.lifetimes[i as usize].lifetime.name.name().to_string()
}
Set1::One(_) => bug!(),
Set1::Many => "Ambiguous".to_string(),
@ -714,7 +716,7 @@ fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map)
/// for each type parameter.
fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics)
-> Vec<ObjectLifetimeDefault> {
fn add_bounds(set: &mut Set1<ast::Name>, bounds: &[hir::TyParamBound]) {
fn add_bounds(set: &mut Set1<hir::LifetimeName>, bounds: &[hir::TyParamBound]) {
for bound in bounds {
if let hir::RegionTyParamBound(ref lifetime) = *bound {
set.insert(lifetime.name);
@ -754,7 +756,7 @@ fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics)
match set {
Set1::Empty => Set1::Empty,
Set1::One(name) => {
if name == "'static" {
if name == hir::LifetimeName::Static {
Set1::One(Region::Static)
} else {
generics.lifetimes.iter().enumerate().find(|&(_, def)| {
@ -922,7 +924,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
self.insert_lifetime(lifetime_ref, def);
} else {
struct_span_err!(self.sess, lifetime_ref.span, E0261,
"use of undeclared lifetime name `{}`", lifetime_ref.name)
"use of undeclared lifetime name `{}`", lifetime_ref.name.name())
.span_label(lifetime_ref.span, "undeclared lifetime")
.emit();
}
@ -1422,13 +1424,17 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
let lifetime_i = &lifetimes[i];
for lifetime in lifetimes {
if lifetime.lifetime.is_static() {
let lifetime = lifetime.lifetime;
let mut err = struct_span_err!(self.sess, lifetime.span, E0262,
"invalid lifetime parameter name: `{}`", lifetime.name);
err.span_label(lifetime.span,
format!("{} is a reserved lifetime name", lifetime.name));
err.emit();
match lifetime.lifetime.name {
hir::LifetimeName::Static | hir::LifetimeName::Underscore => {
let lifetime = lifetime.lifetime;
let name = lifetime.name.name();
let mut err = struct_span_err!(self.sess, lifetime.span, E0262,
"invalid lifetime parameter name: `{}`", name);
err.span_label(lifetime.span,
format!("{} is a reserved lifetime name", name));
err.emit();
}
hir::LifetimeName::Implicit | hir::LifetimeName::Name(_) => {}
}
}
@ -1439,7 +1445,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
if lifetime_i.lifetime.name == lifetime_j.lifetime.name {
struct_span_err!(self.sess, lifetime_j.lifetime.span, E0263,
"lifetime name `{}` declared twice in the same scope",
lifetime_j.lifetime.name)
lifetime_j.lifetime.name.name())
.span_label(lifetime_j.lifetime.span,
"declared twice")
.span_label(lifetime_i.lifetime.span,
@ -1452,15 +1458,27 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime);
for bound in &lifetime_i.bounds {
if !bound.is_static() {
self.resolve_lifetime_ref(bound);
} else {
self.insert_lifetime(bound, Region::Static);
self.sess.struct_span_warn(lifetime_i.lifetime.span.to(bound.span),
&format!("unnecessary lifetime parameter `{}`", lifetime_i.lifetime.name))
.help(&format!("you can use the `'static` lifetime directly, in place \
of `{}`", lifetime_i.lifetime.name))
.emit();
match bound.name {
hir::LifetimeName::Underscore => {
let mut err = struct_span_err!(self.sess, bound.span, E0637,
"invalid lifetime bound name: `'_`");
err.span_label(bound.span, "`'_` is a reserved lifetime name");
err.emit();
}
hir::LifetimeName::Static => {
self.insert_lifetime(bound, Region::Static);
self.sess.struct_span_warn(lifetime_i.lifetime.span.to(bound.span),
&format!("unnecessary lifetime parameter `{}`",
lifetime_i.lifetime.name.name()))
.help(&format!(
"you can use the `'static` lifetime directly, in place \
of `{}`", lifetime_i.lifetime.name.name()))
.emit();
}
hir::LifetimeName::Implicit |
hir::LifetimeName::Name(_) => {
self.resolve_lifetime_ref(bound);
}
}
}
}
@ -1472,9 +1490,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
{
for &(label, label_span) in &self.labels_in_fn {
// FIXME (#24278): non-hygienic comparison
if lifetime.name == label {
if lifetime.name.name() == label {
signal_shadowing_problem(self.sess,
lifetime.name,
label,
original_label(label_span),
shadower_lifetime(&lifetime));
return;
@ -1501,7 +1519,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
signal_shadowing_problem(
self.sess,
lifetime.name,
lifetime.name.name(),
original_lifetime(self.hir_map.span(node_id)),
shadower_lifetime(&lifetime));
return;
@ -1617,7 +1635,7 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
return;
struct ConstrainedCollector {
regions: FxHashSet<ast::Name>,
regions: FxHashSet<hir::LifetimeName>,
}
impl<'v> Visitor<'v> for ConstrainedCollector {
@ -1657,7 +1675,7 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
}
struct AllCollector {
regions: FxHashSet<ast::Name>,
regions: FxHashSet<hir::LifetimeName>,
impl_trait: bool
}

View File

@ -278,7 +278,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSnakeCase {
fn check_lifetime_def(&mut self, cx: &LateContext, t: &hir::LifetimeDef) {
self.check_snake_case(cx,
"lifetime",
&t.lifetime.name.as_str(),
&t.lifetime.name.name().as_str(),
Some(t.lifetime.span));
}

View File

@ -122,14 +122,6 @@ impl<'a> AstValidator<'a> {
}
impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_lifetime(&mut self, lt: &'a Lifetime) {
if lt.ident.name == "'_" {
self.err_handler().span_err(lt.span, &format!("invalid lifetime name `{}`", lt.ident));
}
visit::walk_lifetime(self, lt)
}
fn visit_expr(&mut self, expr: &'a Expr) {
match expr.node {
ExprKind::While(.., Some(ident)) |

View File

@ -523,7 +523,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
let (span, name) = if index < ast_generics.lifetimes.len() {
(ast_generics.lifetimes[index].lifetime.span,
ast_generics.lifetimes[index].lifetime.name)
ast_generics.lifetimes[index].lifetime.name.name())
} else {
let index = index - ast_generics.lifetimes.len();
(ast_generics.ty_params[index].span,

View File

@ -953,7 +953,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics);
let regions = early_lifetimes.enumerate().map(|(i, l)| {
ty::RegionParameterDef {
name: l.lifetime.name,
name: l.lifetime.name.name(),
index: own_start + i as u32,
def_id: tcx.hir.local_def_id(l.lifetime.id),
pure_wrt_drop: l.pure_wrt_drop,
@ -1423,7 +1423,7 @@ fn predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: tcx.hir.local_def_id(param.lifetime.id),
index,
name: param.lifetime.name
name: param.lifetime.name.name(),
}));
index += 1;

View File

@ -132,7 +132,7 @@ fn enforce_impl_params_are_constrained<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
!input_parameters.contains(&param)
{
report_unused_parameter(tcx, lifetime.lifetime.span,
"lifetime", &lifetime.lifetime.name.to_string());
"lifetime", &lifetime.lifetime.name.name().to_string());
}
}

View File

@ -876,7 +876,7 @@ impl Clean<Lifetime> for hir::Lifetime {
}
_ => {}
}
Lifetime(self.name.to_string())
Lifetime(self.name.name().to_string())
}
}
@ -884,14 +884,14 @@ impl Clean<Lifetime> for hir::LifetimeDef {
fn clean(&self, _: &DocContext) -> Lifetime {
if self.bounds.len() > 0 {
let mut s = format!("{}: {}",
self.lifetime.name.to_string(),
self.bounds[0].name.to_string());
self.lifetime.name.name(),
self.bounds[0].name.name());
for bound in self.bounds.iter().skip(1) {
s.push_str(&format!(" + {}", bound.name.to_string()));
s.push_str(&format!(" + {}", bound.name.name()));
}
Lifetime(s)
} else {
Lifetime(self.lifetime.name.to_string())
Lifetime(self.lifetime.name.name().to_string())
}
}
}

View File

@ -389,6 +389,9 @@ declare_features! (
// Copy/Clone closures (RFC 2132)
(active, clone_closures, "1.22.0", Some(44490)),
(active, copy_closures, "1.22.0", Some(44490)),
// allow `'_` placeholder lifetimes
(active, underscore_lifetimes, "1.22.0", Some(44524)),
);
declare_features! (
@ -1572,6 +1575,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
visit::walk_lifetime_def(self, lifetime_def)
}
fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) {
if lt.ident.name == "'_" {
gate_feature_post!(&self, underscore_lifetimes, lt.span,
"underscore lifetimes are unstable");
}
visit::walk_lifetime(self, lt)
}
}
pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {

View File

@ -0,0 +1,20 @@
// Copyright 2017 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.
#![feature(underscore_lifetimes)]
struct Foo<'a: '_>(&'a u8); //~ ERROR invalid lifetime bound name: `'_`
fn foo<'a: '_>(_: &'a u8) {} //~ ERROR invalid lifetime bound name: `'_`
struct Bar<'a>(&'a u8);
impl<'a: '_> Bar<'a> { //~ ERROR invalid lifetime bound name: `'_`
fn bar() {}
}
fn main() {}

View File

@ -0,0 +1,20 @@
// Copyright 2017 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.
struct Foo<'a>(&'a u8);
fn foo(x: &u8) -> Foo<'_> { //~ ERROR underscore lifetimes are unstable
Foo(x)
}
fn main() {
let x = 5;
let _ = foo(&x);
}

View File

@ -8,12 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn _f<'_>() //~ ERROR invalid lifetime name `'_`
-> &'_ u8 //~ ERROR invalid lifetime name `'_`
{
panic!();
}
fn main() {
'_: loop { //~ ERROR invalid label name `'_`
break '_ //~ ERROR invalid label name `'_`

View File

@ -0,0 +1,39 @@
// Copyright 2017 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.
#![feature(underscore_lifetimes)]
struct Foo<'a>(&'a u8);
struct Baz<'a>(&'_ &'a u8); //~ ERROR missing lifetime specifier
impl Foo<'_> { //~ ERROR missing lifetime specifier
fn x() {}
}
fn foo<'_> //~ ERROR invalid lifetime parameter name: `'_`
(_: Foo<'_>) {}
trait Meh<'a> {}
impl<'a> Meh<'a> for u8 {}
fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR invalid lifetime parameter name: `'_`
//~^ ERROR missing lifetime specifier
//~^^ ERROR missing lifetime specifier
{
Box::new(5u8)
}
fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y } //~ ERROR missing lifetime specifier
fn main() {
let x = 5;
foo(Foo(&x));
let _ = meh();
}

View File

@ -0,0 +1,15 @@
// Copyright 2017 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.
#![feature(underscore_lifetimes)]
fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); } //~ ERROR lifetime mismatch
fn main() {}

View File

@ -0,0 +1,47 @@
// Copyright 2017 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.
#![feature(underscore_lifetimes)]
struct Foo<'a>(&'a u8);
fn foo(x: &u8) -> Foo<'_> {
Foo(x)
}
fn foo2(x: &'_ u8) -> Foo<'_> {
Foo(x)
}
fn foo3(x: &'_ u8) -> Foo {
Foo(x)
}
fn foo4(_: Foo<'_>) {}
struct Foo2<'a, 'b> {
a: &'a u8,
b: &'b u8,
}
fn foo5<'b>(foo: Foo2<'_, 'b>) -> &'b u8 {
foo.b
}
fn main() {
let x = &5;
let _ = foo(x);
let _ = foo2(x);
let _ = foo3(x);
foo4(Foo(x));
let _ = foo5(Foo2 {
a: x,
b: &6,
});
}