| use hir::HirId; | |
| use hir::def::{DefKind, Res}; | |
| use rustc_ast::*; | |
| use rustc_data_structures::fx::FxHashSet; | |
| use rustc_hir as hir; | |
| use rustc_hir::def_id::DefId; | |
| use rustc_middle::ty::GenericParamDefKind; | |
| use rustc_middle::{bug, ty}; | |
| use rustc_span::symbol::kw; | |
| use rustc_span::{Ident, Span, sym}; | |
| use crate::LoweringContext; | |
| use crate::diagnostics::DelegationInfersMismatch; | |
| #[derive(Debug, Clone, Copy)] | |
| pub(super) enum GenericsPosition { | |
| Parent, | |
| Child, | |
| } | |
| #[derive(Debug)] | |
| pub(super) enum GenericArgSlot<T> { | |
| UserSpecified, | |
| Generate(T, Option<usize> /* Infer arg index from AST */), | |
| } | |
| pub(super) struct DelegationGenerics<T> { | |
| data: T, | |
| pos: GenericsPosition, | |
| trait_impl: bool, | |
| } | |
| type TyGenerics<'hir> = Vec<GenericArgSlot<&'hir ty::GenericParamDef>>; | |
| impl<'hir> DelegationGenerics<TyGenerics<'hir>> { | |
| fn generate_all( | |
| params: &'hir [ty::GenericParamDef], | |
| pos: GenericsPosition, | |
| trait_impl: bool, | |
| ) -> Self { | |
| DelegationGenerics { | |
| data: params.iter().map(|p| GenericArgSlot::Generate(p, None)).collect(), | |
| pos, | |
| trait_impl, | |
| } | |
| } | |
| } | |
| /// Used for storing either ty generics or their uplifted HIR version. First we obtain | |
| /// ty generics. Next, at some point of generics processing we need to uplift those | |
| /// generics to HIR, for this purpose we use `into_hir_generics` that uplifts ty generics | |
| /// and replaces Ty variant with Hir. Such approach is useful as we can call this method | |
| /// at any time knowing that uplifting will occur at most only once. Then, in order to obtain generic | |
| /// params or args we use `hir_generics_or_empty` or `into_generic_args` functions. | |
| /// There also may be situations when we obtained ty generics but never uplifted them to HIR, | |
| /// meaning we did not propagate them and thus we do not need to generate generic params | |
| /// (i.e., method call scenarios), in such a case this approach helps | |
| /// a lot as if `into_hir_generics` will not be called then uplifting will not happen. | |
| pub(super) enum HirOrTyGenerics<'hir> { | |
| Ty(DelegationGenerics<TyGenerics<'hir>>), | |
| Hir(DelegationGenerics<&'hir hir::Generics<'hir>>), | |
| } | |
| pub(super) struct GenericsGenerationResult<'hir> { | |
| pub(super) generics: HirOrTyGenerics<'hir>, | |
| pub(super) args_segment_id: HirId, | |
| pub(super) use_for_sig_inheritance: bool, | |
| } | |
| impl GenericsGenerationResult<'_> { | |
| pub(super) fn segment_id_for_sig(&self) -> Option<HirId> { | |
| self.use_for_sig_inheritance.then(|| self.args_segment_id) | |
| } | |
| } | |
| pub(super) struct GenericsGenerationResults<'hir> { | |
| pub(super) parent: GenericsGenerationResult<'hir>, | |
| pub(super) child: GenericsGenerationResult<'hir>, | |
| pub(super) self_ty_propagation_kind: Option<hir::DelegationSelfTyPropagationKind>, | |
| } | |
| pub(super) struct DelegationGenericArgsIterator<'hir> { | |
| index: usize = Default::default(), | |
| params: &'hir [hir::GenericParam<'hir>], | |
| } | |
| /// During generic args propagation we need to create generic args | |
| /// (and their `HirId`s) on demand, as some of generic args can not be used | |
| /// and in this case an assert of an unseen `HirId` will be triggered. Moreover, | |
| /// when replacing infers with generated generic params we should reuse existing | |
| /// `HirId` of replaced infer, thus this iterator abstracts the way `HirId`s are | |
| /// created for new generic args. | |
| impl<'hir> DelegationGenericArgsIterator<'hir> { | |
| pub(super) fn next( | |
| &mut self, | |
| ctx: &mut LoweringContext<'_, 'hir>, | |
| hir_id_factory: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> HirId, | |
| ) -> Option<hir::GenericArg<'hir>> { | |
| let p = loop { | |
| if self.index >= self.params.len() { | |
| return None; | |
| } | |
| let p = self.params[self.index]; | |
| self.index += 1; | |
| // Skip self generic arg, we do not need to propagate it. | |
| if p.name.ident().name == kw::SelfUpper || p.is_impl_trait() { | |
| continue; | |
| } | |
| break p; | |
| }; | |
| let hir_id = hir_id_factory(ctx); | |
| Some(match p.kind { | |
| hir::GenericParamKind::Lifetime { .. } => { | |
| hir::GenericArg::Lifetime(ctx.arena.alloc(hir::Lifetime { | |
| hir_id, | |
| ident: p.name.ident(), | |
| kind: hir::LifetimeKind::Param(p.def_id), | |
| source: hir::LifetimeSource::Path { angle_brackets: hir::AngleBrackets::Full }, | |
| syntax: hir::LifetimeSyntax::ExplicitBound, | |
| })) | |
| } | |
| hir::GenericParamKind::Type { .. } => hir::GenericArg::Type(ctx.arena.alloc(hir::Ty { | |
| hir_id, | |
| span: p.span, | |
| kind: hir::TyKind::Path(ctx.create_generic_arg_path(&p)), | |
| })), | |
| hir::GenericParamKind::Const { .. } => { | |
| hir::GenericArg::Const(ctx.arena.alloc(hir::ConstArg { | |
| hir_id, | |
| kind: hir::ConstArgKind::Path(ctx.create_generic_arg_path(&p)), | |
| span: p.span, | |
| })) | |
| } | |
| }) | |
| } | |
| pub(super) fn consume_all( | |
| mut self, | |
| ctx: &mut LoweringContext<'_, 'hir>, | |
| ) -> Vec<hir::GenericArg<'hir>> { | |
| let mut args = vec![]; | |
| while let Some(arg) = self.next(ctx, |ctx| ctx.next_id()) { | |
| args.push(arg); | |
| } | |
| args | |
| } | |
| } | |
| impl<'hir> HirOrTyGenerics<'hir> { | |
| pub(super) fn into_hir_generics(&mut self, ctx: &mut LoweringContext<'_, 'hir>, span: Span) { | |
| if let HirOrTyGenerics::Ty(ty) = self { | |
| let rename_self = matches!(ty.pos, GenericsPosition::Child); | |
| let params = ctx.uplift_delegation_generic_params(span, &ty.data, rename_self); | |
| *self = HirOrTyGenerics::Hir(DelegationGenerics { | |
| data: params, | |
| pos: ty.pos, | |
| trait_impl: ty.trait_impl, | |
| }); | |
| } | |
| } | |
| fn hir_generics_or_empty(&self) -> &'hir hir::Generics<'hir> { | |
| match self { | |
| HirOrTyGenerics::Ty(_) => hir::Generics::empty(), | |
| HirOrTyGenerics::Hir(hir) => hir.data, | |
| } | |
| } | |
| pub(super) fn create_args_iterator(&self) -> DelegationGenericArgsIterator<'hir> { | |
| match self { | |
| HirOrTyGenerics::Ty(_) => { | |
| bug!("attempting to get generic args before uplifting to HIR") | |
| } | |
| HirOrTyGenerics::Hir(hir) => { | |
| DelegationGenericArgsIterator { params: hir.data.params, .. } | |
| } | |
| } | |
| } | |
| pub(super) fn infer_indices(&self) -> FxHashSet<usize> { | |
| match self { | |
| HirOrTyGenerics::Ty(ty) => ty | |
| .data | |
| .iter() | |
| .flat_map(|slot| match slot { | |
| GenericArgSlot::Generate(_, Some(idx)) => Some(*idx), | |
| _ => None, | |
| }) | |
| .collect(), | |
| HirOrTyGenerics::Hir(_) => bug!("accessed infer indices on uplifted generics"), | |
| } | |
| } | |
| pub(super) fn is_trait_impl(&self) -> bool { | |
| match self { | |
| HirOrTyGenerics::Ty(ty) => ty.trait_impl, | |
| HirOrTyGenerics::Hir(hir) => hir.trait_impl, | |
| } | |
| } | |
| pub(super) fn find_self_param(&self) -> &'hir hir::GenericParam<'hir> { | |
| match self { | |
| HirOrTyGenerics::Ty(_) => { | |
| bug!("accessed ty-level generics while searching for uplifted `Self` param") | |
| } | |
| HirOrTyGenerics::Hir(hir) => hir | |
| .data | |
| .params | |
| .iter() | |
| .find(|p| p.name.ident().name == kw::SelfUpper) | |
| .expect("`Self` generic param is not found while expected"), | |
| } | |
| } | |
| } | |
| impl<'hir> GenericsGenerationResult<'hir> { | |
| fn new(generics: DelegationGenerics<TyGenerics<'hir>>) -> GenericsGenerationResult<'hir> { | |
| GenericsGenerationResult { | |
| generics: HirOrTyGenerics::Ty(generics), | |
| args_segment_id: HirId::INVALID, | |
| use_for_sig_inheritance: false, | |
| } | |
| } | |
| } | |
| impl<'hir> GenericsGenerationResults<'hir> { | |
| pub(super) fn all_params(&self) -> impl Iterator<Item = hir::GenericParam<'hir>> { | |
| let parent = self.parent.generics.hir_generics_or_empty().params; | |
| let child = self.child.generics.hir_generics_or_empty().params; | |
| // Order generics, first we have parent and child lifetimes, | |
| // then parent and child types and consts. | |
| // `generics_of` in `rustc_hir_analysis` will order them anyway, | |
| // however we want the order to be consistent in HIR too. | |
| parent | |
| .iter() | |
| .filter(|p| p.is_lifetime()) | |
| .chain(child.iter().filter(|p| p.is_lifetime())) | |
| .chain(parent.iter().filter(|p| !p.is_lifetime())) | |
| .chain(child.iter().filter(|p| !p.is_lifetime())) | |
| .copied() | |
| } | |
| /// As we add hack predicates(`'a: 'a`) for all lifetimes (see `uplift_delegation_generic_params` | |
| /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics. | |
| /// Those predicates will not affect resulting predicate inheritance and folding | |
| /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature. | |
| pub(super) fn all_predicates(&self) -> impl Iterator<Item = hir::WherePredicate<'hir>> { | |
| self.parent | |
| .generics | |
| .hir_generics_or_empty() | |
| .predicates | |
| .into_iter() | |
| .chain(self.child.generics.hir_generics_or_empty().predicates) | |
| .copied() | |
| } | |
| } | |
| impl<'hir> LoweringContext<'_, 'hir> { | |
| pub(super) fn uplift_delegation_generics( | |
| &mut self, | |
| delegation: &Delegation, | |
| sig_id: DefId, | |
| ) -> GenericsGenerationResults<'hir> { | |
| let delegation_parent_kind = self.tcx.def_kind(self.tcx.local_parent(self.owner.def_id)); | |
| let segments = &delegation.path.segments; | |
| let len = segments.len(); | |
| let get_user_args = |idx: usize| -> Option<&AngleBracketedArgs> { | |
| let segment = &segments[idx]; | |
| let Some(args) = segment.args.as_ref() else { return None }; | |
| let GenericArgs::AngleBracketed(args) = args else { | |
| self.tcx.dcx().span_delayed_bug( | |
| segment.span(), | |
| "expected angle-bracketed generic args in delegation segment", | |
| ); | |
| return None; | |
| }; | |
| // Treat empty args `reuse foo::<> as bar` as `reuse foo as bar`, | |
| // the same logic applied when we call function `fn f<T>(t: T)` | |
| // like that `f::<>(())`, in HIR no `<>` will be generated. | |
| (!args.args.is_empty()).then(|| args) | |
| }; | |
| let sig_params = &self.tcx.generics_of(sig_id).own_params[..]; | |
| // If we are in trait impl always generate function whose generics matches | |
| // those that are defined in trait. | |
| if matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }) { | |
| // Considering parent generics, during signature inheritance | |
| // we will take those args that are in trait impl header trait ref. | |
| let parent = | |
| DelegationGenerics { data: vec![], pos: GenericsPosition::Child, trait_impl: true }; | |
| let parent = GenericsGenerationResult::new(parent); | |
| let child = DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, true); | |
| let child = GenericsGenerationResult::new(child); | |
| return GenericsGenerationResults { parent, child, self_ty_propagation_kind: None }; | |
| } | |
| let delegation_in_free_ctx = | |
| !matches!(delegation_parent_kind, DefKind::Trait | DefKind::Impl { .. }); | |
| let sig_parent = self.tcx.parent(sig_id); | |
| let sig_in_trait = matches!(self.tcx.def_kind(sig_parent), DefKind::Trait); | |
| let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait; | |
| let qself_is_infer = | |
| delegation.qself.as_ref().is_some_and(|qself| qself.ty.is_maybe_parenthesised_infer()); | |
| let qself_is_none = delegation.qself.is_none(); | |
| let generate_self = free_to_trait_delegation && (qself_is_none || qself_is_infer); | |
| let can_add_generics_to_parent = len >= 2 | |
| && self.get_resolution_id(segments[len - 2].id).is_some_and(|def_id| { | |
| matches!(self.tcx.def_kind(def_id), DefKind::Trait | DefKind::TraitAlias) | |
| }); | |
| let parent_generics = if can_add_generics_to_parent { | |
| let sig_parent_params = &self.tcx.generics_of(sig_parent).own_params; | |
| if let Some(args) = get_user_args(len - 2) { | |
| DelegationGenerics { | |
| data: self.create_slots_from_args( | |
| args, | |
| &sig_parent_params[usize::from(!generate_self)..], | |
| generate_self, | |
| ), | |
| pos: GenericsPosition::Parent, | |
| trait_impl: false, | |
| } | |
| } else { | |
| DelegationGenerics::generate_all( | |
| &sig_parent_params[usize::from(!generate_self)..], | |
| GenericsPosition::Parent, | |
| false, | |
| ) | |
| } | |
| } else { | |
| DelegationGenerics { data: vec![], pos: GenericsPosition::Parent, trait_impl: false } | |
| }; | |
| let child_generics = if let Some(args) = get_user_args(len - 1) { | |
| let synth_params_index = | |
| sig_params.iter().position(|p| p.kind.is_synthetic()).unwrap_or(sig_params.len()); | |
| let mut slots = | |
| self.create_slots_from_args(args, &sig_params[..synth_params_index], false); | |
| for synth_param in &sig_params[synth_params_index..] { | |
| slots.push(GenericArgSlot::Generate(synth_param, None)); | |
| } | |
| DelegationGenerics { data: slots, pos: GenericsPosition::Child, trait_impl: false } | |
| } else { | |
| DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, false) | |
| }; | |
| GenericsGenerationResults { | |
| parent: GenericsGenerationResult::new(parent_generics), | |
| child: GenericsGenerationResult::new(child_generics), | |
| self_ty_propagation_kind: match free_to_trait_delegation { | |
| true => Some(match qself_is_none { | |
| true => hir::DelegationSelfTyPropagationKind::SelfParam, | |
| false => match qself_is_infer { | |
| true => hir::DelegationSelfTyPropagationKind::SelfParam, | |
| // HirId is filled during generic args propagation. | |
| false => hir::DelegationSelfTyPropagationKind::SelfTy(HirId::INVALID), | |
| }, | |
| }), | |
| false => None, | |
| }, | |
| } | |
| } | |
| /// Generates generic argument slots for user-specified `args` and | |
| /// generic `params` of the signature function. This function checks whether | |
| /// there are infers (`kw::UnderscoreLifetime` or `kw::Underscore`) in | |
| /// user-specified args, and if so we add `Generate` slot meaning we have to | |
| /// generate generic param for delegation and propagate it instead of this infer. | |
| /// We zip over user-specified args and signature generic params, so if there are more | |
| /// infers than generic params then we will not process all infers thus not generating | |
| /// more generic params then needed (anyway it is an error). | |
| fn create_slots_from_args( | |
| &self, | |
| args: &AngleBracketedArgs, | |
| params: &'hir [ty::GenericParamDef], | |
| add_first_self: bool, | |
| ) -> TyGenerics<'hir> { | |
| let mut slots = vec![]; | |
| if add_first_self { | |
| slots.push(GenericArgSlot::Generate(¶ms[0], None)); | |
| } | |
| let params = ¶ms[usize::from(add_first_self)..]; | |
| for (idx, (arg, param)) in args.args.iter().zip(params).enumerate() { | |
| let AngleBracketedArg::Arg(arg) = arg else { continue }; | |
| let is_infer = match arg { | |
| GenericArg::Lifetime(lt) => lt.ident.name == kw::UnderscoreLifetime, | |
| GenericArg::Type(ty) => ty.is_maybe_parenthesised_infer(), | |
| GenericArg::Const(_) => false, | |
| }; | |
| // If `'_` is used instead of `_` (or vice versa) we emit a meaningful | |
| // error instead of processing this infer or leaving it as is for signature | |
| // inheritance. | |
| if is_infer | |
| && matches!( | |
| (arg, ¶m.kind), | |
| ( | |
| GenericArg::Lifetime(_), | |
| GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } | |
| ) | ( | |
| GenericArg::Type(_) | GenericArg::Const(_), | |
| GenericParamDefKind::Lifetime { .. } | |
| ) | |
| ) | |
| { | |
| let (actual, expected) = if matches!(arg, GenericArg::Lifetime(..)) { | |
| (kw::UnderscoreLifetime, kw::Underscore) | |
| } else { | |
| (kw::Underscore, kw::UnderscoreLifetime) | |
| }; | |
| self.tcx.dcx().emit_err(DelegationInfersMismatch { | |
| span: arg.span(), | |
| actual, | |
| expected, | |
| }); | |
| } | |
| slots.push(match is_infer { | |
| true => GenericArgSlot::Generate(param, Some(idx)), | |
| false => GenericArgSlot::UserSpecified, | |
| }); | |
| } | |
| slots | |
| } | |
| fn uplift_delegation_generic_params( | |
| &mut self, | |
| span: Span, | |
| params: &[GenericArgSlot<&ty::GenericParamDef>], | |
| rename_self: bool, | |
| ) -> &'hir hir::Generics<'hir> { | |
| let params = self.arena.alloc_from_iter(params.iter().flat_map(|p| { | |
| let GenericArgSlot::Generate(p, _) = p else { return None }; | |
| let def_kind = match p.kind { | |
| GenericParamDefKind::Lifetime => DefKind::LifetimeParam, | |
| GenericParamDefKind::Type { .. } => DefKind::TyParam, | |
| GenericParamDefKind::Const { .. } => DefKind::ConstParam, | |
| }; | |
| // Rename Self generic param to This so it is properly propagated. | |
| // If the user will create a function `fn foo<Self>() {}` with generic | |
| // param "Self" then it will not be generated in HIR, the same thing | |
| // applies to traits, `trait Trait<Self> {}` will be represented as | |
| // `trait Trait {}` in HIR and "unexpected keyword `Self` in generic parameters" | |
| // error will be emitted. | |
| // Note that we do not rename `Self` to `This` after non-recursive reuse | |
| // from Trait, in this case the `Self` should not be propagated | |
| // (we rely that implicit `Self` generic param of a trait is named "Self") | |
| // and it is OK to have Self generic param generated during lowering. | |
| let param_name = | |
| if rename_self && p.name == kw::SelfUpper { sym::This } else { p.name }; | |
| let param_ident = Ident::new(param_name, span); | |
| let def_name = Some(param_ident.name); | |
| let node_id = self.next_node_id(); | |
| let def_id = self.create_def(node_id, def_name, def_kind, span); | |
| let kind = match p.kind { | |
| GenericParamDefKind::Lifetime => { | |
| hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } | |
| } | |
| GenericParamDefKind::Type { synthetic, .. } => { | |
| hir::GenericParamKind::Type { default: None, synthetic } | |
| } | |
| GenericParamDefKind::Const { .. } => { | |
| let hir_id = self.next_id(); | |
| let kind = hir::TyKind::InferDelegation(hir::InferDelegation::DefId(p.def_id)); | |
| hir::GenericParamKind::Const { | |
| ty: self.arena.alloc(hir::Ty { kind, hir_id, span }), | |
| default: None, | |
| } | |
| } | |
| }; | |
| // Important: we don't use `self.next_id()` as we want to execute | |
| // `lower_node_id` routine so param's id is added to `self.children`. | |
| let hir_id = self.lower_node_id(node_id); | |
| Some(hir::GenericParam { | |
| hir_id, | |
| colon_span: Some(span), | |
| def_id, | |
| kind, | |
| name: hir::ParamName::Plain(param_ident), | |
| pure_wrt_drop: p.pure_wrt_drop, | |
| source: hir::GenericParamSource::Generics, | |
| span, | |
| }) | |
| })); | |
| // HACK: for now we generate predicates such that all lifetimes are early bound, | |
| // we can not not generate early-bound lifetimes, but we can't know which of them | |
| // are late-bound at this level of compilation. | |
| let predicates = | |
| self.arena.alloc_from_iter(params.iter().filter_map(|p| { | |
| p.is_lifetime().then(|| self.generate_lifetime_predicate(p, span)) | |
| })); | |
| self.arena.alloc(hir::Generics { | |
| params, | |
| predicates, | |
| has_where_clause_predicates: false, | |
| where_clause_span: span, | |
| span, | |
| }) | |
| } | |
| fn generate_lifetime_predicate( | |
| &mut self, | |
| p: &hir::GenericParam<'hir>, | |
| span: Span, | |
| ) -> hir::WherePredicate<'hir> { | |
| let create_lifetime = |this: &mut Self| -> &'hir hir::Lifetime { | |
| this.arena.alloc(hir::Lifetime { | |
| hir_id: this.next_id(), | |
| ident: p.name.ident(), | |
| kind: hir::LifetimeKind::Param(p.def_id), | |
| source: hir::LifetimeSource::Path { angle_brackets: hir::AngleBrackets::Full }, | |
| syntax: hir::LifetimeSyntax::ExplicitBound, | |
| }) | |
| }; | |
| hir::WherePredicate { | |
| hir_id: self.next_id(), | |
| span, | |
| kind: self.arena.alloc(hir::WherePredicateKind::RegionPredicate( | |
| hir::WhereRegionPredicate { | |
| in_where_clause: true, | |
| lifetime: create_lifetime(self), | |
| bounds: self | |
| .arena | |
| .alloc_slice(&[hir::GenericBound::Outlives(create_lifetime(self))]), | |
| }, | |
| )), | |
| } | |
| } | |
| pub(super) fn create_generic_arg_path( | |
| &mut self, | |
| p: &hir::GenericParam<'hir>, | |
| ) -> hir::QPath<'hir> { | |
| let res = Res::Def( | |
| match p.kind { | |
| hir::GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, | |
| hir::GenericParamKind::Type { .. } => DefKind::TyParam, | |
| hir::GenericParamKind::Const { .. } => DefKind::ConstParam, | |
| }, | |
| p.def_id.to_def_id(), | |
| ); | |
| hir::QPath::Resolved( | |
| None, | |
| self.arena.alloc(hir::Path { | |
| segments: self.arena.alloc_slice(&[hir::PathSegment { | |
| args: None, | |
| hir_id: self.next_id(), | |
| ident: p.name.ident(), | |
| infer_args: false, | |
| res, | |
| }]), | |
| res, | |
| span: p.span, | |
| }), | |
| ) | |
| } | |
| } |