diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index b452290dff460..73fdc74b6f075 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -95,6 +95,7 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> { } fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { + debug!(in_scope_lifetimes = ?self.lctx.in_scope_lifetimes); self.lctx.with_hir_id_owner(item.id, |lctx| match ctxt { AssocCtxt::Trait => hir::OwnerNode::TraitItem(lctx.lower_trait_item(item)), AssocCtxt::Impl => hir::OwnerNode::ImplItem(lctx.lower_impl_item(item)), @@ -118,35 +119,42 @@ impl<'hir> LoweringContext<'_, 'hir> { // This should only be used with generics that have already had their // in-band lifetimes added. In practice, this means that this function is // only used when lowering a child item of a trait or impl. + #[tracing::instrument(level = "debug", skip(self, f))] fn with_parent_item_lifetime_defs( &mut self, parent_hir_id: LocalDefId, f: impl FnOnce(&mut Self) -> T, ) -> T { - let old_len = self.in_scope_lifetimes.len(); - let parent_generics = match self.owners[parent_hir_id].unwrap().node().expect_item().kind { hir::ItemKind::Impl(hir::Impl { ref generics, .. }) | hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params, _ => &[], }; - let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind { - hir::GenericParamKind::Lifetime { .. } => Some(param.name.normalize_to_macros_2_0()), - _ => None, - }); - self.in_scope_lifetimes.extend(lt_def_names); + let lt_def_names = parent_generics + .iter() + .filter_map(|param| match param.kind { + hir::GenericParamKind::Lifetime { .. } => { + Some(param.name.normalize_to_macros_2_0()) + } + _ => None, + }) + .collect(); + let old_in_scope_lifetimes = mem::replace(&mut self.in_scope_lifetimes, lt_def_names); + debug!(in_scope_lifetimes = ?self.in_scope_lifetimes); let res = f(self); - self.in_scope_lifetimes.truncate(old_len); + self.in_scope_lifetimes = old_in_scope_lifetimes; res } // Clears (and restores) the `in_scope_lifetimes` field. Used when // visiting nested items, which never inherit in-scope lifetimes // from their surrounding environment. + #[tracing::instrument(level = "debug", skip(self, f))] fn without_in_scope_lifetime_defs(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { let old_in_scope_lifetimes = mem::replace(&mut self.in_scope_lifetimes, vec![]); + debug!(?old_in_scope_lifetimes); // this vector is only used when walking over impl headers, // input types, and the like, and should not be non-empty in diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 7a272308fb051..9d973e29999af 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -129,20 +129,16 @@ struct LoweringContext<'a, 'hir: 'a> { /// written at all (e.g., `&T` or `std::cell::Ref`). anonymous_lifetime_mode: AnonymousLifetimeMode, - /// Used to create lifetime definitions from in-band lifetime usages. - /// e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8` - /// When a named lifetime is encountered in a function or impl header and - /// has not been defined - /// (i.e., it doesn't appear in the in_scope_lifetimes list), it is added + /// Used to create lifetime definitions for anonymous lifetimes. + /// When an anonymous lifetime is encountered in a function or impl header and + /// requires to create a fresh lifetime parameter, it is added /// to this list. The results of this list are then added to the list of /// lifetime definitions in the corresponding impl or function generics. - lifetimes_to_define: Vec<(Span, ParamName)>, + lifetimes_to_define: Vec<(Span, NodeId)>, - /// `true` if in-band lifetimes are being collected. This is used to - /// indicate whether or not we're in a place where new lifetimes will result - /// in in-band lifetime definitions, such a function or an impl header, - /// including implicit lifetimes from `impl_header_lifetime_elision`. - is_collecting_anonymous_lifetimes: bool, + /// If anonymous lifetimes are being collected, this field holds the parent + /// `LocalDefId` to create the fresh lifetime parameters' `LocalDefId`. + is_collecting_anonymous_lifetimes: Option, /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. /// We always store a `normalize_to_macros_2_0()` version of the param-name in this @@ -375,7 +371,7 @@ pub fn lower_crate<'a, 'hir>( task_context: None, current_item: None, lifetimes_to_define: Vec::new(), - is_collecting_anonymous_lifetimes: false, + is_collecting_anonymous_lifetimes: None, in_scope_lifetimes: Vec::new(), allow_try_trait: Some([sym::try_trait_v2][..].into()), allow_gen_future: Some([sym::gen_future][..].into()), @@ -720,9 +716,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// parameter while `f` is running (and restored afterwards). fn collect_in_band_defs( &mut self, + parent_def_id: LocalDefId, f: impl FnOnce(&mut Self) -> T, - ) -> (Vec<(Span, ParamName)>, T) { - let was_collecting = std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, true); + ) -> (Vec<(Span, NodeId)>, T) { + let was_collecting = + std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, Some(parent_def_id)); let len = self.lifetimes_to_define.len(); let res = f(self); @@ -733,49 +731,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// Converts a lifetime into a new generic parameter. - fn lifetime_to_generic_param( + fn fresh_lifetime_to_generic_param( &mut self, span: Span, - hir_name: ParamName, - parent_def_id: LocalDefId, + node_id: NodeId, ) -> hir::GenericParam<'hir> { - let node_id = self.resolver.next_node_id(); - - // Get the name we'll use to make the def-path. Note - // that collisions are ok here and this shouldn't - // really show up for end-user. - let (str_name, kind) = match hir_name { - ParamName::Plain(ident) => (ident.name, hir::LifetimeParamKind::Explicit), - ParamName::Fresh(_) => (kw::UnderscoreLifetime, hir::LifetimeParamKind::Elided), - ParamName::Error => (kw::UnderscoreLifetime, hir::LifetimeParamKind::Error), - }; - - // Add a definition for the in-band lifetime def. - self.resolver.create_def( - parent_def_id, - node_id, - DefPathData::LifetimeNs(str_name), - ExpnId::root(), - span.with_parent(None), - ); - + let hir_id = self.lower_node_id(node_id); + let def_id = self.resolver.local_def_id(node_id); hir::GenericParam { - hir_id: self.lower_node_id(node_id), - name: hir_name, + hir_id, + name: hir::ParamName::Fresh(def_id), bounds: &[], span: self.lower_span(span), pure_wrt_drop: false, - kind: hir::GenericParamKind::Lifetime { kind }, + kind: hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided }, } } /// When we have either an elided or `'_` lifetime in an impl /// header, we convert it to an in-band lifetime. fn collect_fresh_anonymous_lifetime(&mut self, span: Span) -> ParamName { - assert!(self.is_collecting_anonymous_lifetimes); - let index = self.lifetimes_to_define.len() + self.in_scope_lifetimes.len(); - let hir_name = ParamName::Fresh(index); - self.lifetimes_to_define.push((span, hir_name)); + let Some(parent_def_id) = self.is_collecting_anonymous_lifetimes else { panic!() }; + + let node_id = self.resolver.next_node_id(); + + // Add a definition for the in-band lifetime def. + let param_def_id = self.resolver.create_def( + parent_def_id, + node_id, + DefPathData::LifetimeNs(kw::UnderscoreLifetime), + ExpnId::root(), + span.with_parent(None), + ); + + let hir_name = ParamName::Fresh(param_def_id); + self.lifetimes_to_define.push((span, node_id)); hir_name } @@ -817,7 +807,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { f: impl FnOnce(&mut Self, &mut Vec>) -> T, ) -> (hir::Generics<'hir>, T) { let (lifetimes_to_define, (mut lowered_generics, impl_trait_defs, res)) = self - .collect_in_band_defs(|this| { + .collect_in_band_defs(parent_def_id, |this| { this.with_anonymous_lifetime_mode(anonymous_lifetime_mode, |this| { this.with_in_scope_lifetime_defs(&generics.params, |this| { let mut impl_trait_defs = Vec::new(); @@ -844,9 +834,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lowered_generics.params.extend( lifetimes_to_define .into_iter() - .map(|(span, hir_name)| { - self.lifetime_to_generic_param(span, hir_name, parent_def_id) - }) + .map(|(span, node_id)| self.fresh_lifetime_to_generic_param(span, node_id)) .chain(impl_trait_defs), ); @@ -1763,15 +1751,53 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .in_scope_lifetimes .iter() .cloned() - .map(|name| (name.ident().span, name, hir::LifetimeName::Param(name))) - .chain( - self.lifetimes_to_define - .iter() - .map(|&(span, name)| (span, name, hir::LifetimeName::Param(name))), - ) + .map(|name| (name.ident().span, hir::LifetimeName::Param(name))) + .chain(self.lifetimes_to_define.iter().map(|&(span, node_id)| { + let def_id = self.resolver.local_def_id(node_id); + let name = hir::ParamName::Fresh(def_id); + (span, hir::LifetimeName::Param(name)) + })) .collect(); self.with_hir_id_owner(opaque_ty_node_id, |this| { + let mut generic_params: Vec<_> = lifetime_params + .iter() + .map(|&(span, name)| { + // We can only get lifetime names from the outside. + let hir::LifetimeName::Param(hir_name) = name else { panic!() }; + + let node_id = this.resolver.next_node_id(); + + // Add a definition for the in-band lifetime def. + let def_id = this.resolver.create_def( + opaque_ty_def_id, + node_id, + DefPathData::LifetimeNs(hir_name.ident().name), + ExpnId::root(), + span.with_parent(None), + ); + + let (kind, name) = match hir_name { + ParamName::Plain(ident) => { + (hir::LifetimeParamKind::Explicit, hir::ParamName::Plain(ident)) + } + ParamName::Fresh(_) => { + (hir::LifetimeParamKind::Elided, hir::ParamName::Fresh(def_id)) + } + ParamName::Error => (hir::LifetimeParamKind::Error, hir::ParamName::Error), + }; + + hir::GenericParam { + hir_id: this.lower_node_id(node_id), + name, + bounds: &[], + span: this.lower_span(span), + pure_wrt_drop: false, + kind: hir::GenericParamKind::Lifetime { kind }, + } + }) + .collect(); + // We have to be careful to get elision right here. The // idea is that we create a lifetime parameter for each // lifetime in the return type. So, given a return type @@ -1782,25 +1808,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // hence the elision takes place at the fn site. let (lifetimes_to_define, future_bound) = this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| { - this.collect_in_band_defs(|this| { + this.collect_in_band_defs(opaque_ty_def_id, |this| { this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span) }) }); debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound); debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define); - lifetime_params.extend( - // Output lifetime like `'_`: - lifetimes_to_define - .into_iter() - .map(|(span, name)| (span, name, hir::LifetimeName::Implicit(false))), - ); + // Output lifetime like `'_`: + for (span, node_id) in lifetimes_to_define { + let param = this.fresh_lifetime_to_generic_param(span, node_id); + lifetime_params.push((span, hir::LifetimeName::Implicit(false))); + generic_params.push(param); + } + let generic_params = this.arena.alloc_from_iter(generic_params); debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params); - - let generic_params = - this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name, _)| { - this.lifetime_to_generic_param(span, hir_name, opaque_ty_def_id) - })); + debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); let opaque_ty_item = hir::OpaqueTy { generics: hir::Generics { @@ -1833,7 +1856,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // For the "output" lifetime parameters, we just want to // generate `'_`. let generic_args = - self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _, name)| { + self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, name)| { GenericArg::Lifetime(hir::Lifetime { hir_id: self.next_id(), span: self.lower_span(span), @@ -1969,7 +1992,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (name, kind) = match param.kind { GenericParamKind::Lifetime => { let was_collecting_in_band = self.is_collecting_anonymous_lifetimes; - self.is_collecting_anonymous_lifetimes = false; + self.is_collecting_anonymous_lifetimes = None; let lt = self .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c3795e48d7625..64ce196e4407b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -59,7 +59,7 @@ pub enum ParamName { /// /// where `'f` is something like `Fresh(0)`. The indices are /// unique per impl, but not necessarily continuous. - Fresh(usize), + Fresh(LocalDefId), /// Indicates an illegal name was given and an error has been /// reported (so we should squelch other derived errors). Occurs @@ -3303,7 +3303,7 @@ mod size_asserts { rustc_data_structures::static_assert_size!(super::Expr<'static>, 56); rustc_data_structures::static_assert_size!(super::Pat<'static>, 88); rustc_data_structures::static_assert_size!(super::QPath<'static>, 24); - rustc_data_structures::static_assert_size!(super::Ty<'static>, 80); + rustc_data_structures::static_assert_size!(super::Ty<'static>, 72); rustc_data_structures::static_assert_size!(super::Item<'static>, 184); rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);