Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/hir/generics.rs')
-rw-r--r--crates/hir-def/src/hir/generics.rs403
1 files changed, 403 insertions, 0 deletions
diff --git a/crates/hir-def/src/hir/generics.rs b/crates/hir-def/src/hir/generics.rs
new file mode 100644
index 0000000000..a9a0e36312
--- /dev/null
+++ b/crates/hir-def/src/hir/generics.rs
@@ -0,0 +1,403 @@
+//! Pre-type IR item generics
+use std::{ops, sync::LazyLock};
+
+use hir_expand::name::Name;
+use la_arena::{Arena, Idx, RawIdx};
+use stdx::impl_from;
+use thin_vec::ThinVec;
+use triomphe::Arc;
+
+use crate::{
+ AdtId, ConstParamId, GenericDefId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
+ db::DefDatabase,
+ expr_store::{ExpressionStore, ExpressionStoreSourceMap},
+ type_ref::{ConstRef, LifetimeRefId, TypeBound, TypeRefId},
+};
+
+pub type LocalTypeOrConstParamId = Idx<TypeOrConstParamData>;
+pub type LocalLifetimeParamId = Idx<LifetimeParamData>;
+
+/// Data about a generic type parameter (to a function, struct, impl, ...).
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub struct TypeParamData {
+ /// [`None`] only if the type ref is an [`TypeRef::ImplTrait`]. FIXME: Might be better to just
+ /// make it always be a value, giving impl trait a special name.
+ pub name: Option<Name>,
+ pub default: Option<TypeRefId>,
+ pub provenance: TypeParamProvenance,
+}
+
+/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub struct LifetimeParamData {
+ pub name: Name,
+}
+
+/// Data about a generic const parameter (to a function, struct, impl, ...).
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub struct ConstParamData {
+ pub name: Name,
+ pub ty: TypeRefId,
+ pub default: Option<ConstRef>,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub enum TypeParamProvenance {
+ TypeParamList,
+ TraitSelf,
+ ArgumentImplTrait,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub enum TypeOrConstParamData {
+ TypeParamData(TypeParamData),
+ ConstParamData(ConstParamData),
+}
+
+impl TypeOrConstParamData {
+ pub fn name(&self) -> Option<&Name> {
+ match self {
+ TypeOrConstParamData::TypeParamData(it) => it.name.as_ref(),
+ TypeOrConstParamData::ConstParamData(it) => Some(&it.name),
+ }
+ }
+
+ pub fn has_default(&self) -> bool {
+ match self {
+ TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
+ TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
+ }
+ }
+
+ pub fn type_param(&self) -> Option<&TypeParamData> {
+ match self {
+ TypeOrConstParamData::TypeParamData(it) => Some(it),
+ TypeOrConstParamData::ConstParamData(_) => None,
+ }
+ }
+
+ pub fn const_param(&self) -> Option<&ConstParamData> {
+ match self {
+ TypeOrConstParamData::TypeParamData(_) => None,
+ TypeOrConstParamData::ConstParamData(it) => Some(it),
+ }
+ }
+
+ pub fn is_trait_self(&self) -> bool {
+ match self {
+ TypeOrConstParamData::TypeParamData(it) => {
+ it.provenance == TypeParamProvenance::TraitSelf
+ }
+ TypeOrConstParamData::ConstParamData(_) => false,
+ }
+ }
+}
+
+impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData);
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub enum GenericParamData {
+ TypeParamData(TypeParamData),
+ ConstParamData(ConstParamData),
+ LifetimeParamData(LifetimeParamData),
+}
+
+impl GenericParamData {
+ pub fn name(&self) -> Option<&Name> {
+ match self {
+ GenericParamData::TypeParamData(it) => it.name.as_ref(),
+ GenericParamData::ConstParamData(it) => Some(&it.name),
+ GenericParamData::LifetimeParamData(it) => Some(&it.name),
+ }
+ }
+
+ pub fn type_param(&self) -> Option<&TypeParamData> {
+ match self {
+ GenericParamData::TypeParamData(it) => Some(it),
+ _ => None,
+ }
+ }
+
+ pub fn const_param(&self) -> Option<&ConstParamData> {
+ match self {
+ GenericParamData::ConstParamData(it) => Some(it),
+ _ => None,
+ }
+ }
+
+ pub fn lifetime_param(&self) -> Option<&LifetimeParamData> {
+ match self {
+ GenericParamData::LifetimeParamData(it) => Some(it),
+ _ => None,
+ }
+ }
+}
+
+impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
+
+#[derive(Debug, Clone, Copy)]
+pub enum GenericParamDataRef<'a> {
+ TypeParamData(&'a TypeParamData),
+ ConstParamData(&'a ConstParamData),
+ LifetimeParamData(&'a LifetimeParamData),
+}
+
+/// Data about the generic parameters of a function, struct, impl, etc.
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub struct GenericParams {
+ pub(crate) type_or_consts: Arena<TypeOrConstParamData>,
+ pub(crate) lifetimes: Arena<LifetimeParamData>,
+ pub(crate) where_predicates: Box<[WherePredicate]>,
+}
+
+impl ops::Index<LocalTypeOrConstParamId> for GenericParams {
+ type Output = TypeOrConstParamData;
+ fn index(&self, index: LocalTypeOrConstParamId) -> &TypeOrConstParamData {
+ &self.type_or_consts[index]
+ }
+}
+
+impl ops::Index<LocalLifetimeParamId> for GenericParams {
+ type Output = LifetimeParamData;
+ fn index(&self, index: LocalLifetimeParamId) -> &LifetimeParamData {
+ &self.lifetimes[index]
+ }
+}
+
+/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
+/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
+/// It might still result in multiple actual predicates though, because of
+/// associated type bindings like `Iterator<Item = u32>`.
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub enum WherePredicate {
+ TypeBound { target: TypeRefId, bound: TypeBound },
+ Lifetime { target: LifetimeRefId, bound: LifetimeRefId },
+ ForLifetime { lifetimes: ThinVec<Name>, target: TypeRefId, bound: TypeBound },
+}
+
+static EMPTY: LazyLock<Arc<GenericParams>> = LazyLock::new(|| {
+ Arc::new(GenericParams {
+ type_or_consts: Arena::default(),
+ lifetimes: Arena::default(),
+ where_predicates: Box::default(),
+ })
+});
+
+impl GenericParams {
+ /// The index of the self param in the generic of the non-parent definition.
+ pub(crate) const SELF_PARAM_ID_IN_SELF: la_arena::Idx<TypeOrConstParamData> =
+ LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0));
+
+ pub fn new(db: &dyn DefDatabase, def: GenericDefId) -> Arc<GenericParams> {
+ match def {
+ GenericDefId::AdtId(AdtId::EnumId(it)) => db.enum_signature(it).generic_params.clone(),
+ GenericDefId::AdtId(AdtId::StructId(it)) => {
+ db.struct_signature(it).generic_params.clone()
+ }
+ GenericDefId::AdtId(AdtId::UnionId(it)) => {
+ db.union_signature(it).generic_params.clone()
+ }
+ GenericDefId::ConstId(_) => EMPTY.clone(),
+ GenericDefId::FunctionId(function_id) => {
+ db.function_signature(function_id).generic_params.clone()
+ }
+ GenericDefId::ImplId(impl_id) => db.impl_signature(impl_id).generic_params.clone(),
+ GenericDefId::StaticId(_) => EMPTY.clone(),
+ GenericDefId::TraitAliasId(trait_alias_id) => {
+ db.trait_alias_signature(trait_alias_id).generic_params.clone()
+ }
+ GenericDefId::TraitId(trait_id) => db.trait_signature(trait_id).generic_params.clone(),
+ GenericDefId::TypeAliasId(type_alias_id) => {
+ db.type_alias_signature(type_alias_id).generic_params.clone()
+ }
+ }
+ }
+
+ pub fn generic_params_and_store(
+ db: &dyn DefDatabase,
+ def: GenericDefId,
+ ) -> (Arc<GenericParams>, Arc<ExpressionStore>) {
+ match def {
+ GenericDefId::AdtId(AdtId::EnumId(id)) => {
+ let sig = db.enum_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ GenericDefId::AdtId(AdtId::StructId(id)) => {
+ let sig = db.struct_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ GenericDefId::AdtId(AdtId::UnionId(id)) => {
+ let sig = db.union_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ GenericDefId::ConstId(id) => {
+ let sig = db.const_signature(id);
+ (EMPTY.clone(), sig.store.clone())
+ }
+ GenericDefId::FunctionId(id) => {
+ let sig = db.function_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ GenericDefId::ImplId(id) => {
+ let sig = db.impl_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ GenericDefId::StaticId(id) => {
+ let sig = db.static_signature(id);
+ (EMPTY.clone(), sig.store.clone())
+ }
+ GenericDefId::TraitAliasId(id) => {
+ let sig = db.trait_alias_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ GenericDefId::TraitId(id) => {
+ let sig = db.trait_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ GenericDefId::TypeAliasId(id) => {
+ let sig = db.type_alias_signature(id);
+ (sig.generic_params.clone(), sig.store.clone())
+ }
+ }
+ }
+
+ pub fn generic_params_and_store_and_source_map(
+ db: &dyn DefDatabase,
+ def: GenericDefId,
+ ) -> (Arc<GenericParams>, Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>) {
+ match def {
+ GenericDefId::AdtId(AdtId::EnumId(id)) => {
+ let (sig, sm) = db.enum_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::AdtId(AdtId::StructId(id)) => {
+ let (sig, sm) = db.struct_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::AdtId(AdtId::UnionId(id)) => {
+ let (sig, sm) = db.union_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::ConstId(id) => {
+ let (sig, sm) = db.const_signature_with_source_map(id);
+ (EMPTY.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::FunctionId(id) => {
+ let (sig, sm) = db.function_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::ImplId(id) => {
+ let (sig, sm) = db.impl_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::StaticId(id) => {
+ let (sig, sm) = db.static_signature_with_source_map(id);
+ (EMPTY.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::TraitAliasId(id) => {
+ let (sig, sm) = db.trait_alias_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::TraitId(id) => {
+ let (sig, sm) = db.trait_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ GenericDefId::TypeAliasId(id) => {
+ let (sig, sm) = db.type_alias_signature_with_source_map(id);
+ (sig.generic_params.clone(), sig.store.clone(), sm)
+ }
+ }
+ }
+
+ /// Number of Generic parameters (type_or_consts + lifetimes)
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.type_or_consts.len() + self.lifetimes.len()
+ }
+
+ #[inline]
+ pub fn len_lifetimes(&self) -> usize {
+ self.lifetimes.len()
+ }
+
+ #[inline]
+ pub fn len_type_or_consts(&self) -> usize {
+ self.type_or_consts.len()
+ }
+
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ #[inline]
+ pub fn no_predicates(&self) -> bool {
+ self.where_predicates.is_empty()
+ }
+
+ #[inline]
+ pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> {
+ self.where_predicates.iter()
+ }
+
+ /// Iterator of type_or_consts field
+ #[inline]
+ pub fn iter_type_or_consts(
+ &self,
+ ) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
+ self.type_or_consts.iter()
+ }
+
+ /// Iterator of lifetimes field
+ #[inline]
+ pub fn iter_lt(
+ &self,
+ ) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
+ self.lifetimes.iter()
+ }
+
+ pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId> {
+ self.type_or_consts.iter().find_map(|(id, p)| {
+ if p.name().as_ref() == Some(&name) && p.type_param().is_some() {
+ Some(TypeParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
+ } else {
+ None
+ }
+ })
+ }
+
+ pub fn find_const_by_name(&self, name: &Name, parent: GenericDefId) -> Option<ConstParamId> {
+ self.type_or_consts.iter().find_map(|(id, p)| {
+ if p.name().as_ref() == Some(&name) && p.const_param().is_some() {
+ Some(ConstParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
+ } else {
+ None
+ }
+ })
+ }
+
+ #[inline]
+ pub fn trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
+ if self.type_or_consts.is_empty() {
+ return None;
+ }
+ matches!(
+ self.type_or_consts[Self::SELF_PARAM_ID_IN_SELF],
+ TypeOrConstParamData::TypeParamData(TypeParamData {
+ provenance: TypeParamProvenance::TraitSelf,
+ ..
+ })
+ )
+ .then(|| Self::SELF_PARAM_ID_IN_SELF)
+ }
+
+ pub fn find_lifetime_by_name(
+ &self,
+ name: &Name,
+ parent: GenericDefId,
+ ) -> Option<LifetimeParamId> {
+ self.lifetimes.iter().find_map(|(id, p)| {
+ if &p.name == name { Some(LifetimeParamId { local_id: id, parent }) } else { None }
+ })
+ }
+}