|
| 1 | +use rustc_data_structures::fx::FxHashMap; |
| 2 | +use rustc_hir::def::DefKind; |
| 3 | +use rustc_hir::def_id::{DefId, LocalDefId}; |
| 4 | +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; |
| 5 | +use rustc_middle::ty::{self, Ty, TyCtxt}; |
| 6 | +use rustc_span::ErrorGuaranteed; |
| 7 | + |
| 8 | +type RemapTable = FxHashMap<u32, u32>; |
| 9 | + |
| 10 | +struct IndicesFolder<'tcx, 'a> { |
| 11 | + tcx: TyCtxt<'tcx>, |
| 12 | + remap_table: &'a RemapTable, |
| 13 | +} |
| 14 | + |
| 15 | +impl<'tcx, 'a> TypeFolder<TyCtxt<'tcx>> for IndicesFolder<'tcx, 'a> { |
| 16 | + fn interner(&self) -> TyCtxt<'tcx> { |
| 17 | + self.tcx |
| 18 | + } |
| 19 | + |
| 20 | + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { |
| 21 | + if let ty::Param(param) = ty.kind() { |
| 22 | + return Ty::new_param(self.tcx, self.remap_table[¶m.index], param.name); |
| 23 | + } |
| 24 | + ty.super_fold_with(self) |
| 25 | + } |
| 26 | + |
| 27 | + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { |
| 28 | + if let ty::ReEarlyParam(param) = r.kind() { |
| 29 | + return ty::Region::new_early_param( |
| 30 | + self.tcx, |
| 31 | + ty::EarlyParamRegion { index: self.remap_table[¶m.index], name: param.name }, |
| 32 | + ); |
| 33 | + } |
| 34 | + r |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +#[derive(Clone, Copy, Debug, PartialEq)] |
| 39 | +enum FnKind { |
| 40 | + Free, |
| 41 | + AssocInherentImpl, |
| 42 | + AssocTrait, |
| 43 | + AssocTraitImpl, |
| 44 | +} |
| 45 | + |
| 46 | +fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind { |
| 47 | + debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn)); |
| 48 | + |
| 49 | + let parent = tcx.parent(def_id); |
| 50 | + match tcx.def_kind(parent) { |
| 51 | + DefKind::Trait => FnKind::AssocTrait, |
| 52 | + DefKind::Impl { of_trait: true } => FnKind::AssocTraitImpl, |
| 53 | + DefKind::Impl { of_trait: false } => FnKind::AssocInherentImpl, |
| 54 | + _ => FnKind::Free, |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +// Lifetime parameters must be declared before type and const parameters. |
| 59 | +// Therefore, When delegating from a free function to a associated function, |
| 60 | +// generic parameters need to be reordered: |
| 61 | +// |
| 62 | +// trait Trait<'a, A> { |
| 63 | +// fn foo<'b, B>(...) {...} |
| 64 | +// } |
| 65 | +// |
| 66 | +// reuse Trait::foo; |
| 67 | +// desugaring: |
| 68 | +// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) { |
| 69 | +// Trait::foo(...) |
| 70 | +// } |
| 71 | +fn create_remap_table<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, sig_id: DefId) -> RemapTable { |
| 72 | + let caller_generics = tcx.generics_of(def_id); |
| 73 | + let callee_generics = tcx.generics_of(sig_id); |
| 74 | + let mut remap_table: RemapTable = FxHashMap::default(); |
| 75 | + for caller_param in &caller_generics.own_params { |
| 76 | + let callee_index = callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap(); |
| 77 | + remap_table.insert(callee_index, caller_param.index); |
| 78 | + } |
| 79 | + remap_table |
| 80 | +} |
| 81 | + |
| 82 | +pub(crate) fn inherit_generics_for_delegation_item<'tcx>( |
| 83 | + tcx: TyCtxt<'tcx>, |
| 84 | + def_id: LocalDefId, |
| 85 | + sig_id: DefId, |
| 86 | +) -> Option<ty::Generics> { |
| 87 | + let caller_kind = fn_kind(tcx, def_id.into()); |
| 88 | + let callee_kind = fn_kind(tcx, sig_id); |
| 89 | + |
| 90 | + // FIXME(fn_delegation): Support generics on associated delegation items. |
| 91 | + // Error was reported in `check_delegation_constraints`. |
| 92 | + match (caller_kind, callee_kind) { |
| 93 | + (FnKind::Free, _) => { |
| 94 | + let mut own_params = vec![]; |
| 95 | + |
| 96 | + let callee_generics = tcx.generics_of(sig_id); |
| 97 | + if let Some(parent_sig_id) = callee_generics.parent { |
| 98 | + let parent_sig_generics = tcx.generics_of(parent_sig_id); |
| 99 | + own_params.append(&mut parent_sig_generics.own_params.clone()); |
| 100 | + } |
| 101 | + own_params.append(&mut callee_generics.own_params.clone()); |
| 102 | + |
| 103 | + // lifetimes go first |
| 104 | + own_params.sort_by_key(|key| key.kind.is_ty_or_const()); |
| 105 | + |
| 106 | + for (idx, param) in own_params.iter_mut().enumerate() { |
| 107 | + param.index = idx as u32; |
| 108 | + // Default type parameters are not inherited: they are not allowed |
| 109 | + // in fn's. |
| 110 | + if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind { |
| 111 | + param.kind = ty::GenericParamDefKind::Type { has_default: false, synthetic } |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + let param_def_id_to_index = |
| 116 | + own_params.iter().map(|param| (param.def_id, param.index)).collect(); |
| 117 | + |
| 118 | + Some(ty::Generics { |
| 119 | + parent: None, |
| 120 | + parent_count: 0, |
| 121 | + own_params, |
| 122 | + param_def_id_to_index, |
| 123 | + has_self: false, |
| 124 | + has_late_bound_regions: callee_generics.has_late_bound_regions, |
| 125 | + host_effect_index: None, |
| 126 | + }) |
| 127 | + } |
| 128 | + _ => None, |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( |
| 133 | + tcx: TyCtxt<'tcx>, |
| 134 | + def_id: LocalDefId, |
| 135 | + sig_id: DefId, |
| 136 | +) -> Option<ty::GenericPredicates<'tcx>> { |
| 137 | + let caller_kind = fn_kind(tcx, def_id.into()); |
| 138 | + let callee_kind = fn_kind(tcx, sig_id); |
| 139 | + |
| 140 | + // FIXME(fn_delegation): Support generics on associated delegation items. |
| 141 | + // Error was reported in `check_delegation_constraints`. |
| 142 | + match (caller_kind, callee_kind) { |
| 143 | + (FnKind::Free, _) => { |
| 144 | + let mut predicates = vec![]; |
| 145 | + let callee_predicates = tcx.predicates_of(sig_id); |
| 146 | + if let Some(parent_sig_id) = callee_predicates.parent { |
| 147 | + let parent_sig_predicates = tcx.predicates_of(parent_sig_id); |
| 148 | + predicates.extend_from_slice(parent_sig_predicates.predicates); |
| 149 | + } |
| 150 | + predicates.extend_from_slice(callee_predicates.predicates); |
| 151 | + |
| 152 | + let remap_table = create_remap_table(tcx, def_id, sig_id); |
| 153 | + let mut folder = IndicesFolder { tcx, remap_table: &remap_table }; |
| 154 | + let predicates = predicates.fold_with(&mut folder); |
| 155 | + |
| 156 | + Some(ty::GenericPredicates { |
| 157 | + parent: None, |
| 158 | + predicates: tcx.arena.alloc_from_iter(predicates), |
| 159 | + }) |
| 160 | + } |
| 161 | + _ => None, |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +fn check_constraints<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { |
| 166 | + let mut ret = Ok(()); |
| 167 | + let sig_id = tcx.hir().delegation_sig_id(def_id); |
| 168 | + |
| 169 | + let mut emit = |descr| { |
| 170 | + ret = Err(tcx.dcx().emit_err(crate::errors::UnsupportedDelegation { |
| 171 | + span: tcx.def_span(def_id), |
| 172 | + descr, |
| 173 | + callee_span: tcx.def_span(sig_id), |
| 174 | + })); |
| 175 | + }; |
| 176 | + |
| 177 | + if let Some(local_sig_id) = sig_id.as_local() |
| 178 | + && tcx.hir().opt_delegation_sig_id(local_sig_id).is_some() |
| 179 | + { |
| 180 | + emit("recursive delegation"); |
| 181 | + } |
| 182 | + |
| 183 | + let caller_kind = fn_kind(tcx, def_id.into()); |
| 184 | + if caller_kind != FnKind::Free { |
| 185 | + let sig_generics = tcx.generics_of(sig_id); |
| 186 | + let parent = tcx.parent(def_id.into()); |
| 187 | + let parent_generics = tcx.generics_of(parent); |
| 188 | + |
| 189 | + let parent_is_trait = (tcx.def_kind(parent) == DefKind::Trait) as usize; |
| 190 | + let sig_has_self = sig_generics.has_self as usize; |
| 191 | + |
| 192 | + if sig_generics.count() > sig_has_self || parent_generics.count() > parent_is_trait { |
| 193 | + emit("early bound generics are not supported for associated delegation items"); |
| 194 | + } |
| 195 | + } |
| 196 | + |
| 197 | + ret |
| 198 | +} |
| 199 | + |
| 200 | +pub(crate) fn inherit_sig_for_delegation_item<'tcx>( |
| 201 | + tcx: TyCtxt<'tcx>, |
| 202 | + def_id: LocalDefId, |
| 203 | +) -> &'tcx [Ty<'tcx>] { |
| 204 | + let sig_id = tcx.hir().delegation_sig_id(def_id); |
| 205 | + let caller_sig = tcx.fn_sig(sig_id); |
| 206 | + if let Err(err) = check_constraints(tcx, def_id) { |
| 207 | + let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1; |
| 208 | + let err_type = Ty::new_error(tcx, err); |
| 209 | + return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type)); |
| 210 | + } |
| 211 | + |
| 212 | + let caller_kind = fn_kind(tcx, def_id.into()); |
| 213 | + let callee_kind = fn_kind(tcx, sig_id); |
| 214 | + |
| 215 | + // FIXME(fn_delegation): Support generics on associated delegation items. |
| 216 | + // Error was reported in `check_constraints`. |
| 217 | + let sig = match (caller_kind, callee_kind) { |
| 218 | + (FnKind::Free, _) => { |
| 219 | + let remap_table = create_remap_table(tcx, def_id, sig_id); |
| 220 | + let mut folder = IndicesFolder { tcx, remap_table: &remap_table }; |
| 221 | + caller_sig.instantiate_identity().fold_with(&mut folder) |
| 222 | + } |
| 223 | + // only `Self` param supported here |
| 224 | + (FnKind::AssocTraitImpl, FnKind::AssocTrait) |
| 225 | + | (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { |
| 226 | + let parent = tcx.parent(def_id.into()); |
| 227 | + let self_ty = tcx.type_of(parent).instantiate_identity(); |
| 228 | + let generic_self_ty = ty::GenericArg::from(self_ty); |
| 229 | + let args = tcx.mk_args_from_iter(std::iter::once(generic_self_ty)); |
| 230 | + caller_sig.instantiate(tcx, args) |
| 231 | + } |
| 232 | + _ => caller_sig.instantiate_identity(), |
| 233 | + }; |
| 234 | + // Bound vars are also inherited from `sig_id`. |
| 235 | + // They will be rebound later in `lower_fn_ty`. |
| 236 | + let sig = sig.skip_binder(); |
| 237 | + let sig_it = sig.inputs().iter().cloned().chain(std::iter::once(sig.output())); |
| 238 | + tcx.arena.alloc_from_iter(sig_it) |
| 239 | +} |
0 commit comments