Change:
The diagnostics ("use of undeclared lifetime name") are bad, but this indeed must be an error because the inner is an illegal use of the outer rather than an implicitly defined fresh lifetime.
Hm, I don't think that's quite what I'd expect: previously using in that context would be fine because there is no shadowing so to speak, that is, this compiles:
From Similarly, if a fn definition is nested inside another fn definition, it is an error to mention lifetimes from that outer definition (without binding them explicitly). This is again intended for future-proofing and clarity, and is an edge case. (In the second example there's an explicit definition that shadows the previous definition for following uses, in the first example there's no second definition, only use)
I see that this is in the RFC, but I think I more or less disagree with the reasoning given. Disallowing the use of inband lifetimes when nesting function seems arbitrary and not entirely helpful; it also makes a mechanical change from "old" to "new" more difficult because shadowing is visible only based on indent levels.
IMO it should be an error, with a helpful note like the one you get when writing . Let's not make in-band lifetimes any more confusing than they already are.
The distinction is that inband lifetime's selling point is that you don't need to declare lifetimes. If we say that, and then clarify it with "except" then I think the feature feels incomplete. Inband lifetimes confusion does not increase with permitting this, IMO.
The problem is if it's allowed, then has a different meaning than This seems unfortunate and confusing. Even unergonomic if I may say so. There's no way that the distinction between and won't be missed in a quick scan of the second one. But, like, the RFC process did decide that isn't important, so whatever.
Well, somewhat, but not really. Nothing outside of imports/definitions inherits into the inner function, unlike the impl where there is a history and multiple things which inherit (, lifetimes, generics).

Discussion:
/// 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. <del> in_scope_lifetimes: Vec<Ident>, </del> <ins> /// /// We always store a `modern()` version of the param-name in this /// vector. in_scope_lifetimes: Vec<ParamName>, </ins> current_module: NodeId,

return; } <del> if self.in_scope_lifetimes.contains(&ident.modern()) { </del> <ins> if self.in_scope_lifetimes.contains(&ParamName::Plain(ident.modern())) { </ins> return; }

{ let old_len = self.in_scope_lifetimes.len(); let lt_def_names = params.iter().filter_map(|param| match param.kind { <del> GenericParamKind::Lifetime { .. } => Some(param.ident.modern()), </del> <ins> GenericParamKind::Lifetime { .. } => Some(ParamName::Plain(param.ident.modern())), </ins> _ => None, }); self.in_scope_lifetimes.extend(lt_def_names);

let lifetime_params: Vec<(Span, ParamName)> = this.in_scope_lifetimes .iter().cloned() <del> .map(|ident| (ident.span, ParamName::Plain(ident))) </del> <ins> .map(|name| (name.ident().span, name)) </ins> .chain(this.lifetimes_to_define.iter().cloned()) .collect(); <ins> 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); </ins> let generic_params = lifetime_params .iter().cloned()

fn visit_item(&mut self, item: &'tcx Item) { let mut item_hir_id = None; self.lctx.with_hir_id_owner(item.id, |lctx| { <del> if let Some(hir_item) = lctx.lower_item(item) { item_hir_id = Some(hir_item.hir_id); lctx.insert_item(hir_item); } </del> <ins> 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); } }) </ins> }); if let Some(hir_id) = item_hir_id {

_ => &[], }; let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind { <del> hir::GenericParamKind::Lifetime { .. } => Some(param.name.ident().modern()), </del> <ins> hir::GenericParamKind::Lifetime { .. } => Some(param.name.modern()), </ins> _ => None, }); self.in_scope_lifetimes.extend(lt_def_names);

res } <ins> // 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 } </ins> pub(super) fn lower_mod(&mut self, m: &Mod) -> hir::Mod { hir::Mod { inner: m.inner,

<ins> // 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() { } </ins>

<ins> // 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() { } </ins>

<ins> // 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() { } </ins>