Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/builtin_derive.rs')
-rw-r--r--crates/hir-ty/src/builtin_derive.rs599
1 files changed, 599 insertions, 0 deletions
diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs
new file mode 100644
index 0000000000..f3e67d01e5
--- /dev/null
+++ b/crates/hir-ty/src/builtin_derive.rs
@@ -0,0 +1,599 @@
+//! Implementation of builtin derive impls.
+
+use std::ops::ControlFlow;
+
+use hir_def::{
+ AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, HasModule, LocalFieldId, TraitId,
+ TypeOrConstParamId, TypeParamId,
+ attrs::AttrFlags,
+ builtin_derive::BuiltinDeriveImplTrait,
+ hir::generics::{GenericParams, TypeOrConstParamData},
+};
+use itertools::Itertools;
+use la_arena::ArenaMap;
+use rustc_type_ir::{
+ AliasTyKind, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
+ inherent::{GenericArgs as _, IntoKind},
+};
+
+use crate::{
+ GenericPredicates,
+ db::HirDatabase,
+ next_solver::{
+ Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, StoredEarlyBinder,
+ StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
+ },
+};
+
+fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId {
+ // HACK: Fake the param.
+ // We cannot use a dummy param here, because it can leak into the IDE layer and that'll cause panics
+ // when e.g. trying to display it. So we use an existing param.
+ TypeParamId::from_unchecked(TypeOrConstParamId {
+ parent: trait_id.into(),
+ local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(1)),
+ })
+}
+
+pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics {
+ let db = interner.db;
+ let loc = id.loc(db);
+ match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Default
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::PartialOrd
+ | BuiltinDeriveImplTrait::Eq
+ | BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()),
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
+ let mut generics = interner.generics_of(loc.adt.into());
+ let trait_id = loc
+ .trait_
+ .get_id(interner.lang_items())
+ .expect("we don't pass the impl to the solver if we can't resolve the trait");
+ generics.push_param(coerce_pointee_new_type_param(trait_id).into());
+ generics
+ }
+ }
+}
+
+pub fn generic_params_count(db: &dyn HirDatabase, id: BuiltinDeriveImplId) -> usize {
+ let loc = id.loc(db);
+ let adt_params = GenericParams::new(db, loc.adt.into());
+ let extra_params_count = match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Default
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::PartialOrd
+ | BuiltinDeriveImplTrait::Eq
+ | BuiltinDeriveImplTrait::PartialEq => 0,
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => 1,
+ };
+ adt_params.len() + extra_params_count
+}
+
+pub fn impl_trait<'db>(
+ interner: DbInterner<'db>,
+ id: BuiltinDeriveImplId,
+) -> EarlyBinder<'db, TraitRef<'db>> {
+ let db = interner.db;
+ let loc = id.loc(db);
+ let trait_id = loc
+ .trait_
+ .get_id(interner.lang_items())
+ .expect("we don't pass the impl to the solver if we can't resolve the trait");
+ match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Default
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::Eq => {
+ let self_ty = Ty::new_adt(
+ interner,
+ loc.adt,
+ GenericArgs::identity_for_item(interner, loc.adt.into()),
+ );
+ EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty]))
+ }
+ BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => {
+ let self_ty = Ty::new_adt(
+ interner,
+ loc.adt,
+ GenericArgs::identity_for_item(interner, loc.adt.into()),
+ );
+ EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, self_ty]))
+ }
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
+ let generic_params = GenericParams::new(db, loc.adt.into());
+ let interner = DbInterner::new_no_crate(db);
+ let args = GenericArgs::identity_for_item(interner, loc.adt.into());
+ let self_ty = Ty::new_adt(interner, loc.adt, args);
+ let Some((pointee_param_idx, _, new_param_ty)) =
+ coerce_pointee_params(interner, loc, &generic_params, trait_id)
+ else {
+ // Malformed derive.
+ return EarlyBinder::bind(TraitRef::new(
+ interner,
+ trait_id.into(),
+ [self_ty, self_ty],
+ ));
+ };
+ let changed_args = replace_pointee(interner, pointee_param_idx, new_param_ty, args);
+ let changed_self_ty = Ty::new_adt(interner, loc.adt, changed_args);
+ EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, changed_self_ty]))
+ }
+ }
+}
+
+#[salsa::tracked(returns(ref), unsafe(non_update_types))]
+pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
+ let loc = impl_.loc(db);
+ let generic_params = GenericParams::new(db, loc.adt.into());
+ let interner = DbInterner::new_with(db, loc.module(db).krate(db));
+ let adt_predicates = GenericPredicates::query(db, loc.adt.into());
+ let trait_id = loc
+ .trait_
+ .get_id(interner.lang_items())
+ .expect("we don't pass the impl to the solver if we can't resolve the trait");
+ match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::PartialOrd
+ | BuiltinDeriveImplTrait::Eq
+ | BuiltinDeriveImplTrait::PartialEq => {
+ simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
+ }
+ BuiltinDeriveImplTrait::Default => {
+ if matches!(loc.adt, AdtId::EnumId(_)) {
+ // Enums don't have extra bounds.
+ GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::new_from_slice(adt_predicates.explicit_predicates().skip_binder())
+ .store(),
+ ))
+ } else {
+ simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
+ }
+ }
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
+ let Some((pointee_param_idx, pointee_param_id, new_param_ty)) =
+ coerce_pointee_params(interner, loc, &generic_params, trait_id)
+ else {
+ // Malformed derive.
+ return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::default().store(),
+ ));
+ };
+ let duplicated_bounds =
+ adt_predicates.explicit_predicates().iter_identity_copied().filter_map(|pred| {
+ let mentions_pointee =
+ pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
+ if !mentions_pointee {
+ return None;
+ }
+ let transformed =
+ replace_pointee(interner, pointee_param_idx, new_param_ty, pred);
+ Some(transformed)
+ });
+ let unsize_trait = interner.lang_items().Unsize;
+ let unsize_bound = unsize_trait.map(|unsize_trait| {
+ let pointee_param_ty = Ty::new_param(interner, pointee_param_id, pointee_param_idx);
+ TraitRef::new(interner, unsize_trait.into(), [pointee_param_ty, new_param_ty])
+ .upcast(interner)
+ });
+ GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::new_from_iter(
+ interner,
+ adt_predicates
+ .explicit_predicates()
+ .iter_identity_copied()
+ .chain(duplicated_bounds)
+ .chain(unsize_bound),
+ )
+ .store(),
+ ))
+ }
+ }
+}
+
+/// Not cached in a query, currently used in `hir` only. If you need this in `hir-ty` consider introducing a query.
+pub fn param_env<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> ParamEnv<'db> {
+ let predicates = predicates(interner.db, id);
+ crate::lower::param_env_from_predicates(interner, predicates)
+}
+
+struct MentionsPointee {
+ pointee_param_idx: u32,
+}
+
+impl<'db> TypeVisitor<DbInterner<'db>> for MentionsPointee {
+ type Result = ControlFlow<()>;
+
+ fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
+ if let TyKind::Param(param) = t.kind()
+ && param.index == self.pointee_param_idx
+ {
+ ControlFlow::Break(())
+ } else {
+ t.super_visit_with(self)
+ }
+ }
+}
+
+fn replace_pointee<'db, T: TypeFoldable<DbInterner<'db>>>(
+ interner: DbInterner<'db>,
+ pointee_param_idx: u32,
+ new_param_ty: Ty<'db>,
+ t: T,
+) -> T {
+ fold_tys(interner, t, |ty| match ty.kind() {
+ TyKind::Param(param) if param.index == pointee_param_idx => new_param_ty,
+ _ => ty,
+ })
+}
+
+fn simple_trait_predicates<'db>(
+ interner: DbInterner<'db>,
+ loc: &BuiltinDeriveImplLoc,
+ generic_params: &GenericParams,
+ adt_predicates: &GenericPredicates,
+ trait_id: TraitId,
+) -> GenericPredicates {
+ let extra_predicates = generic_params
+ .iter_type_or_consts()
+ .filter(|(_, data)| matches!(data, TypeOrConstParamData::TypeParamData(_)))
+ .map(|(param_idx, _)| {
+ let param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
+ parent: loc.adt.into(),
+ local_id: param_idx,
+ });
+ let param_idx =
+ param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
+ let param_ty = Ty::new_param(interner, param_id, param_idx);
+ let trait_ref = TraitRef::new(interner, trait_id.into(), [param_ty]);
+ trait_ref.upcast(interner)
+ });
+ let mut assoc_type_bounds = Vec::new();
+ match loc.adt {
+ AdtId::StructId(id) => extend_assoc_type_bounds(
+ interner,
+ &mut assoc_type_bounds,
+ interner.db.field_types(id.into()),
+ trait_id,
+ ),
+ AdtId::UnionId(id) => extend_assoc_type_bounds(
+ interner,
+ &mut assoc_type_bounds,
+ interner.db.field_types(id.into()),
+ trait_id,
+ ),
+ AdtId::EnumId(id) => {
+ for &(variant_id, _, _) in &id.enum_variants(interner.db).variants {
+ extend_assoc_type_bounds(
+ interner,
+ &mut assoc_type_bounds,
+ interner.db.field_types(variant_id.into()),
+ trait_id,
+ )
+ }
+ }
+ }
+ GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::new_from_iter(
+ interner,
+ adt_predicates
+ .explicit_predicates()
+ .iter_identity_copied()
+ .chain(extra_predicates)
+ .chain(assoc_type_bounds),
+ )
+ .store(),
+ ))
+}
+
+fn extend_assoc_type_bounds<'db>(
+ interner: DbInterner<'db>,
+ assoc_type_bounds: &mut Vec<Clause<'db>>,
+ fields: &ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>,
+ trait_: TraitId,
+) {
+ struct ProjectionFinder<'a, 'db> {
+ interner: DbInterner<'db>,
+ assoc_type_bounds: &'a mut Vec<Clause<'db>>,
+ trait_: TraitId,
+ }
+
+ impl<'db> TypeVisitor<DbInterner<'db>> for ProjectionFinder<'_, 'db> {
+ type Result = ();
+
+ fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
+ if let TyKind::Alias(AliasTyKind::Projection, _) = t.kind() {
+ self.assoc_type_bounds.push(
+ TraitRef::new(self.interner, self.trait_.into(), [t]).upcast(self.interner),
+ );
+ }
+
+ t.super_visit_with(self)
+ }
+ }
+
+ let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_ };
+ for (_, field) in fields.iter() {
+ field.get().instantiate_identity().visit_with(&mut visitor);
+ }
+}
+
+fn coerce_pointee_params<'db>(
+ interner: DbInterner<'db>,
+ loc: &BuiltinDeriveImplLoc,
+ generic_params: &GenericParams,
+ trait_id: TraitId,
+) -> Option<(u32, TypeParamId, Ty<'db>)> {
+ let pointee_param = {
+ if let Ok((pointee_param, _)) = generic_params
+ .iter_type_or_consts()
+ .filter(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
+ .exactly_one()
+ {
+ pointee_param
+ } else {
+ let (_, generic_param_attrs) =
+ AttrFlags::query_generic_params(interner.db, loc.adt.into());
+ generic_param_attrs
+ .iter()
+ .find(|param| param.1.contains(AttrFlags::IS_POINTEE))
+ .map(|(param, _)| param)
+ .or_else(|| {
+ generic_params
+ .iter_type_or_consts()
+ .find(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
+ .map(|(idx, _)| idx)
+ })?
+ }
+ };
+ let pointee_param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
+ parent: loc.adt.into(),
+ local_id: pointee_param,
+ });
+ let pointee_param_idx =
+ pointee_param.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
+ let new_param_idx = generic_params.len() as u32;
+ let new_param_id = coerce_pointee_new_type_param(trait_id);
+ let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx);
+ Some((pointee_param_idx, pointee_param_id, new_param_ty))
+}
+
+#[cfg(test)]
+mod tests {
+ use expect_test::{Expect, expect};
+ use hir_def::nameres::crate_def_map;
+ use itertools::Itertools;
+ use stdx::format_to;
+ use test_fixture::WithFixture;
+
+ use crate::{builtin_derive::impl_trait, next_solver::DbInterner, test_db::TestDB};
+
+ fn check_trait_refs(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
+ let db = TestDB::with_files(ra_fixture);
+ let def_map = crate_def_map(&db, db.test_crate());
+
+ let interner = DbInterner::new_with(&db, db.test_crate());
+ crate::attach_db(&db, || {
+ let mut trait_refs = Vec::new();
+ for (_, module) in def_map.modules() {
+ for derive in module.scope.builtin_derive_impls() {
+ let trait_ref = impl_trait(interner, derive).skip_binder();
+ trait_refs.push(format!("{trait_ref:?}"));
+ }
+ }
+
+ expectation.assert_eq(&trait_refs.join("\n"));
+ });
+ }
+
+ fn check_predicates(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
+ let db = TestDB::with_files(ra_fixture);
+ let def_map = crate_def_map(&db, db.test_crate());
+
+ crate::attach_db(&db, || {
+ let mut predicates = String::new();
+ for (_, module) in def_map.modules() {
+ for derive in module.scope.builtin_derive_impls() {
+ let preds = super::predicates(&db, derive).all_predicates().skip_binder();
+ format_to!(
+ predicates,
+ "{}\n\n",
+ preds.iter().format_with("\n", |pred, formatter| formatter(&format_args!(
+ "{pred:?}"
+ ))),
+ );
+ }
+ }
+
+ expectation.assert_eq(&predicates);
+ });
+ }
+
+ #[test]
+ fn simple_macros_trait_ref() {
+ check_trait_refs(
+ r#"
+//- minicore: derive, clone, copy, eq, ord, hash, fmt
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct Simple;
+
+trait Trait {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
+ "#,
+ expect![[r#"
+ Simple: Debug
+ Simple: Clone
+ Simple: Copy
+ Simple: PartialEq<[Simple]>
+ Simple: Eq
+ Simple: PartialOrd<[Simple]>
+ Simple: Ord
+ Simple: Hash
+ WithGenerics<#0, #1, #2>: Debug
+ WithGenerics<#0, #1, #2>: Clone
+ WithGenerics<#0, #1, #2>: Copy
+ WithGenerics<#0, #1, #2>: PartialEq<[WithGenerics<#0, #1, #2>]>
+ WithGenerics<#0, #1, #2>: Eq
+ WithGenerics<#0, #1, #2>: PartialOrd<[WithGenerics<#0, #1, #2>]>
+ WithGenerics<#0, #1, #2>: Ord
+ WithGenerics<#0, #1, #2>: Hash"#]],
+ );
+ }
+
+ #[test]
+ fn coerce_pointee_trait_ref() {
+ check_trait_refs(
+ r#"
+//- minicore: derive, coerce_pointee
+use core::marker::CoercePointee;
+
+#[derive(CoercePointee)]
+struct Simple<T: ?Sized>(*const T);
+
+#[derive(CoercePointee)]
+struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U);
+ "#,
+ expect![[r#"
+ Simple<#0>: CoerceUnsized<[Simple<#1>]>
+ Simple<#0>: DispatchFromDyn<[Simple<#1>]>
+ MultiGenericParams<#0, #1, #2, #3>: CoerceUnsized<[MultiGenericParams<#0, #1, #4, #3>]>
+ MultiGenericParams<#0, #1, #2, #3>: DispatchFromDyn<[MultiGenericParams<#0, #1, #4, #3>]>"#]],
+ );
+ }
+
+ #[test]
+ fn simple_macros_predicates() {
+ check_predicates(
+ r#"
+//- minicore: derive, clone, copy, eq, ord, hash, fmt
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct Simple;
+
+trait Trait {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
+ "#,
+ expect![[r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Debug, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Clone, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Copy, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: PartialEq, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Eq, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: PartialOrd, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Ord, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Hash, polarity:Positive), bound_vars: [] })
+
+ "#]],
+ );
+ }
+
+ #[test]
+ fn coerce_pointee_predicates() {
+ check_predicates(
+ r#"
+//- minicore: derive, coerce_pointee
+use core::marker::CoercePointee;
+
+#[derive(CoercePointee)]
+struct Simple<T: ?Sized>(*const T);
+
+trait Trait<T> {}
+
+#[derive(CoercePointee)]
+struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U)
+where
+ T: Trait<U>,
+ U: Trait<U>;
+ "#,
+ expect![[r#"
+ Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
+
+ "#]],
+ );
+ }
+}