From 18e54539ca08ee9cad46200206320ce290086999 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 12 Aug 2019 15:17:16 -0400 Subject: [PATCH 1/4] use `ParamName` to track in-scope lifetimes instead of Ident This allows us to record "fresh" lifetime names for cases like `impl Foo<'_>`. --- src/librustc/hir/lowering.rs | 12 ++++++++---- src/librustc/hir/lowering/item.rs | 2 +- .../async-fn-elided-impl-lifetime-parameter.rs | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index fe69c9e634635..184180d67be7b 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -136,7 +136,7 @@ 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, + in_scope_lifetimes: Vec, current_module: NodeId, @@ -865,7 +865,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; } @@ -899,7 +899,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); @@ -2267,10 +2267,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() diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs index 6b717e75199c7..4d6039e101a88 100644 --- a/src/librustc/hir/lowering/item.rs +++ b/src/librustc/hir/lowering/item.rs @@ -123,7 +123,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), _ => None, }); self.in_scope_lifetimes.extend(lt_def_names); diff --git a/src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs b/src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs new file mode 100644 index 0000000000000..4ded6385325a8 --- /dev/null +++ b/src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs @@ -0,0 +1,16 @@ +// Check that `async fn` inside of an impl with `'_` +// in the header compiles correctly. +// +// Regression test for #63500. +// +// check-pass + +#![feature(async_await)] + +struct Foo<'a>(&'a u8); + +impl Foo<'_> { + async fn bar() {} +} + +fn main() { } From cbe8518407fd6986093946353252b0384867aa53 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 12 Aug 2019 18:14:48 -0400 Subject: [PATCH 2/4] use `modern` everywhere --- src/librustc/hir/lowering.rs | 3 +++ src/librustc/hir/lowering/item.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 184180d67be7b..0816c7a20f850 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -136,6 +136,9 @@ 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. + /// + /// We always store a `modern()` version of the param-name in this + /// vector. in_scope_lifetimes: Vec, current_module: NodeId, diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs index 4d6039e101a88..7b774812a24f2 100644 --- a/src/librustc/hir/lowering/item.rs +++ b/src/librustc/hir/lowering/item.rs @@ -123,7 +123,7 @@ impl LoweringContext<'_> { _ => &[], }; let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind { - hir::GenericParamKind::Lifetime { .. } => Some(param.name), + hir::GenericParamKind::Lifetime { .. } => Some(param.name.modern()), _ => None, }); self.in_scope_lifetimes.extend(lt_def_names); From a02a171e6a8e1cf601867230bba577c0351461b2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 12 Aug 2019 18:33:53 -0400 Subject: [PATCH 3/4] add edition to regression test --- .../ui/async-await/async-fn-elided-impl-lifetime-parameter.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs b/src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs index 4ded6385325a8..1cbc5133a07e6 100644 --- a/src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs +++ b/src/test/ui/async-await/async-fn-elided-impl-lifetime-parameter.rs @@ -4,6 +4,7 @@ // Regression test for #63500. // // check-pass +// edition:2018 #![feature(async_await)] From e4756e6b07989d1cf195667bcf5d9f780618031a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 12 Aug 2019 21:08:32 -0400 Subject: [PATCH 4/4] 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() { }