1
Fork 0

Allow both explicit and elided lifetimes in the same impl header

(While still prohibiting explicit and in-band in the same header.)
This commit is contained in:
Scott McMurray 2018-09-22 01:45:42 -07:00
parent ed45f9cbf4
commit 003c4ffa83
5 changed files with 83 additions and 29 deletions

View file

@ -683,9 +683,15 @@ impl<'a> LoweringContext<'a> {
// Get the name we'll use to make the def-path. Note
// that collisions are ok here and this shouldn't
// really show up for end-user.
let str_name = match hir_name {
ParamName::Plain(ident) => ident.as_interned_str(),
ParamName::Fresh(_) => keywords::UnderscoreLifetime.name().as_interned_str(),
let (str_name, kind) = match hir_name {
ParamName::Plain(ident) => (
ident.as_interned_str(),
hir::LifetimeParamKind::InBand,
),
ParamName::Fresh(_) => (
keywords::UnderscoreLifetime.name().as_interned_str(),
hir::LifetimeParamKind::Elided,
),
};
// Add a definition for the in-band lifetime def
@ -705,7 +711,7 @@ impl<'a> LoweringContext<'a> {
bounds: hir_vec![],
span,
pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { in_band: true }
kind: hir::GenericParamKind::Lifetime { kind }
}
})
.chain(in_band_ty_params.into_iter())
@ -1452,11 +1458,15 @@ impl<'a> LoweringContext<'a> {
lifetime.span,
);
let name = match name {
hir::LifetimeName::Underscore => {
hir::ParamName::Plain(keywords::UnderscoreLifetime.ident())
}
hir::LifetimeName::Param(param_name) => param_name,
let (name, kind) = match name {
hir::LifetimeName::Underscore => (
hir::ParamName::Plain(keywords::UnderscoreLifetime.ident()),
hir::LifetimeParamKind::Elided,
),
hir::LifetimeName::Param(param_name) => (
param_name,
hir::LifetimeParamKind::Explicit,
),
_ => bug!("expected LifetimeName::Param or ParamName::Plain"),
};
@ -1467,9 +1477,7 @@ impl<'a> LoweringContext<'a> {
pure_wrt_drop: false,
attrs: hir_vec![],
bounds: hir_vec![],
kind: hir::GenericParamKind::Lifetime {
in_band: false,
}
kind: hir::GenericParamKind::Lifetime { kind }
});
}
}
@ -2283,7 +2291,9 @@ impl<'a> LoweringContext<'a> {
pure_wrt_drop: attr::contains_name(&param.attrs, "may_dangle"),
attrs: self.lower_attrs(&param.attrs),
bounds,
kind: hir::GenericParamKind::Lifetime { in_band: false }
kind: hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit,
}
};
self.is_collecting_in_band_lifetimes = was_collecting_in_band;

View file

@ -499,14 +499,27 @@ impl GenericBound {
pub type GenericBounds = HirVec<GenericBound>;
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
pub enum LifetimeParamKind {
// Indicates that the lifetime definition was explicitly declared, like:
// `fn foo<'a>(x: &'a u8) -> &'a u8 { x }`
Explicit,
// Indicates that the lifetime definition was synthetically added
// as a result of an in-band lifetime usage like:
// `fn foo(x: &'a u8) -> &'a u8 { x }`
InBand,
// Indication that the lifetime was elided like both cases here:
// `fn foo(x: &u8) -> &'_ u8 { x }`
Elided,
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub enum GenericParamKind {
/// A lifetime definition, eg `'a: 'b + 'c + 'd`.
Lifetime {
// Indicates that the lifetime definition was synthetically added
// as a result of an in-band lifetime usage like:
// `fn foo(x: &'a u8) -> &'a u8 { x }`
in_band: bool,
kind: LifetimeParamKind,
},
Type {
default: Option<P<Ty>>,

View file

@ -207,14 +207,20 @@ impl_stable_hash_for!(struct hir::GenericParam {
kind
});
impl_stable_hash_for!(enum hir::LifetimeParamKind {
Explicit,
InBand,
Elided
});
impl<'a> HashStable<StableHashingContext<'a>> for hir::GenericParamKind {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
mem::discriminant(self).hash_stable(hcx, hasher);
match self {
hir::GenericParamKind::Lifetime { in_band } => {
in_band.hash_stable(hcx, hasher);
hir::GenericParamKind::Lifetime { kind } => {
kind.hash_stable(hcx, hasher);
}
hir::GenericParamKind::Type { ref default, synthetic } => {
default.hash_stable(hcx, hasher);

View file

@ -35,7 +35,7 @@ use syntax_pos::Span;
use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap, NodeSet};
use hir::intravisit::{self, NestedVisitorMap, Visitor};
use hir::{self, GenericParamKind};
use hir::{self, GenericParamKind, LifetimeParamKind};
/// The origin of a named lifetime definition.
///
@ -51,8 +51,8 @@ pub enum LifetimeDefOrigin {
impl LifetimeDefOrigin {
fn from_param(param: &GenericParam) -> Self {
match param.kind {
GenericParamKind::Lifetime { in_band } => {
if in_band {
GenericParamKind::Lifetime { kind } => {
if kind == LifetimeParamKind::InBand {
LifetimeDefOrigin::InBand
} else {
LifetimeDefOrigin::Explicit
@ -1087,15 +1087,15 @@ fn check_mixed_explicit_and_in_band_defs(
tcx: TyCtxt<'_, '_, '_>,
params: &P<[hir::GenericParam]>,
) {
let in_bands: Vec<_> = params.iter().filter_map(|param| match param.kind {
GenericParamKind::Lifetime { in_band, .. } => Some((in_band, param.span)),
let lifetime_params: Vec<_> = params.iter().filter_map(|param| match param.kind {
GenericParamKind::Lifetime { kind, .. } => Some((kind, param.span)),
_ => None,
}).collect();
let out_of_band = in_bands.iter().find(|(in_band, _)| !in_band);
let in_band = in_bands.iter().find(|(in_band, _)| *in_band);
let explicit = lifetime_params.iter().find(|(kind, _)| *kind == LifetimeParamKind::Explicit);
let in_band = lifetime_params.iter().find(|(kind, _)| *kind == LifetimeParamKind::InBand);
if let (Some((_, out_of_band_span)), Some((_, in_band_span)))
= (out_of_band, in_band) {
if let (Some((_, explicit_span)), Some((_, in_band_span)))
= (explicit, in_band) {
struct_span_err!(
tcx.sess,
*in_band_span,
@ -1104,7 +1104,7 @@ fn check_mixed_explicit_and_in_band_defs(
).span_label(
*in_band_span,
"in-band lifetime definition here",
).span_label(*out_of_band_span, "explicit lifetime definition here")
).span_label(*explicit_span, "explicit lifetime definition here")
.emit();
}
}

View file

@ -0,0 +1,25 @@
// Copyright 2018 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.
// run-pass
#![allow(warnings)]
#![feature(impl_header_lifetime_elision)]
// This works for functions...
fn foo<'a>(x: &str, y: &'a str) {}
// ...so this should work for impls
impl<'a> Foo<&str> for &'a str {}
trait Foo<T> {}
fn main() {
}