Skip to content

Commit

Permalink
Rollup merge of rust-lang#63501 - nikomatsakis:issue-63500-async-anon…
Browse files Browse the repository at this point in the history
…-impl-lifetime, r=cramertj

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

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

Fixes rust-lang#63500.
Fixes rust-lang#63225.
Fixes rust-lang#52532.

r? @cramertj
  • Loading branch information
Centril committed Aug 14, 2019
2 parents 4134241 + e4756e6 commit 5741e29
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 9 deletions.
15 changes: 11 additions & 4 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()
Expand Down
34 changes: 29 additions & 5 deletions src/librustc/hir/lowering/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down
17 changes: 17 additions & 0 deletions src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs
Original file line number Diff line number Diff line change
@@ -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() { }
17 changes: 17 additions & 0 deletions src/test/ui/async-await/nested-in-impl.rs
Original file line number Diff line number Diff line change
@@ -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() { }
20 changes: 20 additions & 0 deletions src/test/ui/in-band-lifetimes/nested-items.rs
Original file line number Diff line number Diff line change
@@ -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() { }

0 comments on commit 5741e29

Please sign in to comment.