| //! Support inheriting generic parameters and predicates for function delegation. | |
| //! | |
| //! For more information about delegation design, see the tracking issue #118212. | |
| use std::debug_assert_matches; | |
| use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | |
| use rustc_hir::def::DefKind; | |
| use rustc_hir::def_id::{DefId, LocalDefId}; | |
| use rustc_hir::{DelegationSelfTyPropagationKind, PathSegment}; | |
| use rustc_middle::ty::{ | |
| self, EarlyBinder, RegionExt, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, | |
| TypeVisitableExt, | |
| }; | |
| use rustc_span::{ErrorGuaranteed, Span, kw}; | |
| use crate::collect::ItemCtxt; | |
| use crate::hir_ty_lowering::HirTyLowerer; | |
| type RemapTable = FxHashMap<u32, u32>; | |
| struct ParamIndexRemapper<'tcx> { | |
| tcx: TyCtxt<'tcx>, | |
| remap_table: RemapTable, | |
| delegation_parent_consts: FxHashSet<ty::ParamConst>, | |
| } | |
| impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamIndexRemapper<'tcx> { | |
| fn cx(&self) -> TyCtxt<'tcx> { | |
| self.tcx | |
| } | |
| fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { | |
| if !ty.has_param() { | |
| return ty; | |
| } | |
| if let ty::Param(param) = ty.kind() | |
| && let Some(index) = self.remap_table.get(¶m.index) | |
| { | |
| return Ty::new_param(self.tcx, *index, param.name); | |
| } | |
| ty.super_fold_with(self) | |
| } | |
| fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { | |
| if let ty::ReEarlyParam(param) = r.kind() | |
| && let Some(index) = self.remap_table.get(¶m.index).copied() | |
| { | |
| return ty::Region::new_early_param( | |
| self.tcx, | |
| ty::EarlyParamRegion { index, name: param.name }, | |
| ); | |
| } | |
| r | |
| } | |
| fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { | |
| if let ty::ConstKind::Param(param) = ct.kind() | |
| && let Some(idx) = self.remap_table.get(¶m.index) | |
| { | |
| let param = ty::ParamConst::new(*idx, param.name); | |
| return ty::Const::new_param(self.tcx, param); | |
| } | |
| ct.super_fold_with(self) | |
| } | |
| } | |
| #[derive(Debug)] | |
| enum SelfPositionKind { | |
| AfterLifetimes(Option<DelegationSelfTyPropagationKind>), | |
| Zero, | |
| None, | |
| } | |
| fn create_self_position_kind( | |
| tcx: TyCtxt<'_>, | |
| delegation_id: LocalDefId, | |
| sig_id: DefId, | |
| ) -> SelfPositionKind { | |
| match (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)) { | |
| (FnKind::AssocInherentImpl, FnKind::AssocTrait) | |
| | (FnKind::AssocTraitImpl, FnKind::AssocTrait) | |
| | (FnKind::AssocTrait, FnKind::AssocTrait) | |
| | (FnKind::AssocTrait, FnKind::Free) => SelfPositionKind::Zero, | |
| (FnKind::Free, FnKind::AssocTrait) => { | |
| let kind = tcx.hir_delegation_info(delegation_id).self_ty_propagation_kind; | |
| SelfPositionKind::AfterLifetimes(kind) | |
| } | |
| _ => SelfPositionKind::None, | |
| } | |
| } | |
| #[derive(Clone, Copy, Debug, PartialEq)] | |
| enum FnKind { | |
| Free, | |
| AssocInherentImpl, | |
| AssocTrait, | |
| AssocTraitImpl, | |
| } | |
| fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: impl Into<DefId>) -> FnKind { | |
| let def_id = def_id.into(); | |
| debug_assert_matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn); | |
| let parent = tcx.parent(def_id); | |
| match tcx.def_kind(parent) { | |
| DefKind::Trait => FnKind::AssocTrait, | |
| DefKind::Impl { of_trait: true } => FnKind::AssocTraitImpl, | |
| DefKind::Impl { of_trait: false } => FnKind::AssocInherentImpl, | |
| _ => FnKind::Free, | |
| } | |
| } | |
| /// Given the current context(caller and callee `FnKind`), it specifies | |
| /// the policy of predicates and generic parameters inheritance. | |
| #[derive(Clone, Copy, Debug, PartialEq)] | |
| enum InheritanceKind { | |
| /// Copying all predicates and parameters, including those of the parent | |
| /// container. | |
| /// | |
| /// Boolean value defines whether the `Self` parameter or `Self: Trait` | |
| /// predicate are copied. It's always equal to `false` except when | |
| /// delegating from a free function to a trait method. | |
| /// | |
| /// FIXME(fn_delegation): This often leads to type inference | |
| /// errors. Support providing generic arguments or restrict use sites. | |
| WithParent(bool), | |
| /// The trait implementation should be compatible with the original trait. | |
| /// Therefore, for trait implementations only the method's own parameters | |
| /// and predicates are copied. | |
| Own, | |
| } | |
| /// Maps sig generics into generic args of delegation. Delegation generics has the following pattern: | |
| /// | |
| /// [SELF | maybe self in the beginning] | |
| /// [PARENT | args of delegation parent] | |
| /// [SIG PARENT LIFETIMES] | |
| /// [SIG LIFETIMES] | |
| /// [SELF | maybe self after lifetimes, when we reuse trait fn in free context] | |
| /// [SIG PARENT TYPES/CONSTS] | |
| /// [SIG TYPES/CONSTS] | |
| fn create_mapping<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| sig_id: DefId, | |
| def_id: LocalDefId, | |
| ) -> FxHashMap<u32, u32> { | |
| let mut mapping: FxHashMap<u32, u32> = Default::default(); | |
| let self_pos_kind = create_self_position_kind(tcx, def_id, sig_id); | |
| let is_self_at_zero = matches!(self_pos_kind, SelfPositionKind::Zero); | |
| // Is self at zero? If so insert mapping, self in sig parent is always at 0. | |
| if is_self_at_zero { | |
| mapping.insert(0, 0); | |
| } | |
| let mut args_index = 0; | |
| args_index += is_self_at_zero as usize; | |
| args_index += get_delegation_parent_args_count_without_self(tcx, def_id, sig_id); | |
| let sig_generics = tcx.generics_of(sig_id); | |
| let process_sig_parent_generics = matches!(fn_kind(tcx, sig_id), FnKind::AssocTrait); | |
| if process_sig_parent_generics { | |
| for i in (sig_generics.has_self as usize)..sig_generics.parent_count { | |
| let param = sig_generics.param_at(i, tcx); | |
| if !param.kind.is_ty_or_const() { | |
| mapping.insert(param.index, args_index as u32); | |
| args_index += 1; | |
| } | |
| } | |
| } | |
| for param in &sig_generics.own_params { | |
| if !param.kind.is_ty_or_const() { | |
| mapping.insert(param.index, args_index as u32); | |
| args_index += 1; | |
| } | |
| } | |
| // If self after lifetimes insert mapping, relying that self is at 0 in sig parent. | |
| // If self ty is propagated (meaning there is no generic param `Self`), the specified | |
| // self ty will be inserted in args in `create_generic_args`. | |
| if matches!(self_pos_kind, SelfPositionKind::AfterLifetimes { .. }) { | |
| mapping.insert(0, args_index as u32); | |
| args_index += 1; | |
| } | |
| if process_sig_parent_generics { | |
| for i in (sig_generics.has_self as usize)..sig_generics.parent_count { | |
| let param = sig_generics.param_at(i, tcx); | |
| if param.kind.is_ty_or_const() { | |
| mapping.insert(param.index, args_index as u32); | |
| args_index += 1; | |
| } | |
| } | |
| } | |
| for param in &sig_generics.own_params { | |
| if param.kind.is_ty_or_const() { | |
| mapping.insert(param.index, args_index as u32); | |
| args_index += 1; | |
| } | |
| } | |
| mapping | |
| } | |
| fn get_delegation_parent_args_count_without_self<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| delegation_id: LocalDefId, | |
| sig_id: DefId, | |
| ) -> usize { | |
| let delegation_parent_args_count = tcx.generics_of(delegation_id).parent_count; | |
| match (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)) { | |
| (FnKind::Free, FnKind::Free) | |
| | (FnKind::Free, FnKind::AssocTrait) | |
| | (FnKind::AssocTraitImpl, FnKind::AssocTrait) => 0, | |
| (FnKind::AssocInherentImpl, FnKind::Free) | |
| | (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { | |
| delegation_parent_args_count /* No Self in AssocInherentImpl */ | |
| } | |
| (FnKind::AssocTrait, FnKind::Free) | (FnKind::AssocTrait, FnKind::AssocTrait) => { | |
| delegation_parent_args_count - 1 /* Without Self */ | |
| } | |
| // For trait impl's `sig_id` is always equal to the corresponding trait method. | |
| // For inherent methods delegation is not yet supported. | |
| (FnKind::AssocTraitImpl, _) | |
| | (_, FnKind::AssocTraitImpl) | |
| | (_, FnKind::AssocInherentImpl) => unreachable!(), | |
| } | |
| } | |
| fn get_parent_and_inheritance_kind<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| sig_id: DefId, | |
| ) -> (Option<DefId>, InheritanceKind) { | |
| match (fn_kind(tcx, def_id), fn_kind(tcx, sig_id)) { | |
| (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => { | |
| (None, InheritanceKind::WithParent(true)) | |
| } | |
| (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { | |
| (Some(tcx.parent(def_id.to_def_id())), InheritanceKind::Own) | |
| } | |
| (FnKind::AssocInherentImpl, FnKind::AssocTrait) | |
| | (FnKind::AssocTrait, FnKind::AssocTrait) | |
| | (FnKind::AssocInherentImpl, FnKind::Free) | |
| | (FnKind::AssocTrait, FnKind::Free) => { | |
| (Some(tcx.parent(def_id.to_def_id())), InheritanceKind::WithParent(false)) | |
| } | |
| // For trait impl's `sig_id` is always equal to the corresponding trait method. | |
| // For inherent methods delegation is not yet supported. | |
| (FnKind::AssocTraitImpl, _) | |
| | (_, FnKind::AssocTraitImpl) | |
| | (_, FnKind::AssocInherentImpl) => unreachable!(), | |
| } | |
| } | |
| fn get_delegation_self_ty<'tcx>(tcx: TyCtxt<'tcx>, delegation_id: LocalDefId) -> Option<Ty<'tcx>> { | |
| let sig_id = tcx.hir_opt_delegation_sig_id(delegation_id).expect("Delegation must have sig_id"); | |
| let (caller_kind, callee_kind) = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)); | |
| match (caller_kind, callee_kind) { | |
| (FnKind::Free, FnKind::AssocTrait) | |
| | (FnKind::AssocInherentImpl, FnKind::Free) | |
| | (FnKind::Free, FnKind::Free) | |
| | (FnKind::AssocTrait, FnKind::Free) | |
| | (FnKind::AssocTrait, FnKind::AssocTrait) => { | |
| match create_self_position_kind(tcx, delegation_id, sig_id) { | |
| SelfPositionKind::None => None, | |
| SelfPositionKind::AfterLifetimes(propagation_kind) => { | |
| Some(match propagation_kind { | |
| Some(kind) => match kind { | |
| DelegationSelfTyPropagationKind::SelfTy(self_ty_id) => { | |
| let ctx = ItemCtxt::new(tcx, delegation_id); | |
| ctx.lower_ty(tcx.hir_node(self_ty_id).expect_ty()) | |
| } | |
| DelegationSelfTyPropagationKind::SelfParam => { | |
| let index = tcx.generics_of(delegation_id).own_counts().lifetimes; | |
| Ty::new_param(tcx, index as u32, kw::SelfUpper) | |
| } | |
| }, | |
| None => Ty::new_error_with_message( | |
| tcx, | |
| tcx.def_span(delegation_id), | |
| "self propagation kind must be specified for `AfterLifetimes` variant", | |
| ), | |
| }) | |
| } | |
| SelfPositionKind::Zero => Some(Ty::new_param(tcx, 0, kw::SelfUpper)), | |
| } | |
| } | |
| (FnKind::AssocTraitImpl, FnKind::AssocTrait) | |
| | (FnKind::AssocInherentImpl, FnKind::AssocTrait) => Some( | |
| tcx.type_of(tcx.local_parent(delegation_id)).instantiate_identity().skip_norm_wip(), | |
| ), | |
| // For trait impl's `sig_id` is always equal to the corresponding trait method. | |
| // For inherent methods delegation is not yet supported. | |
| (FnKind::AssocTraitImpl, _) | |
| | (_, FnKind::AssocTraitImpl) | |
| | (_, FnKind::AssocInherentImpl) => unreachable!(), | |
| } | |
| } | |
| /// Creates generic arguments for further delegation signature and predicates instantiation. | |
| /// Arguments can be user-specified (in this case they are in `parent_args` and `child_args`) | |
| /// or propagated. User can specify either both `parent_args` and `child_args`, one of them or none, | |
| /// that is why we firstly create generic arguments from generic params and then adjust them with | |
| /// user-specified args. | |
| /// | |
| /// The order of produced list is important, it must be of this pattern: | |
| /// | |
| /// [SELF | maybe self in the beginning] | |
| /// [PARENT | args of delegation parent] | |
| /// [SIG PARENT LIFETIMES] <- `lifetimes_end_pos` | |
| /// [SIG LIFETIMES] | |
| /// [SELF | maybe self after lifetimes, when we reuse trait fn in free context] | |
| /// [SIG PARENT TYPES/CONSTS] | |
| /// [SIG TYPES/CONSTS] | |
| fn create_generic_args<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| sig_id: DefId, | |
| delegation_id: LocalDefId, | |
| mut parent_args: &[ty::GenericArg<'tcx>], | |
| mut child_args: &[ty::GenericArg<'tcx>], | |
| ) -> (Vec<ty::GenericArg<'tcx>>, &'tcx [ty::GenericArg<'tcx>]) { | |
| let delegation_generics = tcx.generics_of(delegation_id); | |
| let delegation_args = ty::GenericArgs::identity_for_item(tcx, delegation_id); | |
| let real_args_count = delegation_args.len() - delegation_generics.own_synthetic_params_count(); | |
| let synth_args = &delegation_args[real_args_count..]; | |
| let mut delegation_parent_args = | |
| &delegation_args[delegation_generics.has_self as usize..delegation_generics.parent_count]; | |
| let delegation_args = &delegation_args[delegation_generics.parent_count..]; | |
| let kinds = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)); | |
| if matches!(kinds, (FnKind::AssocTraitImpl, FnKind::AssocTrait)) { | |
| // Special case, as user specifies Trait args in trait impl header, we want to treat | |
| // them as parent args. We always generate a function whose generics match | |
| // child generics in trait. | |
| let parent = tcx.local_parent(delegation_id); | |
| parent_args = | |
| tcx.impl_trait_header(parent).trait_ref.instantiate_identity().skip_norm_wip().args; | |
| child_args = | |
| &delegation_args[delegation_args.len() - delegation_generics.own_params.len()..]; | |
| delegation_parent_args = &[]; | |
| } | |
| let self_type = get_delegation_self_ty(tcx, delegation_id).map(|t| t.into()); | |
| // Remove `Self` from parent args (it is always at the `0th` index) as it is | |
| // added manually. | |
| if self_type.is_some() && !parent_args.is_empty() { | |
| parent_args = &parent_args[1..]; | |
| } | |
| let (zero_self, after_lifetimes_self) = | |
| match create_self_position_kind(tcx, delegation_id, sig_id) { | |
| SelfPositionKind::AfterLifetimes(_) => { | |
| assert!(self_type.is_some()); | |
| (None, self_type) | |
| } | |
| SelfPositionKind::Zero => { | |
| assert!(self_type.is_some()); | |
| (self_type, None) | |
| } | |
| SelfPositionKind::None => (None, None), | |
| }; | |
| let zero_self = zero_self.as_ref().into_iter(); | |
| let after_lifetimes_self = after_lifetimes_self.as_ref().into_iter(); | |
| let args = zero_self | |
| .chain(delegation_parent_args) | |
| .chain(parent_args.iter().filter(|a| a.as_region().is_some())) | |
| .chain(child_args.iter().filter(|a| a.as_region().is_some())) | |
| .chain(after_lifetimes_self) | |
| .chain(parent_args.iter().filter(|a| a.as_region().is_none())) | |
| .chain(child_args.iter().filter(|a| a.as_region().is_none())) | |
| .chain(synth_args) | |
| .copied() | |
| .collect::<Vec<_>>(); | |
| (args, delegation_parent_args) | |
| } | |
| pub(crate) fn inherit_clauses_for_delegation_item<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| sig_id: DefId, | |
| ) -> ty::GenericClauses<'tcx> { | |
| struct ClausesCollector<'tcx> { | |
| tcx: TyCtxt<'tcx>, | |
| clauses: Vec<(ty::Clause<'tcx>, Span)>, | |
| args: Vec<ty::GenericArg<'tcx>>, | |
| folder: ParamIndexRemapper<'tcx>, | |
| filter_self_clauses: bool, | |
| } | |
| impl<'tcx> ClausesCollector<'tcx> { | |
| fn with_own_clauses( | |
| mut self, | |
| f: impl Fn(DefId) -> ty::GenericClauses<'tcx>, | |
| def_id: DefId, | |
| ) -> Self { | |
| let clauses = f(def_id); | |
| let args = self.args.as_slice(); | |
| for clause in clauses.clauses { | |
| // If self ty is specified then there will be no generic param `Self`, | |
| // so we do not need its clauses. | |
| if self.filter_self_clauses | |
| && let Some(trait_clause) = clause.0.as_trait_clause() | |
| // Rely that `Self` has zero index. | |
| && trait_clause.self_ty().skip_binder().is_param(0) | |
| { | |
| continue; | |
| } | |
| // If we have a constant in parent or child args that came from delegation | |
| // parent: | |
| // ```rust | |
| // trait Trait<T, const B: bool> { /* .. */} | |
| // impl<const N: usize> S<N> { | |
| // reuse Trait::<S<N>, N>::foo; | |
| // } | |
| // ``` | |
| // Then if we inherit const clause from `Trait` then we end up with | |
| // two `ConstArgHasType` for `N` constant: | |
| // 1) ConstArgHasType(N/#0, bool) from `Trait` | |
| // 2) ConstArgHasType(N/#0, usize) from delegation parent | |
| // So in case the constant came from delegation parent we will not inherit | |
| // ConstArgHasType from signature. | |
| // The check is so complicated because we build generic args for signature | |
| // and clauses inheritance, for the example above it will be | |
| // `args = [S<N/#0>, N/#0, S<N/#0>, N/#0]`, where | |
| // args[0] - Self type, args[1] - delegation parent const, args[2] - first | |
| // arg of callee path, args[3] - second arg of callee path. | |
| // When processing clause ConstArgHasType(B/#2, bool) | |
| // from delegation signature (`Trait::foo`), we need to map `B/#2` into some | |
| // arg from `args`. The mapping which is built by `create_mapping` function is: | |
| // `{0: 0, 2: 3, 1: 2}`, so as `B/#2` has index `2` it is mapped into third | |
| // arg from `args` - `N/#0`. After we obtained mapped const param, we check if | |
| // it came from delegation parent, and if so we do not process its `ConstArgHasType` | |
| // clause. | |
| // (Issue #158675). | |
| if let ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) = | |
| clause.0.as_predicate().fold_with(&mut self.folder).kind().skip_binder() | |
| { | |
| let unnorm_const = EarlyBinder::bind(self.tcx, ct).instantiate(self.tcx, args); | |
| if let ty::ConstKind::Param(param) = unnorm_const.skip_norm_wip().kind() | |
| && self.folder.delegation_parent_consts.contains(¶m) | |
| { | |
| continue; | |
| } | |
| } | |
| let new_clause = clause.0.fold_with(&mut self.folder); | |
| self.clauses.push(( | |
| EarlyBinder::bind(self.tcx, new_clause) | |
| .instantiate(self.tcx, args) | |
| .skip_norm_wip(), | |
| clause.1, | |
| )); | |
| } | |
| self | |
| } | |
| fn with_clauses( | |
| mut self, | |
| f: impl Fn(DefId) -> ty::GenericClauses<'tcx> + Copy, | |
| def_id: DefId, | |
| ) -> Self { | |
| let preds = f(def_id); | |
| if let Some(parent_def_id) = preds.parent { | |
| self = self.with_own_clauses(f, parent_def_id); | |
| } | |
| self.with_own_clauses(f, def_id) | |
| } | |
| } | |
| let (parent_args, child_args) = tcx.delegation_user_specified_args(def_id); | |
| let (folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args); | |
| let self_pos_kind = create_self_position_kind(tcx, def_id, sig_id); | |
| let filter_self_clauses = matches!( | |
| self_pos_kind, | |
| SelfPositionKind::AfterLifetimes(Some(DelegationSelfTyPropagationKind::SelfTy(..))) | |
| ); | |
| let collector = ClausesCollector { tcx, clauses: vec![], args, folder, filter_self_clauses }; | |
| let (parent, inh_kind) = get_parent_and_inheritance_kind(tcx, def_id, sig_id); | |
| // `explicit_clauses_of` is used here to avoid copying `Self: Trait` clause. | |
| // Note: `clauses_of` query can also add inferred outlives clauses, but that | |
| // is not the case here as `sig_id` is either a trait or a function. | |
| let clauses = match inh_kind { | |
| InheritanceKind::WithParent(false) => { | |
| collector.with_clauses(|def_id| tcx.explicit_clauses_of(def_id), sig_id) | |
| } | |
| InheritanceKind::WithParent(true) => { | |
| collector.with_clauses(|def_id| tcx.clauses_of(def_id), sig_id) | |
| } | |
| InheritanceKind::Own => collector.with_own_clauses(|def_id| tcx.clauses_of(def_id), sig_id), | |
| } | |
| .clauses; | |
| ty::GenericClauses { parent, clauses: tcx.arena.alloc_from_iter(clauses) } | |
| } | |
| fn create_folder_and_args<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| sig_id: DefId, | |
| parent_args: &'tcx [ty::GenericArg<'tcx>], | |
| child_args: &'tcx [ty::GenericArg<'tcx>], | |
| ) -> (ParamIndexRemapper<'tcx>, Vec<ty::GenericArg<'tcx>>) { | |
| let (args, delegation_parent_args) = | |
| create_generic_args(tcx, sig_id, def_id, parent_args, child_args); | |
| let remap_table = create_mapping(tcx, sig_id, def_id); | |
| let delegation_parent_consts = delegation_parent_args | |
| .iter() | |
| .filter_map(|a| { | |
| a.as_const().and_then(|c| { | |
| if let ty::ConstKind::Param(param) = c.kind() { Some(param) } else { None } | |
| }) | |
| }) | |
| .collect(); | |
| (ParamIndexRemapper { tcx, remap_table, delegation_parent_consts }, args) | |
| } | |
| fn check_constraints<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| sig_id: DefId, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let mut ret = Ok(()); | |
| let mut emit = |descr| { | |
| ret = Err(tcx.dcx().emit_err(crate::diagnostics::UnsupportedDelegation { | |
| span: tcx.def_span(def_id), | |
| descr, | |
| callee_span: tcx.def_span(sig_id), | |
| })); | |
| }; | |
| if tcx.fn_sig(sig_id).skip_binder().skip_binder().c_variadic() { | |
| // See issue #127443 for explanation. | |
| emit("delegation to C-variadic functions is not allowed"); | |
| } | |
| ret | |
| } | |
| pub(crate) fn inherit_sig_for_delegation_item<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| ) -> &'tcx [Ty<'tcx>] { | |
| let sig_id = tcx.hir_opt_delegation_sig_id(def_id).expect("Delegation must have sig_id"); | |
| let caller_sig = tcx.fn_sig(sig_id); | |
| if let Err(err) = check_constraints(tcx, def_id, sig_id) { | |
| let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1; | |
| let err_type = Ty::new_error(tcx, err); | |
| return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type)); | |
| } | |
| let (parent_args, child_args) = tcx.delegation_user_specified_args(def_id); | |
| let (mut folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args); | |
| let caller_sig = EarlyBinder::bind(tcx, caller_sig.skip_binder().fold_with(&mut folder)); | |
| let sig = caller_sig.instantiate(tcx, args.as_slice()).skip_binder(); | |
| let sig_iter = sig.inputs().iter().cloned().chain(std::iter::once(sig.output())); | |
| tcx.arena.alloc_from_iter(sig_iter) | |
| } | |
| // Creates user-specified generic arguments from delegation path, | |
| // they will be used during delegation signature and predicates inheritance. | |
| // Example: reuse Trait::<'static, i32, 1>::foo::<A, B> | |
| // we want to extract [Self, 'static, i32, 1] for parent and [A, B] for child. | |
| pub(crate) fn delegation_user_specified_args<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| delegation_id: LocalDefId, | |
| ) -> (&'tcx [ty::GenericArg<'tcx>], &'tcx [ty::GenericArg<'tcx>]) { | |
| let info = tcx.hir_delegation_info(delegation_id); | |
| let get_segment = |hir_id| -> Option<(&'tcx PathSegment<'tcx>, DefId)> { | |
| let segment = tcx.hir_node(hir_id).expect_path_segment(); | |
| segment.res.opt_def_id().map(|def_id| (segment, def_id)) | |
| }; | |
| let ctx = ItemCtxt::new_for_delegation(tcx, delegation_id); | |
| let lowerer = ctx.lowerer(); | |
| let parent_args = info | |
| .parent_seg_id_for_sig | |
| .and_then(get_segment) | |
| .filter(|(_, def_id)| matches!(tcx.def_kind(*def_id), DefKind::Trait)) | |
| .map(|(segment, def_id)| { | |
| let self_ty = get_delegation_self_ty(tcx, delegation_id); | |
| lowerer | |
| .lower_generic_args_of_path(segment.ident.span, def_id, &[], segment, self_ty) | |
| .0 | |
| .as_slice() | |
| }); | |
| let child_args = info | |
| .child_seg_id_for_sig | |
| .and_then(get_segment) | |
| .filter(|(_, def_id)| matches!(tcx.def_kind(*def_id), DefKind::Fn | DefKind::AssocFn)) | |
| .map(|(segment, def_id)| { | |
| let parent_args = if let Some(parent_args) = parent_args { | |
| parent_args | |
| } else { | |
| let parent = tcx.parent(def_id); | |
| if matches!(tcx.def_kind(parent), DefKind::Trait) { | |
| ty::GenericArgs::identity_for_item(tcx, parent).as_slice() | |
| } else { | |
| &[] | |
| } | |
| }; | |
| let args = lowerer | |
| .lower_generic_args_of_path(segment.ident.span, def_id, parent_args, segment, None) | |
| .0; | |
| let synth_params_count = tcx.generics_of(def_id).own_synthetic_params_count(); | |
| &args[parent_args.len()..args.len() - synth_params_count] | |
| }); | |
| (parent_args.unwrap_or_default(), child_args.unwrap_or_default()) | |
| } |