Rollup merge of #63501 - nikomatsakis:issue-63500-async-anon-impl-lifetime, r=cramertj

use `ParamName` to track in-scope lifetimes instead of Ident

Also, clear in-scope lifetimes when visiting nested items.

Fixes #63500.
Fixes #63225.
Fixes #52532.

r? @cramertj
This commit is contained in:
Mazdak Farrokhzad 2019-08-14 04:18:49 +02:00 committed by GitHub
commit 5741e29417
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 9 deletions

View File

@ -136,7 +136,10 @@ pub struct LoweringContext<'a> {
/// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
/// against this list to see if it is already in-scope, or if a definition
/// needs to be created for it.
in_scope_lifetimes: Vec<Ident>,
///
/// We always store a `modern()` version of the param-name in this
/// vector.
in_scope_lifetimes: Vec<ParamName>,
current_module: NodeId,
@ -822,7 +825,7 @@ impl<'a> LoweringContext<'a> {
return;
}
if self.in_scope_lifetimes.contains(&ident.modern()) {
if self.in_scope_lifetimes.contains(&ParamName::Plain(ident.modern())) {
return;
}
@ -856,7 +859,7 @@ impl<'a> LoweringContext<'a> {
{
let old_len = self.in_scope_lifetimes.len();
let lt_def_names = params.iter().filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => Some(param.ident.modern()),
GenericParamKind::Lifetime { .. } => Some(ParamName::Plain(param.ident.modern())),
_ => None,
});
self.in_scope_lifetimes.extend(lt_def_names);
@ -2271,10 +2274,14 @@ impl<'a> LoweringContext<'a> {
let lifetime_params: Vec<(Span, ParamName)> =
this.in_scope_lifetimes
.iter().cloned()
.map(|ident| (ident.span, ParamName::Plain(ident)))
.map(|name| (name.ident().span, name))
.chain(this.lifetimes_to_define.iter().cloned())
.collect();
debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", this.in_scope_lifetimes);
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", this.lifetimes_to_define);
debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
let generic_params =
lifetime_params
.iter().cloned()

View File

@ -60,10 +60,12 @@ impl<'tcx, 'interner> Visitor<'tcx> for ItemLowerer<'tcx, 'interner> {
fn visit_item(&mut self, item: &'tcx Item) {
let mut item_hir_id = None;
self.lctx.with_hir_id_owner(item.id, |lctx| {
if let Some(hir_item) = lctx.lower_item(item) {
item_hir_id = Some(hir_item.hir_id);
lctx.insert_item(hir_item);
}
lctx.without_in_scope_lifetime_defs(|lctx| {
if let Some(hir_item) = lctx.lower_item(item) {
item_hir_id = Some(hir_item.hir_id);
lctx.insert_item(hir_item);
}
})
});
if let Some(hir_id) = item_hir_id {
@ -123,7 +125,7 @@ impl LoweringContext<'_> {
_ => &[],
};
let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind {
hir::GenericParamKind::Lifetime { .. } => Some(param.name.ident().modern()),
hir::GenericParamKind::Lifetime { .. } => Some(param.name.modern()),
_ => None,
});
self.in_scope_lifetimes.extend(lt_def_names);
@ -134,6 +136,28 @@ impl LoweringContext<'_> {
res
}
// Clears (and restores) the `in_scope_lifetimes` field. Used when
// visiting nested items, which never inherit in-scope lifetimes
// from their surrounding environment.
fn without_in_scope_lifetime_defs<T>(
&mut self,
f: impl FnOnce(&mut LoweringContext<'_>) -> T,
) -> T {
let old_in_scope_lifetimes = std::mem::replace(&mut self.in_scope_lifetimes, vec![]);
// this vector is only used when walking over impl headers,
// input types, and the like, and should not be non-empty in
// between items
assert!(self.lifetimes_to_define.is_empty());
let res = f(self);
assert!(self.in_scope_lifetimes.is_empty());
self.in_scope_lifetimes = old_in_scope_lifetimes;
res
}
pub(super) fn lower_mod(&mut self, m: &Mod) -> hir::Mod {
hir::Mod {
inner: m.inner,

View File

@ -0,0 +1,17 @@
// Check that `async fn` inside of an impl with `'_`
// in the header compiles correctly.
//
// Regression test for #63500.
//
// check-pass
// edition:2018
#![feature(async_await)]
struct Foo<'a>(&'a u8);
impl Foo<'_> {
async fn bar() {}
}
fn main() { }

View File

@ -0,0 +1,17 @@
// Test that async fn works when nested inside of
// impls with lifetime parameters.
//
// check-pass
// edition:2018
#![feature(async_await)]
struct Foo<'a>(&'a ());
impl<'a> Foo<'a> {
fn test() {
async fn test() {}
}
}
fn main() { }

View File

@ -0,0 +1,20 @@
// Test that the `'a` from the impl doesn't
// prevent us from creating a `'a` parameter
// on the `blah` function.
//
// check-pass
#![feature(in_band_lifetimes)]
struct Foo<'a> {
x: &'a u32
}
impl Foo<'a> {
fn method(&self) {
fn blah(f: Foo<'a>) { }
}
}
fn main() { }