//! 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)),
})
}
fn trait_args(trait_: BuiltinDeriveImplTrait, self_ty: Ty<'_>) -> GenericArgs<'_> {
match trait_ {
BuiltinDeriveImplTrait::Copy
| BuiltinDeriveImplTrait::Clone
| BuiltinDeriveImplTrait::Default
| BuiltinDeriveImplTrait::Debug
| BuiltinDeriveImplTrait::Hash
| BuiltinDeriveImplTrait::Eq
| BuiltinDeriveImplTrait::Ord => GenericArgs::new_from_slice(&[self_ty.into()]),
BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => {
GenericArgs::new_from_slice(&[self_ty.into(), self_ty.into()])
}
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
panic!("`CoerceUnsized` and `DispatchFromDyn` have special generics")
}
}
}
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
| 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_from_args(
interner,
trait_id.into(),
trait_args(loc.trait_, 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))]
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_args = trait_args(loc.trait_, param_ty);
let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), trait_args);
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,
loc.trait_,
),
AdtId::UnionId(id) => extend_assoc_type_bounds(
interner,
&mut assoc_type_bounds,
interner.db.field_types(id.into()),
trait_id,
loc.trait_,
),
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,
loc.trait_,
)
}
}
}
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_id: TraitId,
trait_: BuiltinDeriveImplTrait,
) {
struct ProjectionFinder<'a, 'db> {
interner: DbInterner<'db>,
assoc_type_bounds: &'a mut Vec<Clause<'db>>,
trait_id: TraitId,
trait_: BuiltinDeriveImplTrait,
}
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_from_args(
self.interner,
self.trait_id.into(),
trait_args(self.trait_, t),
)
.upcast(self.interner),
);
}
t.super_visit_with(self)
}
}
let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_id, 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 {
type Assoc;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N], T::Assoc);
"#,
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(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): 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(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): 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(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): 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<[#1]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialEq<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, 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(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): 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<[#1]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialOrd<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, 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(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): 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: [] })
Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): 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: [] })
"#]],
);
}
}