rust-analyzer/crates/hir-ty/src/generics.rs

274 lines
10 KiB
Rust
Raw Normal View History

2024-06-21 15:24:22 +00:00
//! Utilities for working with generics.
//!
//! The layout for generics as expected by chalk are as follows:
//! - Optional Self parameter
//! - Type or Const parameters
//! - Lifetime parameters
//! - Parent parameters
//!
//! where parent follows the same scheme.
use std::ops;
2024-06-21 08:49:19 +00:00
use chalk_ir::{cast::Cast as _, BoundVar, DebruijnIndex};
use hir_def::{
db::DefDatabase,
generics::{
GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
TypeParamProvenance,
},
2024-06-21 15:24:22 +00:00
ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId,
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
2024-06-21 08:49:19 +00:00
};
use intern::Interned;
use stdx::TupleExt;
2024-06-21 08:49:19 +00:00
2024-06-21 15:24:22 +00:00
use crate::{db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx, Interner, Substitution};
2024-06-21 08:49:19 +00:00
pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
Generics { def, params: db.generic_params(def), parent_generics }
}
#[derive(Clone, Debug)]
pub(crate) struct Generics {
def: GenericDefId,
2024-06-21 15:24:22 +00:00
params: Interned<GenericParams>,
2024-06-21 08:49:19 +00:00
parent_generics: Option<Box<Generics>>,
}
2024-06-21 15:24:22 +00:00
impl<T> ops::Index<T> for Generics
where
GenericParams: ops::Index<T>,
{
type Output = <GenericParams as ops::Index<T>>::Output;
fn index(&self, index: T) -> &Self::Output {
&self.params[index]
2024-06-21 08:49:19 +00:00
}
2024-06-21 15:24:22 +00:00
}
2024-06-21 08:49:19 +00:00
2024-06-21 15:24:22 +00:00
impl Generics {
2024-06-21 08:49:19 +00:00
pub(crate) fn def(&self) -> GenericDefId {
self.def
}
2024-06-21 15:24:22 +00:00
pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
2024-06-21 16:27:05 +00:00
self.iter_self_id().chain(self.iter_parent_id())
2024-06-21 08:49:19 +00:00
}
2024-06-21 15:55:16 +00:00
pub(crate) fn iter_self_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
self.iter_self().map(|(id, _)| id)
}
pub(crate) fn iter_parent_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
2024-06-21 16:27:05 +00:00
self.iter_parent().map(|(id, _)| id)
}
2024-06-21 15:24:22 +00:00
pub(crate) fn iter_self_type_or_consts(
&self,
) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
self.params.iter_type_or_consts()
}
2024-06-21 08:49:19 +00:00
pub(crate) fn iter_self_type_or_consts_id(
&self,
) -> impl DoubleEndedIterator<Item = GenericParamId> + '_ {
self.params.iter_type_or_consts().map(from_toc_id(self)).map(TupleExt::head)
}
pub(crate) fn iter_self_lt_id(&self) -> impl DoubleEndedIterator<Item = GenericParamId> + '_ {
self.params.iter_lt().map(from_lt_id(self)).map(TupleExt::head)
}
2024-06-21 15:24:22 +00:00
/// Iterate over the params followed by the parent params.
pub(crate) fn iter(
&self,
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
self.iter_self().chain(self.iter_parent())
}
2024-06-21 08:49:19 +00:00
2024-06-21 15:24:22 +00:00
/// Iterate over the params without parent params.
pub(crate) fn iter_self(
&self,
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
2024-06-21 08:49:19 +00:00
self.params
.iter_type_or_consts()
.map(from_toc_id(self))
.chain(self.params.iter_lt().map(from_lt_id(self)))
}
/// Iterator over types and const params of parent.
2024-06-21 16:27:05 +00:00
fn iter_parent(
2024-06-21 08:49:19 +00:00
&self,
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
self.parent_generics().into_iter().flat_map(|it| {
2024-06-21 15:24:22 +00:00
let lt_iter = it.params.iter_lt().map(from_lt_id(it));
it.params.iter_type_or_consts().map(from_toc_id(it)).chain(lt_iter)
2024-06-21 08:49:19 +00:00
})
}
/// Returns total number of generic parameters in scope, including those from parent.
pub(crate) fn len(&self) -> usize {
let parent = self.parent_generics().map_or(0, Generics::len);
let child = self.params.len();
parent + child
}
/// Returns numbers of generic parameters excluding those from parent.
pub(crate) fn len_self(&self) -> usize {
self.params.len()
}
/// (parent total, self param, type params, const params, impl trait list, lifetimes)
2024-06-21 15:55:16 +00:00
pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) {
let mut self_param = false;
2024-06-21 08:49:19 +00:00
let mut type_params = 0;
let mut impl_trait_params = 0;
let mut const_params = 0;
self.params.iter_type_or_consts().for_each(|(_, data)| match data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList => type_params += 1,
2024-06-21 15:55:16 +00:00
TypeParamProvenance::TraitSelf => self_param |= true,
2024-06-21 08:49:19 +00:00
TypeParamProvenance::ArgumentImplTrait => impl_trait_params += 1,
},
TypeOrConstParamData::ConstParamData(_) => const_params += 1,
});
2024-06-21 16:27:05 +00:00
let lifetime_params = self.params.iter_lt().count();
2024-06-21 08:49:19 +00:00
let parent_len = self.parent_generics().map_or(0, Generics::len);
2024-06-21 15:55:16 +00:00
(parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params)
2024-06-21 08:49:19 +00:00
}
pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
2024-06-21 16:27:05 +00:00
self.find_type_or_const_param(param)
2024-06-21 08:49:19 +00:00
}
2024-06-21 16:27:05 +00:00
fn find_type_or_const_param(&self, param: TypeOrConstParamId) -> Option<usize> {
2024-06-21 08:49:19 +00:00
if param.parent == self.def {
let idx = param.local_id.into_raw().into_u32() as usize;
debug_assert!(idx <= self.params.len_type_or_consts());
2024-06-21 16:27:05 +00:00
Some(idx)
2024-06-21 08:49:19 +00:00
} else {
2024-06-21 16:27:05 +00:00
debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(param.parent));
2024-06-21 08:49:19 +00:00
self.parent_generics()
.and_then(|g| g.find_type_or_const_param(param))
// Remember that parent parameters come after parameters for self.
2024-06-21 16:27:05 +00:00
.map(|idx| self.len_self() + idx)
2024-06-21 08:49:19 +00:00
}
}
pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> {
2024-06-21 16:27:05 +00:00
self.find_lifetime(lifetime)
2024-06-21 08:49:19 +00:00
}
2024-06-21 16:27:05 +00:00
fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<usize> {
2024-06-21 08:49:19 +00:00
if lifetime.parent == self.def {
let idx = lifetime.local_id.into_raw().into_u32() as usize;
2024-07-02 09:31:02 +00:00
debug_assert!(idx <= self.params.len_lifetimes());
Some(self.params.len_type_or_consts() + idx)
2024-06-21 08:49:19 +00:00
} else {
2024-06-21 16:27:05 +00:00
debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(lifetime.parent));
2024-06-21 08:49:19 +00:00
self.parent_generics()
.and_then(|g| g.find_lifetime(lifetime))
2024-06-21 16:27:05 +00:00
.map(|idx| self.len_self() + idx)
2024-06-21 08:49:19 +00:00
}
}
pub(crate) fn parent_generics(&self) -> Option<&Generics> {
self.parent_generics.as_deref()
}
pub(crate) fn parent_or_self(&self) -> &Generics {
self.parent_generics.as_deref().unwrap_or(self)
}
/// Returns a Substitution that replaces each parameter by a bound variable.
pub(crate) fn bound_vars_subst(
&self,
db: &dyn HirDatabase,
debruijn: DebruijnIndex,
) -> Substitution {
Substitution::from_iter(
Interner,
self.iter_id().enumerate().map(|(idx, id)| match id {
GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx)
.to_const(Interner, db.const_param_ty(id))
.cast(Interner),
GenericParamId::TypeParamId(_) => {
BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner)
}
GenericParamId::LifetimeParamId(_) => {
BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner)
}
}),
)
}
/// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`).
pub(crate) fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution {
Substitution::from_iter(
Interner,
self.iter_id().map(|id| match id {
GenericParamId::TypeParamId(id) => {
2024-06-21 15:24:22 +00:00
to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner)
2024-06-21 08:49:19 +00:00
}
2024-06-21 15:24:22 +00:00
GenericParamId::ConstParamId(id) => to_placeholder_idx(db, id.into())
2024-06-21 08:49:19 +00:00
.to_const(Interner, db.const_param_ty(id))
.cast(Interner),
GenericParamId::LifetimeParamId(id) => {
2024-06-21 15:24:22 +00:00
lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner)
2024-06-21 08:49:19 +00:00
}
}),
)
}
}
fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
let container = match def {
GenericDefId::FunctionId(it) => it.lookup(db).container,
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
GenericDefId::ConstId(it) => it.lookup(db).container,
GenericDefId::AdtId(_)
| GenericDefId::TraitId(_)
| GenericDefId::ImplId(_)
| GenericDefId::TraitAliasId(_) => return None,
};
match container {
ItemContainerId::ImplId(it) => Some(it.into()),
ItemContainerId::TraitId(it) => Some(it.into()),
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
}
}
2024-06-21 15:24:22 +00:00
fn from_toc_id<'a>(
it: &'a Generics,
) -> impl Fn(
(LocalTypeOrConstParamId, &'a TypeOrConstParamData),
) -> (GenericParamId, GenericParamDataRef<'a>) {
move |(local_id, p): (_, _)| {
let id = TypeOrConstParamId { parent: it.def, local_id };
match p {
TypeOrConstParamData::TypeParamData(p) => (
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
GenericParamDataRef::TypeParamData(p),
),
TypeOrConstParamData::ConstParamData(p) => (
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
GenericParamDataRef::ConstParamData(p),
),
}
}
}
fn from_lt_id<'a>(
it: &'a Generics,
) -> impl Fn((LocalLifetimeParamId, &'a LifetimeParamData)) -> (GenericParamId, GenericParamDataRef<'a>)
{
move |(local_id, p): (_, _)| {
(
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
GenericParamDataRef::LifetimeParamData(p),
)
}
}