1use std::ops::ControlFlow;
4
5use hir_def::{
6 AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, HasModule, LocalFieldId, TraitId,
7 TypeOrConstParamId, TypeParamId,
8 attrs::AttrFlags,
9 builtin_derive::BuiltinDeriveImplTrait,
10 hir::generics::{GenericParams, TypeOrConstParamData},
11};
12use itertools::Itertools;
13use la_arena::ArenaMap;
14use rustc_type_ir::{
15 AliasTyKind, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
16 inherent::{GenericArgs as _, IntoKind},
17};
18
19use crate::{
20 FieldType, GenericPredicates,
21 db::HirDatabase,
22 next_solver::{
23 AliasTy, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv,
24 StoredEarlyBinder, TraitRef, Ty, TyKind, Unnormalized, fold::fold_tys, generics::Generics,
25 },
26};
27
28fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId {
29 TypeParamId::from_unchecked(TypeOrConstParamId {
33 parent: trait_id.into(),
34 local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(1)),
35 })
36}
37
38fn trait_args(trait_: BuiltinDeriveImplTrait, self_ty: Ty<'_>) -> GenericArgs<'_> {
39 match trait_ {
40 BuiltinDeriveImplTrait::Copy
41 | BuiltinDeriveImplTrait::Reborrow
42 | BuiltinDeriveImplTrait::Clone
43 | BuiltinDeriveImplTrait::Default
44 | BuiltinDeriveImplTrait::Debug
45 | BuiltinDeriveImplTrait::Hash
46 | BuiltinDeriveImplTrait::Eq
47 | BuiltinDeriveImplTrait::Ord => GenericArgs::new_from_slice(&[self_ty.into()]),
48 BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => {
49 GenericArgs::new_from_slice(&[self_ty.into(), self_ty.into()])
50 }
51 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
52 panic!("`CoerceUnsized` and `DispatchFromDyn` have special generics")
53 }
54 }
55}
56
57pub(crate) fn generics_of<'db>(
58 interner: DbInterner<'db>,
59 id: BuiltinDeriveImplId,
60) -> Generics<'db> {
61 let db = interner.db;
62 let loc = id.loc(db);
63 match loc.trait_ {
64 BuiltinDeriveImplTrait::Copy
65 | BuiltinDeriveImplTrait::Reborrow
66 | BuiltinDeriveImplTrait::Clone
67 | BuiltinDeriveImplTrait::Default
68 | BuiltinDeriveImplTrait::Debug
69 | BuiltinDeriveImplTrait::Hash
70 | BuiltinDeriveImplTrait::Ord
71 | BuiltinDeriveImplTrait::PartialOrd
72 | BuiltinDeriveImplTrait::Eq
73 | BuiltinDeriveImplTrait::PartialEq => {
74 Generics::from_generic_def(db, loc.adt.into(), false)
75 }
76 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
77 let trait_id = loc
78 .trait_
79 .get_id(interner.lang_items())
80 .expect("we don't pass the impl to the solver if we can't resolve the trait");
81 let additional_param = coerce_pointee_new_type_param(trait_id);
82 Generics::from_generic_def_plus_one(db, loc.adt.into(), additional_param, false)
83 }
84 }
85}
86
87pub fn generic_params_count(db: &dyn HirDatabase, id: BuiltinDeriveImplId) -> usize {
88 let loc = id.loc(db);
89 let adt_params = GenericParams::of(db, loc.adt.into());
90 let extra_params_count = match loc.trait_ {
91 BuiltinDeriveImplTrait::Copy
92 | BuiltinDeriveImplTrait::Reborrow
93 | BuiltinDeriveImplTrait::Clone
94 | BuiltinDeriveImplTrait::Default
95 | BuiltinDeriveImplTrait::Debug
96 | BuiltinDeriveImplTrait::Hash
97 | BuiltinDeriveImplTrait::Ord
98 | BuiltinDeriveImplTrait::PartialOrd
99 | BuiltinDeriveImplTrait::Eq
100 | BuiltinDeriveImplTrait::PartialEq => 0,
101 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => 1,
102 };
103 adt_params.len() + extra_params_count
104}
105
106pub fn impl_trait<'db>(
107 interner: DbInterner<'db>,
108 id: BuiltinDeriveImplId,
109) -> EarlyBinder<'db, TraitRef<'db>> {
110 let db = interner.db;
111 let loc = id.loc(db);
112 let trait_id = loc
113 .trait_
114 .get_id(interner.lang_items())
115 .expect("we don't pass the impl to the solver if we can't resolve the trait");
116 match loc.trait_ {
117 BuiltinDeriveImplTrait::Copy
118 | BuiltinDeriveImplTrait::Reborrow
119 | BuiltinDeriveImplTrait::Clone
120 | BuiltinDeriveImplTrait::Default
121 | BuiltinDeriveImplTrait::Debug
122 | BuiltinDeriveImplTrait::Hash
123 | BuiltinDeriveImplTrait::Ord
124 | BuiltinDeriveImplTrait::Eq
125 | BuiltinDeriveImplTrait::PartialOrd
126 | BuiltinDeriveImplTrait::PartialEq => {
127 let self_ty = Ty::new_adt(
128 interner,
129 loc.adt,
130 GenericArgs::identity_for_item(interner, loc.adt.into()),
131 );
132 EarlyBinder::bind(TraitRef::new_from_args(
133 interner,
134 trait_id.into(),
135 trait_args(loc.trait_, self_ty),
136 ))
137 }
138 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
139 let generic_params = GenericParams::of(db, loc.adt.into());
140 let interner = DbInterner::new_no_crate(db);
141 let args = GenericArgs::identity_for_item(interner, loc.adt.into());
142 let self_ty = Ty::new_adt(interner, loc.adt, args);
143 let Some((pointee_param_idx, _, new_param_ty)) =
144 coerce_pointee_params(interner, loc, generic_params, trait_id)
145 else {
146 return EarlyBinder::bind(TraitRef::new(
148 interner,
149 trait_id.into(),
150 [self_ty, self_ty],
151 ));
152 };
153 let changed_args = replace_pointee(interner, pointee_param_idx, new_param_ty, args);
154 let changed_self_ty = Ty::new_adt(interner, loc.adt, changed_args);
155 EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, changed_self_ty]))
156 }
157 }
158}
159
160#[inline]
161pub fn impl_super_outlives<'db>(
162 _interner: DbInterner<'db>,
163 _id: BuiltinDeriveImplId,
164) -> EarlyBinder<'db, &'db [Clause<'db>]> {
165 EarlyBinder::bind(&[])
167}
168
169#[salsa::tracked(returns(ref))]
170pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
171 let loc = impl_.loc(db);
172 let generic_params = GenericParams::of(db, loc.adt.into());
173 let interner = DbInterner::new_with(db, loc.module(db).krate(db));
174 let adt_predicates = GenericPredicates::query(db, loc.adt.into());
175 let trait_id = loc
176 .trait_
177 .get_id(interner.lang_items())
178 .expect("we don't pass the impl to the solver if we can't resolve the trait");
179 match loc.trait_ {
180 BuiltinDeriveImplTrait::Copy
181 | BuiltinDeriveImplTrait::Clone
182 | BuiltinDeriveImplTrait::Debug
183 | BuiltinDeriveImplTrait::Hash
184 | BuiltinDeriveImplTrait::Ord
185 | BuiltinDeriveImplTrait::PartialOrd
186 | BuiltinDeriveImplTrait::Eq
187 | BuiltinDeriveImplTrait::PartialEq => {
188 simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id)
189 }
190 BuiltinDeriveImplTrait::Reborrow => {
191 explicit_own_predicates(adt_predicates.own_explicit_predicates())
192 }
193 BuiltinDeriveImplTrait::Default => {
194 if matches!(loc.adt, AdtId::EnumId(_)) {
195 explicit_own_predicates(adt_predicates.own_explicit_predicates())
197 } else {
198 simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id)
199 }
200 }
201 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
202 let Some((pointee_param_idx, pointee_param_id, new_param_ty)) =
203 coerce_pointee_params(interner, loc, generic_params, trait_id)
204 else {
205 return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
207 Clauses::empty().store(),
208 ));
209 };
210 let duplicated_bounds =
211 adt_predicates.explicit_predicates().iter_identity().filter_map(|pred| {
212 let pred = pred.skip_norm_wip();
213 let mentions_pointee =
214 pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
215 if !mentions_pointee {
216 return None;
217 }
218 let transformed =
219 replace_pointee(interner, pointee_param_idx, new_param_ty, pred);
220 Some(transformed)
221 });
222 let unsize_trait = interner.lang_items().Unsize;
223 let unsize_bound = unsize_trait.map(|unsize_trait| {
224 let pointee_param_ty = Ty::new_param(interner, pointee_param_id, pointee_param_idx);
225 TraitRef::new(interner, unsize_trait.into(), [pointee_param_ty, new_param_ty])
226 .upcast(interner)
227 });
228 GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
229 Clauses::new_from_iter(
230 adt_predicates
231 .explicit_predicates()
232 .iter_identity()
233 .map(Unnormalized::skip_norm_wip)
234 .chain(duplicated_bounds)
235 .chain(unsize_bound),
236 )
237 .store(),
238 ))
239 }
240 }
241}
242
243fn explicit_own_predicates<'db>(
244 predicates: EarlyBinder<'db, impl Iterator<Item = Clause<'db>>>,
245) -> GenericPredicates {
246 GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
247 Clauses::new_from_iter(predicates.skip_binder()).store(),
248 ))
249}
250
251pub fn param_env<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> ParamEnv<'db> {
253 let predicates = predicates(interner.db, id);
254 crate::lower::param_env_from_predicates(interner, predicates)
255}
256
257struct MentionsPointee {
258 pointee_param_idx: u32,
259}
260
261impl<'db> TypeVisitor<DbInterner<'db>> for MentionsPointee {
262 type Result = ControlFlow<()>;
263
264 fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
265 if let TyKind::Param(param) = t.kind()
266 && param.index == self.pointee_param_idx
267 {
268 ControlFlow::Break(())
269 } else {
270 t.super_visit_with(self)
271 }
272 }
273}
274
275fn replace_pointee<'db, T: TypeFoldable<DbInterner<'db>>>(
276 interner: DbInterner<'db>,
277 pointee_param_idx: u32,
278 new_param_ty: Ty<'db>,
279 t: T,
280) -> T {
281 fold_tys(interner, t, |ty| match ty.kind() {
282 TyKind::Param(param) if param.index == pointee_param_idx => new_param_ty,
283 _ => ty,
284 })
285}
286
287fn simple_trait_predicates<'db>(
288 interner: DbInterner<'db>,
289 loc: &BuiltinDeriveImplLoc,
290 generic_params: &GenericParams,
291 adt_predicates: &GenericPredicates,
292 trait_id: TraitId,
293) -> GenericPredicates {
294 let extra_predicates = generic_params
295 .iter_type_or_consts()
296 .filter(|(_, data)| matches!(data, TypeOrConstParamData::TypeParamData(_)))
297 .map(|(param_idx, _)| {
298 let param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
299 parent: loc.adt.into(),
300 local_id: param_idx,
301 });
302 let param_idx = param_idx.into_raw().into_u32()
303 + (generic_params.len_early_bound_lifetimes() as u32);
304 let param_ty = Ty::new_param(interner, param_id, param_idx);
305 let trait_args = trait_args(loc.trait_, param_ty);
306 let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), trait_args);
307 trait_ref.upcast(interner)
308 });
309 let mut assoc_type_bounds = Vec::new();
310 match loc.adt {
311 AdtId::StructId(id) => extend_assoc_type_bounds(
312 interner,
313 &mut assoc_type_bounds,
314 interner.db.field_types(id.into()),
315 trait_id,
316 loc.trait_,
317 ),
318 AdtId::UnionId(id) => extend_assoc_type_bounds(
319 interner,
320 &mut assoc_type_bounds,
321 interner.db.field_types(id.into()),
322 trait_id,
323 loc.trait_,
324 ),
325 AdtId::EnumId(id) => {
326 for &(variant_id, _) in id.enum_variants(interner.db).variants.values() {
327 extend_assoc_type_bounds(
328 interner,
329 &mut assoc_type_bounds,
330 interner.db.field_types(variant_id.into()),
331 trait_id,
332 loc.trait_,
333 )
334 }
335 }
336 }
337 GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
338 Clauses::new_from_iter(
339 adt_predicates
340 .explicit_predicates()
341 .iter_identity()
342 .map(Unnormalized::skip_norm_wip)
343 .chain(extra_predicates)
344 .chain(assoc_type_bounds),
345 )
346 .store(),
347 ))
348}
349
350fn extend_assoc_type_bounds<'db>(
351 interner: DbInterner<'db>,
352 assoc_type_bounds: &mut Vec<Clause<'db>>,
353 fields: &ArenaMap<LocalFieldId, FieldType>,
354 trait_id: TraitId,
355 trait_: BuiltinDeriveImplTrait,
356) {
357 struct ProjectionFinder<'a, 'db> {
358 interner: DbInterner<'db>,
359 assoc_type_bounds: &'a mut Vec<Clause<'db>>,
360 trait_id: TraitId,
361 trait_: BuiltinDeriveImplTrait,
362 }
363
364 impl<'db> TypeVisitor<DbInterner<'db>> for ProjectionFinder<'_, 'db> {
365 type Result = ();
366
367 fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
368 if let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) = t.kind() {
369 self.assoc_type_bounds.push(
370 TraitRef::new_from_args(
371 self.interner,
372 self.trait_id.into(),
373 trait_args(self.trait_, t),
374 )
375 .upcast(self.interner),
376 );
377 }
378
379 t.super_visit_with(self)
380 }
381 }
382
383 let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_id, trait_ };
384 for (_, field) in fields.iter() {
385 field.ty().instantiate_identity().skip_norm_wip().visit_with(&mut visitor);
386 }
387}
388
389fn coerce_pointee_params<'db>(
390 interner: DbInterner<'db>,
391 loc: &BuiltinDeriveImplLoc,
392 generic_params: &GenericParams,
393 trait_id: TraitId,
394) -> Option<(u32, TypeParamId, Ty<'db>)> {
395 let pointee_param = {
396 if let Ok((pointee_param, _)) = generic_params
397 .iter_type_or_consts()
398 .filter(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
399 .exactly_one()
400 {
401 pointee_param
402 } else {
403 let (_, generic_param_attrs) =
404 AttrFlags::query_generic_params(interner.db, loc.adt.into());
405 generic_param_attrs
406 .iter()
407 .find(|param| param.1.contains(AttrFlags::IS_POINTEE))
408 .map(|(param, _)| param)
409 .or_else(|| {
410 generic_params
411 .iter_type_or_consts()
412 .find(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
413 .map(|(idx, _)| idx)
414 })?
415 }
416 };
417 let pointee_param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
418 parent: loc.adt.into(),
419 local_id: pointee_param,
420 });
421 let pointee_param_idx =
422 pointee_param.into_raw().into_u32() + (generic_params.len_early_bound_lifetimes() as u32);
423 let new_param_idx = generic_params.len() as u32;
424 let new_param_id = coerce_pointee_new_type_param(trait_id);
425 let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx);
426 Some((pointee_param_idx, pointee_param_id, new_param_ty))
427}