use mir_types::{Atomic, Type};
use crate::db::{extends_or_implements, MirDatabase};
fn sup_param_is_free(ty: &Type) -> bool {
ty.is_mixed()
|| ty
.types
.iter()
.all(|a| matches!(a, Atomic::TTemplateParam { .. }))
}
pub(crate) fn is_subtype(db: &dyn MirDatabase, sub: &Type, sup: &Type) -> bool {
if sub.is_subtype_structural(sup) {
return true;
}
if sup.is_mixed() {
return true;
}
if sub.is_never() {
return true;
}
sub.types.iter().all(|a| {
sup.types.iter().any(|b| {
if Type::single(a.clone()).is_subtype_structural(&Type::single(b.clone())) {
return true;
}
match (a, b) {
(
Atomic::TNamedObject {
fqcn: sub_fqcn,
type_params: sub_params,
},
Atomic::TNamedObject {
fqcn: sup_fqcn,
type_params: sup_params,
},
) => {
let params_ok = sup_params.is_empty()
|| sub_params == sup_params
|| sup_params.iter().all(sup_param_is_free)
|| (!sub_params.is_empty()
&& sub_params.iter().all(|p| p.is_mixed() || p.is_never()));
params_ok && extends_or_implements(db, sub_fqcn.as_ref(), sup_fqcn.as_ref())
}
(Atomic::TNamedObject { fqcn: sub_fqcn, .. }, Atomic::TIntersection { parts }) => {
parts.iter().all(|part| {
part.types.iter().any(|part_atomic| match part_atomic {
Atomic::TNamedObject {
fqcn: part_fqcn, ..
} => extends_or_implements(db, sub_fqcn.as_ref(), part_fqcn.as_ref()),
_ => false,
})
})
}
(Atomic::TIntersection { parts }, b) => {
let sup_single = Type::single(b.clone());
parts.iter().any(|part| is_subtype(db, part, &sup_single))
}
_ => false,
}
})
})
}