blob: e9960374c6f5fcb2a55b411eda6cf3855113dc41 [file] [log] [blame]
//! Various extensions traits for Chalk types.
use chalk_ir::Mutability;
use hir_def::{FunctionId, ItemContainerId, Lookup, TraitId};
use crate::{
AdtId, Binders, CallableDefId, CallableSig, DynTy, Interner, Lifetime, ProjectionTy,
Substitution, ToChalk, TraitRef, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase,
from_assoc_type_id, from_chalk_trait_id, generics::generics, to_chalk_trait_id,
utils::ClosureSubst,
};
pub(crate) trait TyExt {
fn is_unit(&self) -> bool;
fn is_unknown(&self) -> bool;
fn contains_unknown(&self) -> bool;
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
fn as_tuple(&self) -> Option<&Substitution>;
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
fn strip_references(&self) -> &Ty;
/// If this is a `dyn Trait`, returns that trait.
fn dyn_trait(&self) -> Option<TraitId>;
}
impl TyExt for Ty {
fn is_unit(&self) -> bool {
matches!(self.kind(Interner), TyKind::Tuple(0, _))
}
fn is_unknown(&self) -> bool {
matches!(self.kind(Interner), TyKind::Error)
}
fn contains_unknown(&self) -> bool {
self.data(Interner).flags.contains(TypeFlags::HAS_ERROR)
}
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
match self.kind(Interner) {
TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
_ => None,
}
}
fn as_tuple(&self) -> Option<&Substitution> {
match self.kind(Interner) {
TyKind::Tuple(_, substs) => Some(substs),
_ => None,
}
}
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
match self.callable_def(db) {
Some(CallableDefId::FunctionId(func)) => Some(func),
Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None,
}
}
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
match self.kind(Interner) {
TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
_ => None,
}
}
fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
match self.kind(Interner) {
&TyKind::FnDef(def, ..) => Some(ToChalk::from_chalk(db, def)),
_ => None,
}
}
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
match self.kind(Interner) {
TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
TyKind::FnDef(def, parameters) => Some(CallableSig::from_def(db, *def, parameters)),
TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty(db).callable_sig(db),
_ => None,
}
}
fn dyn_trait(&self) -> Option<TraitId> {
let trait_ref = match self.kind(Interner) {
// The principal trait bound should be the first element of the bounds. This is an
// invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
// FIXME: dyn types may not have principal trait and we don't want to return auto trait
// here.
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().first().and_then(|b| {
match b.skip_binders() {
WhereClause::Implemented(trait_ref) => Some(trait_ref),
_ => None,
}
}),
_ => None,
}?;
Some(from_chalk_trait_id(trait_ref.trait_id))
}
fn strip_references(&self) -> &Ty {
let mut t: &Ty = self;
while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(Interner) {
t = ty;
}
t
}
}
pub trait ProjectionTyExt {
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty;
}
impl ProjectionTyExt for ProjectionTy {
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
// FIXME: something like `Split` trait from chalk-solve might be nice.
let generics = generics(db, from_assoc_type_id(self.associated_ty_id).into());
let parent_len = generics.parent_generics().map_or(0, |g| g.len_self());
let substitution =
Substitution::from_iter(Interner, self.substitution.iter(Interner).take(parent_len));
TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
}
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
match from_assoc_type_id(self.associated_ty_id).lookup(db).container {
ItemContainerId::TraitId(it) => it,
_ => panic!("projection ty without parent trait"),
}
}
fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty {
self.trait_ref(db).self_type_parameter(Interner)
}
}
pub(crate) trait DynTyExt {
fn principal(&self) -> Option<Binders<Binders<&TraitRef>>>;
}
impl DynTyExt for DynTy {
fn principal(&self) -> Option<Binders<Binders<&TraitRef>>> {
self.bounds.as_ref().filter_map(|bounds| {
bounds.interned().first().and_then(|b| {
b.as_ref().filter_map(|b| match b {
crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
_ => None,
})
})
})
}
}
pub trait TraitRefExt {
fn hir_trait_id(&self) -> TraitId;
}
impl TraitRefExt for TraitRef {
fn hir_trait_id(&self) -> TraitId {
from_chalk_trait_id(self.trait_id)
}
}