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, BoundVar, BoundVarIndexKind, DebruijnIndex, INNERMOST, TypeFlags, TypeFoldable,
16 TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
17 TypeVisitor, Upcast,
18 inherent::{GenericArgs as _, IntoKind, Ty as _},
19};
20
21use crate::{
22 FieldType, GenericPredicates,
23 db::HirDatabase,
24 next_solver::{
25 AliasTy, Binder, BoundConst, BoundRegion, BoundTy, BoundVarKinds, Clause, Clauses, Const,
26 ConstKind, DbInterner, EarlyBinder, GenericArgs, ParamEnv, Region, RegionKind,
27 StoredEarlyBinder, TraitRef, Ty, TyKind, Unnormalized, fold::fold_tys, generics::Generics,
28 },
29};
30
31fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId {
32 TypeParamId::from_unchecked(TypeOrConstParamId {
36 parent: trait_id.into(),
37 local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(1)),
38 })
39}
40
41fn trait_args(trait_: BuiltinDeriveImplTrait, self_ty: Ty<'_>) -> GenericArgs<'_> {
42 match trait_ {
43 BuiltinDeriveImplTrait::Copy
44 | BuiltinDeriveImplTrait::Reborrow
45 | BuiltinDeriveImplTrait::Clone
46 | BuiltinDeriveImplTrait::Default
47 | BuiltinDeriveImplTrait::Debug
48 | BuiltinDeriveImplTrait::Hash
49 | BuiltinDeriveImplTrait::Eq
50 | BuiltinDeriveImplTrait::Ord => GenericArgs::new_from_slice(&[self_ty.into()]),
51 BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => {
52 GenericArgs::new_from_slice(&[self_ty.into(), self_ty.into()])
53 }
54 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
55 panic!("`CoerceUnsized` and `DispatchFromDyn` have special generics")
56 }
57 }
58}
59
60pub(crate) fn generics_of<'db>(
61 interner: DbInterner<'db>,
62 id: BuiltinDeriveImplId,
63) -> Generics<'db> {
64 let db = interner.db;
65 let loc = id.loc(db);
66 match loc.trait_ {
67 BuiltinDeriveImplTrait::Copy
68 | BuiltinDeriveImplTrait::Reborrow
69 | BuiltinDeriveImplTrait::Clone
70 | BuiltinDeriveImplTrait::Default
71 | BuiltinDeriveImplTrait::Debug
72 | BuiltinDeriveImplTrait::Hash
73 | BuiltinDeriveImplTrait::Ord
74 | BuiltinDeriveImplTrait::PartialOrd
75 | BuiltinDeriveImplTrait::Eq
76 | BuiltinDeriveImplTrait::PartialEq => {
77 Generics::from_generic_def(db, loc.adt.into(), false)
78 }
79 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
80 let trait_id = loc
81 .trait_
82 .get_id(interner.lang_items())
83 .expect("we don't pass the impl to the solver if we can't resolve the trait");
84 let additional_param = coerce_pointee_new_type_param(trait_id);
85 Generics::from_generic_def_plus_one(db, loc.adt.into(), additional_param, false)
86 }
87 }
88}
89
90pub fn generic_params_count(db: &dyn HirDatabase, id: BuiltinDeriveImplId) -> usize {
91 let loc = id.loc(db);
92 let adt_params = GenericParams::of(db, loc.adt.into());
93 let extra_params_count = match loc.trait_ {
94 BuiltinDeriveImplTrait::Copy
95 | BuiltinDeriveImplTrait::Reborrow
96 | BuiltinDeriveImplTrait::Clone
97 | BuiltinDeriveImplTrait::Default
98 | BuiltinDeriveImplTrait::Debug
99 | BuiltinDeriveImplTrait::Hash
100 | BuiltinDeriveImplTrait::Ord
101 | BuiltinDeriveImplTrait::PartialOrd
102 | BuiltinDeriveImplTrait::Eq
103 | BuiltinDeriveImplTrait::PartialEq => 0,
104 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => 1,
105 };
106 adt_params.len() + extra_params_count
107}
108
109pub fn impl_trait<'db>(
110 interner: DbInterner<'db>,
111 id: BuiltinDeriveImplId,
112) -> EarlyBinder<'db, TraitRef<'db>> {
113 let db = interner.db;
114 let loc = id.loc(db);
115 let trait_id = loc
116 .trait_
117 .get_id(interner.lang_items())
118 .expect("we don't pass the impl to the solver if we can't resolve the trait");
119 match loc.trait_ {
120 BuiltinDeriveImplTrait::Copy
121 | BuiltinDeriveImplTrait::Reborrow
122 | BuiltinDeriveImplTrait::Clone
123 | BuiltinDeriveImplTrait::Default
124 | BuiltinDeriveImplTrait::Debug
125 | BuiltinDeriveImplTrait::Hash
126 | BuiltinDeriveImplTrait::Ord
127 | BuiltinDeriveImplTrait::Eq
128 | BuiltinDeriveImplTrait::PartialOrd
129 | BuiltinDeriveImplTrait::PartialEq => {
130 let self_ty = Ty::new_adt(
131 interner,
132 loc.adt,
133 GenericArgs::identity_for_item(interner, loc.adt.into()),
134 );
135 EarlyBinder::bind(TraitRef::new_from_args(
136 interner,
137 trait_id.into(),
138 trait_args(loc.trait_, self_ty),
139 ))
140 }
141 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
142 let generic_params = GenericParams::of(db, loc.adt.into());
143 let interner = DbInterner::new_no_crate(db);
144 let args = GenericArgs::identity_for_item(interner, loc.adt.into());
145 let self_ty = Ty::new_adt(interner, loc.adt, args);
146 let Some((pointee_param_idx, _, new_param_ty)) =
147 coerce_pointee_params(interner, loc, generic_params, trait_id)
148 else {
149 return EarlyBinder::bind(TraitRef::new(
151 interner,
152 trait_id.into(),
153 [self_ty, self_ty],
154 ));
155 };
156 let changed_args = replace_pointee(interner, pointee_param_idx, new_param_ty, args);
157 let changed_self_ty = Ty::new_adt(interner, loc.adt, changed_args);
158 EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, changed_self_ty]))
159 }
160 }
161}
162
163#[inline]
164pub fn impl_super_outlives<'db>(
165 _interner: DbInterner<'db>,
166 _id: BuiltinDeriveImplId,
167) -> EarlyBinder<'db, &'db [Clause<'db>]> {
168 EarlyBinder::bind(&[])
170}
171
172#[salsa::tracked(returns(ref))]
173pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
174 let loc = impl_.loc(db);
175 let generic_params = GenericParams::of(db, loc.adt.into());
176 let interner = DbInterner::new_with(db, loc.module(db).krate(db));
177 let adt_predicates = GenericPredicates::query(db, loc.adt.into());
178 let trait_id = loc
179 .trait_
180 .get_id(interner.lang_items())
181 .expect("we don't pass the impl to the solver if we can't resolve the trait");
182 match loc.trait_ {
183 BuiltinDeriveImplTrait::Copy
184 | BuiltinDeriveImplTrait::Clone
185 | BuiltinDeriveImplTrait::Debug
186 | BuiltinDeriveImplTrait::Hash
187 | BuiltinDeriveImplTrait::Ord
188 | BuiltinDeriveImplTrait::PartialOrd
189 | BuiltinDeriveImplTrait::Eq
190 | BuiltinDeriveImplTrait::PartialEq => {
191 simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id)
192 }
193 BuiltinDeriveImplTrait::Reborrow => {
194 explicit_own_predicates(adt_predicates.own_explicit_predicates())
195 }
196 BuiltinDeriveImplTrait::Default => {
197 if matches!(loc.adt, AdtId::EnumId(_)) {
198 explicit_own_predicates(adt_predicates.own_explicit_predicates())
200 } else {
201 simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id)
202 }
203 }
204 BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
205 let Some((pointee_param_idx, pointee_param_id, new_param_ty)) =
206 coerce_pointee_params(interner, loc, generic_params, trait_id)
207 else {
208 return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
210 Clauses::empty().store(),
211 ));
212 };
213 let duplicated_bounds =
214 adt_predicates.explicit_predicates().iter_identity().filter_map(|pred| {
215 let pred = pred.skip_norm_wip();
216 let mentions_pointee =
217 pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
218 if !mentions_pointee {
219 return None;
220 }
221 let transformed =
222 replace_pointee(interner, pointee_param_idx, new_param_ty, pred);
223 Some(transformed)
224 });
225 let unsize_trait = interner.lang_items().Unsize;
226 let unsize_bound = unsize_trait.map(|unsize_trait| {
227 let pointee_param_ty = Ty::new_param(interner, pointee_param_id, pointee_param_idx);
228 TraitRef::new(interner, unsize_trait.into(), [pointee_param_ty, new_param_ty])
229 .upcast(interner)
230 });
231 GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
232 Clauses::new_from_iter(
233 adt_predicates
234 .explicit_predicates()
235 .iter_identity()
236 .map(Unnormalized::skip_norm_wip)
237 .chain(duplicated_bounds)
238 .chain(unsize_bound),
239 )
240 .store(),
241 ))
242 }
243 }
244}
245
246fn explicit_own_predicates<'db>(
247 predicates: EarlyBinder<'db, impl Iterator<Item = Clause<'db>>>,
248) -> GenericPredicates {
249 GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
250 Clauses::new_from_iter(predicates.skip_binder()).store(),
251 ))
252}
253
254pub fn param_env<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> ParamEnv<'db> {
256 let predicates = predicates(interner.db, id);
257 crate::lower::param_env_from_predicates(interner, predicates)
258}
259
260struct MentionsPointee {
261 pointee_param_idx: u32,
262}
263
264impl<'db> TypeVisitor<DbInterner<'db>> for MentionsPointee {
265 type Result = ControlFlow<()>;
266
267 fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
268 if let TyKind::Param(param) = t.kind()
269 && param.index == self.pointee_param_idx
270 {
271 ControlFlow::Break(())
272 } else {
273 t.super_visit_with(self)
274 }
275 }
276}
277
278fn replace_pointee<'db, T: TypeFoldable<DbInterner<'db>>>(
279 interner: DbInterner<'db>,
280 pointee_param_idx: u32,
281 new_param_ty: Ty<'db>,
282 t: T,
283) -> T {
284 fold_tys(interner, t, |ty| match ty.kind() {
285 TyKind::Param(param) if param.index == pointee_param_idx => new_param_ty,
286 _ => ty,
287 })
288}
289
290fn simple_trait_predicates<'db>(
291 interner: DbInterner<'db>,
292 loc: &BuiltinDeriveImplLoc,
293 generic_params: &GenericParams,
294 adt_predicates: &GenericPredicates,
295 trait_id: TraitId,
296) -> GenericPredicates {
297 let extra_predicates = generic_params
298 .iter_type_or_consts()
299 .filter(|(_, data)| matches!(data, TypeOrConstParamData::TypeParamData(_)))
300 .map(|(param_idx, _)| {
301 let param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
302 parent: loc.adt.into(),
303 local_id: param_idx,
304 });
305 let param_idx = param_idx.into_raw().into_u32()
306 + (generic_params.len_early_bound_lifetimes() as u32);
307 let param_ty = Ty::new_param(interner, param_id, param_idx);
308 let trait_args = trait_args(loc.trait_, param_ty);
309 let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), trait_args);
310 trait_ref.upcast(interner)
311 });
312 let mut assoc_type_bounds = Vec::new();
313 match loc.adt {
314 AdtId::StructId(id) => extend_assoc_type_bounds(
315 interner,
316 &mut assoc_type_bounds,
317 interner.db.field_types(id.into()),
318 trait_id,
319 loc.trait_,
320 ),
321 AdtId::UnionId(id) => extend_assoc_type_bounds(
322 interner,
323 &mut assoc_type_bounds,
324 interner.db.field_types(id.into()),
325 trait_id,
326 loc.trait_,
327 ),
328 AdtId::EnumId(id) => {
329 for &(variant_id, _) in id.enum_variants(interner.db).variants.values() {
330 extend_assoc_type_bounds(
331 interner,
332 &mut assoc_type_bounds,
333 interner.db.field_types(variant_id.into()),
334 trait_id,
335 loc.trait_,
336 )
337 }
338 }
339 }
340 GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
341 Clauses::new_from_iter(
342 adt_predicates
343 .explicit_predicates()
344 .iter_identity()
345 .map(Unnormalized::skip_norm_wip)
346 .chain(extra_predicates)
347 .chain(assoc_type_bounds),
348 )
349 .store(),
350 ))
351}
352
353fn extend_assoc_type_bounds<'db>(
354 interner: DbInterner<'db>,
355 assoc_type_bounds: &mut Vec<Clause<'db>>,
356 fields: &ArenaMap<LocalFieldId, FieldType>,
357 trait_id: TraitId,
358 trait_: BuiltinDeriveImplTrait,
359) {
360 struct ProjectionFinder<'a, 'db> {
361 interner: DbInterner<'db>,
362 assoc_type_bounds: &'a mut Vec<Clause<'db>>,
363 trait_id: TraitId,
364 trait_: BuiltinDeriveImplTrait,
365 bound_generic_params_stack: Vec<BoundVarKinds<'db>>,
366 }
367
368 impl<'db> TypeVisitor<DbInterner<'db>> for ProjectionFinder<'_, 'db> {
369 type Result = ();
370
371 fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
372 if !t.has_type_flags(TypeFlags::HAS_TY_PROJECTION) {
373 return;
374 }
375 if let TyKind::Alias(alias @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) =
376 t.kind()
377 && let TyKind::Param(_) = alias.self_ty().kind()
378 {
379 let t = t.fold_with(&mut FlattenBinders {
383 interner: self.interner,
384 enclosing_binders: &self.bound_generic_params_stack,
385 nested_binder_depth: INNERMOST,
386 });
387 let bound_vars = BoundVarKinds::new_from_iter(
388 self.bound_generic_params_stack.iter().flat_map(|it| it.iter()),
389 );
390 self.assoc_type_bounds.push(
391 Binder::bind_with_vars(
392 TraitRef::new_from_args(
393 self.interner,
394 self.trait_id.into(),
395 trait_args(self.trait_, t),
396 ),
397 bound_vars,
398 )
399 .upcast(self.interner),
400 );
401 }
402
403 t.super_visit_with(self)
404 }
405
406 fn visit_binder<T: TypeVisitable<DbInterner<'db>>>(
408 &mut self,
409 binder: &Binder<'db, T>,
410 ) -> Self::Result {
411 let stack_len = self.bound_generic_params_stack.len();
412 self.bound_generic_params_stack.push(binder.bound_vars());
415
416 binder.super_visit_with(self);
417
418 self.bound_generic_params_stack.truncate(stack_len);
419 }
420 }
421
422 let mut visitor = ProjectionFinder {
423 interner,
424 assoc_type_bounds,
425 trait_id,
426 trait_,
427 bound_generic_params_stack: Vec::new(),
428 };
429 for (_, field) in fields.iter() {
430 field.ty().instantiate_identity().skip_norm_wip().visit_with(&mut visitor);
431 }
432}
433
434struct FlattenBinders<'a, 'db> {
438 interner: DbInterner<'db>,
439 enclosing_binders: &'a [BoundVarKinds<'db>],
440 nested_binder_depth: DebruijnIndex,
441}
442
443impl FlattenBinders<'_, '_> {
444 fn flattened_var(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<BoundVar> {
445 let depth = debruijn.as_usize().checked_sub(self.nested_binder_depth.as_usize())?;
446 let binder = self.enclosing_binders.len().checked_sub(depth + 1)?;
447 let offset = self.enclosing_binders[..binder].iter().map(|it| it.len()).sum::<usize>();
448 Some(BoundVar::from_usize(offset + var.as_usize()))
449 }
450}
451
452impl<'db> TypeFolder<DbInterner<'db>> for FlattenBinders<'_, 'db> {
453 fn cx(&self) -> DbInterner<'db> {
454 self.interner
455 }
456
457 fn fold_binder<T: TypeFoldable<DbInterner<'db>>>(
458 &mut self,
459 binder: Binder<'db, T>,
460 ) -> Binder<'db, T> {
461 self.nested_binder_depth.shift_in(1);
462 let binder = binder.super_fold_with(self);
463 self.nested_binder_depth.shift_out(1);
464 binder
465 }
466
467 fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
468 if !ty.has_bound_vars() {
469 return ty;
470 }
471 if let TyKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty) = ty.kind()
472 && let Some(var) = self.flattened_var(debruijn, bound_ty.var)
473 {
474 return Ty::new_bound(
475 self.interner,
476 self.nested_binder_depth,
477 BoundTy { var, kind: bound_ty.kind },
478 );
479 }
480 ty.super_fold_with(self)
481 }
482
483 fn fold_region(&mut self, region: Region<'db>) -> Region<'db> {
484 if !region.has_bound_vars() {
485 return region;
486 }
487 if let RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), bound_region) = region.kind()
488 && let Some(var) = self.flattened_var(debruijn, bound_region.var)
489 {
490 return Region::new_bound(
491 self.interner,
492 self.nested_binder_depth,
493 BoundRegion { var, kind: bound_region.kind },
494 );
495 }
496 region
497 }
498
499 fn fold_const(&mut self, konst: Const<'db>) -> Const<'db> {
500 if !konst.has_bound_vars() {
501 return konst;
502 }
503 if let ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_const) = konst.kind()
504 && let Some(var) = self.flattened_var(debruijn, bound_const.var)
505 {
506 return Const::new_bound(self.interner, self.nested_binder_depth, BoundConst::new(var));
507 }
508 konst.super_fold_with(self)
509 }
510}
511
512fn coerce_pointee_params<'db>(
513 interner: DbInterner<'db>,
514 loc: &BuiltinDeriveImplLoc,
515 generic_params: &GenericParams,
516 trait_id: TraitId,
517) -> Option<(u32, TypeParamId, Ty<'db>)> {
518 let pointee_param = {
519 if let Ok((pointee_param, _)) = generic_params
520 .iter_type_or_consts()
521 .filter(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
522 .exactly_one()
523 {
524 pointee_param
525 } else {
526 let (_, generic_param_attrs) =
527 AttrFlags::query_generic_params(interner.db, loc.adt.into());
528 generic_param_attrs
529 .iter()
530 .find(|param| param.1.contains(AttrFlags::IS_POINTEE))
531 .map(|(param, _)| param)
532 .or_else(|| {
533 generic_params
534 .iter_type_or_consts()
535 .find(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
536 .map(|(idx, _)| idx)
537 })?
538 }
539 };
540 let pointee_param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
541 parent: loc.adt.into(),
542 local_id: pointee_param,
543 });
544 let pointee_param_idx =
545 pointee_param.into_raw().into_u32() + (generic_params.len_early_bound_lifetimes() as u32);
546 let new_param_idx = generic_params.len() as u32;
547 let new_param_id = coerce_pointee_new_type_param(trait_id);
548 let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx);
549 Some((pointee_param_idx, pointee_param_id, new_param_ty))
550}