From e4756e6b07989d1cf195667bcf5d9f780618031a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 12 Aug 2019 21:08:32 -0400 Subject: [PATCH] clear in-scope lifetimes for nested items in HIR lowering This was causing us to incorrectly think the lifetimes were already declared on the scope for the nested item, when in fact they are not inherited. --- src/librustc/hir/lowering/item.rs | 32 ++++++++++++++++--- src/test/ui/async-await/nested-in-impl.rs | 17 ++++++++++ src/test/ui/in-band-lifetimes/nested-items.rs | 20 ++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/async-await/nested-in-impl.rs create mode 100644 src/test/ui/in-band-lifetimes/nested-items.rs diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs index 7b774812a24f2..dd95d99d4e1d6 100644 --- a/src/librustc/hir/lowering/item.rs +++ b/src/librustc/hir/lowering/item.rs @@ -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 { @@ -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( + &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, diff --git a/src/test/ui/async-await/nested-in-impl.rs b/src/test/ui/async-await/nested-in-impl.rs new file mode 100644 index 0000000000000..3c82160595f1d --- /dev/null +++ b/src/test/ui/async-await/nested-in-impl.rs @@ -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() { } diff --git a/src/test/ui/in-band-lifetimes/nested-items.rs b/src/test/ui/in-band-lifetimes/nested-items.rs new file mode 100644 index 0000000000000..7de20712fba94 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/nested-items.rs @@ -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() { }