Skip to main content

hir_def/hir/
generics.rs

1//! Pre-type IR item generics
2use std::{ops, sync::LazyLock};
3
4use base_db::SourceDatabase;
5use hir_expand::name::Name;
6use la_arena::{Arena, Idx, RawIdx};
7use salsa::SalsaValue;
8use stdx::impl_from;
9use thin_vec::ThinVec;
10
11use crate::{
12    AdtId, ConstParamId, GenericDefId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
13    expr_store::{ExpressionStore, ExpressionStoreSourceMap},
14    signatures::{
15        ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature,
16        StructSignature, TraitSignature, TypeAliasSignature, UnionSignature,
17    },
18    type_ref::{ConstRef, LifetimeRefId, TypeBound, TypeRefId},
19};
20
21pub type LocalTypeOrConstParamId = Idx<TypeOrConstParamData>;
22pub type LocalLifetimeParamId = Idx<LifetimeParamData>;
23
24/// Data about a generic type parameter (to a function, struct, impl, ...).
25#[derive(Clone, PartialEq, Eq, Debug, Hash)]
26pub struct TypeParamData {
27    /// [`None`] only if the type ref is an [`crate::type_ref::TypeRef::ImplTrait`].
28    pub name: Option<Name>,
29    pub default: Option<TypeRefId>,
30    pub provenance: TypeParamProvenance,
31}
32
33/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
34#[derive(Clone, PartialEq, Eq, Debug, Hash)]
35pub struct LifetimeParamData {
36    pub name: Name,
37    pub bound_type: LifetimeBoundType,
38}
39
40#[derive(Clone, PartialEq, Eq, Debug, Hash)]
41pub enum LifetimeBoundType {
42    EarlyBound,
43    LateBound,
44}
45
46impl LifetimeParamData {
47    #[inline]
48    pub fn is_late_bound(&self) -> bool {
49        self.bound_type == LifetimeBoundType::LateBound
50    }
51
52    #[inline]
53    pub fn is_early_bound(&self) -> bool {
54        self.bound_type == LifetimeBoundType::EarlyBound
55    }
56}
57
58/// Data about a generic const parameter (to a function, struct, impl, ...).
59#[derive(Clone, PartialEq, Eq, Debug, Hash)]
60pub struct ConstParamData {
61    pub name: Name,
62    pub ty: TypeRefId,
63    pub default: Option<ConstRef>,
64}
65
66#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
67pub enum TypeParamProvenance {
68    TypeParamList,
69    TraitSelf,
70    ArgumentImplTrait,
71}
72
73#[derive(Clone, PartialEq, Eq, Debug, Hash, SalsaValue)]
74pub enum TypeOrConstParamData {
75    TypeParamData(TypeParamData),
76    ConstParamData(ConstParamData),
77}
78
79impl TypeOrConstParamData {
80    pub fn name(&self) -> Option<&Name> {
81        match self {
82            TypeOrConstParamData::TypeParamData(it) => it.name.as_ref(),
83            TypeOrConstParamData::ConstParamData(it) => Some(&it.name),
84        }
85    }
86
87    pub fn has_default(&self) -> bool {
88        match self {
89            TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
90            TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
91        }
92    }
93
94    pub fn type_param(&self) -> Option<&TypeParamData> {
95        match self {
96            TypeOrConstParamData::TypeParamData(it) => Some(it),
97            TypeOrConstParamData::ConstParamData(_) => None,
98        }
99    }
100
101    pub fn const_param(&self) -> Option<&ConstParamData> {
102        match self {
103            TypeOrConstParamData::TypeParamData(_) => None,
104            TypeOrConstParamData::ConstParamData(it) => Some(it),
105        }
106    }
107
108    pub fn is_trait_self(&self) -> bool {
109        match self {
110            TypeOrConstParamData::TypeParamData(it) => {
111                it.provenance == TypeParamProvenance::TraitSelf
112            }
113            TypeOrConstParamData::ConstParamData(_) => false,
114        }
115    }
116}
117
118impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData);
119
120#[derive(Clone, PartialEq, Eq, Debug, Hash)]
121pub enum GenericParamData {
122    TypeParamData(TypeParamData),
123    ConstParamData(ConstParamData),
124    LifetimeParamData(LifetimeParamData),
125}
126
127impl GenericParamData {
128    pub fn name(&self) -> Option<&Name> {
129        match self {
130            GenericParamData::TypeParamData(it) => it.name.as_ref(),
131            GenericParamData::ConstParamData(it) => Some(&it.name),
132            GenericParamData::LifetimeParamData(it) => Some(&it.name),
133        }
134    }
135
136    pub fn type_param(&self) -> Option<&TypeParamData> {
137        match self {
138            GenericParamData::TypeParamData(it) => Some(it),
139            _ => None,
140        }
141    }
142
143    pub fn const_param(&self) -> Option<&ConstParamData> {
144        match self {
145            GenericParamData::ConstParamData(it) => Some(it),
146            _ => None,
147        }
148    }
149
150    pub fn lifetime_param(&self) -> Option<&LifetimeParamData> {
151        match self {
152            GenericParamData::LifetimeParamData(it) => Some(it),
153            _ => None,
154        }
155    }
156}
157
158impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
159
160#[derive(Debug, Clone, Copy)]
161pub enum GenericParamDataRef<'a> {
162    TypeParamData(&'a TypeParamData),
163    ConstParamData(&'a ConstParamData),
164    LifetimeParamData(&'a LifetimeParamData),
165}
166
167/// Data about the generic parameters of a function, struct, impl, etc.
168#[derive(PartialEq, Eq, Debug, Hash, Default)]
169pub struct GenericParams {
170    pub(crate) type_or_consts: Arena<TypeOrConstParamData>,
171    pub(crate) lifetimes: Arena<LifetimeParamData>,
172    pub(crate) where_predicates: Box<[WherePredicate]>,
173    pub(crate) early_bound_lifetimes_len: usize,
174}
175
176impl ops::Index<LocalTypeOrConstParamId> for GenericParams {
177    type Output = TypeOrConstParamData;
178    fn index(&self, index: LocalTypeOrConstParamId) -> &TypeOrConstParamData {
179        &self.type_or_consts[index]
180    }
181}
182
183impl ops::Index<LocalLifetimeParamId> for GenericParams {
184    type Output = LifetimeParamData;
185    fn index(&self, index: LocalLifetimeParamId) -> &LifetimeParamData {
186        &self.lifetimes[index]
187    }
188}
189
190/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
191/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
192/// It might still result in multiple actual predicates though, because of
193/// associated type bindings like `Iterator<Item = u32>`.
194#[derive(Clone, PartialEq, Eq, Debug, Hash)]
195pub enum WherePredicate {
196    TypeBound { lifetimes: Option<ThinVec<Name>>, target: TypeRefId, bound: TypeBound },
197    Lifetime { target: LifetimeRefId, bound: LifetimeRefId },
198}
199
200static EMPTY: LazyLock<GenericParams> = LazyLock::new(|| GenericParams {
201    type_or_consts: Arena::default(),
202    lifetimes: Arena::default(),
203    where_predicates: Box::default(),
204    early_bound_lifetimes_len: 0,
205});
206
207impl GenericParams {
208    /// The index of the self param in the generic of the non-parent definition.
209    pub const SELF_PARAM_ID_IN_SELF: la_arena::Idx<TypeOrConstParamData> =
210        LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0));
211
212    #[inline]
213    pub fn empty() -> &'static GenericParams {
214        LazyLock::force(&EMPTY)
215    }
216
217    pub fn of(db: &dyn SourceDatabase, def: GenericDefId) -> &GenericParams {
218        Self::with_store(db, def).0
219    }
220
221    pub fn with_store(
222        db: &dyn SourceDatabase,
223        def: GenericDefId,
224    ) -> (&GenericParams, &ExpressionStore) {
225        match def {
226            GenericDefId::AdtId(AdtId::EnumId(id)) => {
227                let sig = EnumSignature::of(db, id);
228                (&sig.generic_params, &sig.store)
229            }
230            GenericDefId::AdtId(AdtId::StructId(id)) => {
231                let sig = StructSignature::of(db, id);
232                (&sig.generic_params, &sig.store)
233            }
234            GenericDefId::AdtId(AdtId::UnionId(id)) => {
235                let sig = UnionSignature::of(db, id);
236                (&sig.generic_params, &sig.store)
237            }
238            GenericDefId::ConstId(id) => {
239                let sig = ConstSignature::of(db, id);
240                (&EMPTY, &sig.store)
241            }
242            GenericDefId::FunctionId(id) => {
243                let sig = FunctionSignature::of(db, id);
244                (&sig.generic_params, &sig.store)
245            }
246            GenericDefId::ImplId(id) => {
247                let sig = ImplSignature::of(db, id);
248                (&sig.generic_params, &sig.store)
249            }
250            GenericDefId::StaticId(id) => {
251                let sig = StaticSignature::of(db, id);
252                (&EMPTY, &sig.store)
253            }
254            GenericDefId::TraitId(id) => {
255                let sig = TraitSignature::of(db, id);
256                (&sig.generic_params, &sig.store)
257            }
258            GenericDefId::TypeAliasId(id) => {
259                let sig = TypeAliasSignature::of(db, id);
260                (&sig.generic_params, &sig.store)
261            }
262        }
263    }
264
265    pub fn with_source_map(
266        db: &dyn SourceDatabase,
267        def: GenericDefId,
268    ) -> (&GenericParams, &ExpressionStore, &ExpressionStoreSourceMap) {
269        match def {
270            GenericDefId::AdtId(AdtId::EnumId(id)) => {
271                let (sig, sm) = EnumSignature::with_source_map(db, id);
272                (&sig.generic_params, &sig.store, sm)
273            }
274            GenericDefId::AdtId(AdtId::StructId(id)) => {
275                let (sig, sm) = StructSignature::with_source_map(db, id);
276                (&sig.generic_params, &sig.store, sm)
277            }
278            GenericDefId::AdtId(AdtId::UnionId(id)) => {
279                let (sig, sm) = UnionSignature::with_source_map(db, id);
280                (&sig.generic_params, &sig.store, sm)
281            }
282            GenericDefId::ConstId(id) => {
283                let (sig, sm) = ConstSignature::with_source_map(db, id);
284                (&EMPTY, &sig.store, sm)
285            }
286            GenericDefId::FunctionId(id) => {
287                let (sig, sm) = FunctionSignature::with_source_map(db, id);
288                (&sig.generic_params, &sig.store, sm)
289            }
290            GenericDefId::ImplId(id) => {
291                let (sig, sm) = ImplSignature::with_source_map(db, id);
292                (&sig.generic_params, &sig.store, sm)
293            }
294            GenericDefId::StaticId(id) => {
295                let (sig, sm) = StaticSignature::with_source_map(db, id);
296                (&EMPTY, &sig.store, sm)
297            }
298            GenericDefId::TraitId(id) => {
299                let (sig, sm) = TraitSignature::with_source_map(db, id);
300                (&sig.generic_params, &sig.store, sm)
301            }
302            GenericDefId::TypeAliasId(id) => {
303                let (sig, sm) = TypeAliasSignature::with_source_map(db, id);
304                (&sig.generic_params, &sig.store, sm)
305            }
306        }
307    }
308
309    /// Number of Generic parameters (type_or_consts + lifetimes)
310    #[inline]
311    pub fn len(&self) -> usize {
312        self.type_or_consts.len() + self.lifetimes.len()
313    }
314
315    #[inline]
316    pub fn len_no_late(&self) -> usize {
317        self.type_or_consts.len() + self.early_bound_lifetimes_len
318    }
319
320    #[inline]
321    pub fn len_lifetimes(&self) -> usize {
322        self.lifetimes.len()
323    }
324
325    #[inline]
326    pub fn len_early_bound_lifetimes(&self) -> usize {
327        self.early_bound_lifetimes_len
328    }
329
330    #[inline]
331    pub fn len_late_bound_lifetimes(&self) -> usize {
332        self.len_lifetimes() - self.len_early_bound_lifetimes()
333    }
334
335    #[inline]
336    pub fn len_type_or_consts(&self) -> usize {
337        self.type_or_consts.len()
338    }
339
340    #[inline]
341    pub fn is_empty(&self) -> bool {
342        self.len() == 0
343    }
344
345    #[inline]
346    pub fn has_no_predicates(&self) -> bool {
347        self.where_predicates.is_empty()
348    }
349
350    #[inline]
351    pub fn where_predicates(&self) -> &[WherePredicate] {
352        &self.where_predicates
353    }
354
355    /// Iterator of type_or_consts field
356    #[inline]
357    pub fn iter_type_or_consts(
358        &self,
359    ) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
360        self.type_or_consts.iter()
361    }
362
363    /// Iterator of lifetimes field
364    #[inline]
365    pub fn iter_lt(
366        &self,
367    ) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
368        self.lifetimes.iter()
369    }
370
371    #[inline]
372    pub fn iter_early_bound_lt(
373        &self,
374    ) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
375        self.lifetimes.iter().filter(|(_, p)| p.bound_type == LifetimeBoundType::EarlyBound)
376    }
377
378    #[inline]
379    pub fn iter_late_bound_lt(
380        &self,
381    ) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
382        self.lifetimes.iter().filter(|(_, p)| p.bound_type == LifetimeBoundType::LateBound)
383    }
384
385    pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId> {
386        self.type_or_consts.iter().find_map(|(id, p)| {
387            if p.name().as_ref() == Some(&name) && p.type_param().is_some() {
388                Some(TypeParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
389            } else {
390                None
391            }
392        })
393    }
394
395    pub fn find_const_by_name(&self, name: &Name, parent: GenericDefId) -> Option<ConstParamId> {
396        self.type_or_consts.iter().find_map(|(id, p)| {
397            if p.name().as_ref() == Some(&name) && p.const_param().is_some() {
398                Some(ConstParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
399            } else {
400                None
401            }
402        })
403    }
404
405    #[inline]
406    pub fn trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
407        if self.type_or_consts.is_empty() {
408            return None;
409        }
410        matches!(
411            self.type_or_consts[Self::SELF_PARAM_ID_IN_SELF],
412            TypeOrConstParamData::TypeParamData(TypeParamData {
413                provenance: TypeParamProvenance::TraitSelf,
414                ..
415            })
416        )
417        .then(|| Self::SELF_PARAM_ID_IN_SELF)
418    }
419
420    pub fn find_lifetime_by_name(
421        &self,
422        name: &Name,
423        parent: GenericDefId,
424    ) -> Option<LifetimeParamId> {
425        self.lifetimes.iter().find_map(|(id, p)| {
426            if &p.name == name { Some(LifetimeParamId { local_id: id, parent }) } else { None }
427        })
428    }
429
430    pub fn lifetime_param_idx(
431        &self,
432        lifetime_param_id: &LocalLifetimeParamId,
433    ) -> Option<(usize, bool)> {
434        let mut late_bound_idx = 0;
435        self.iter_lt().enumerate().find_map(|(idx, (param_id, param_data))| {
436            let idx = if param_data.is_late_bound() {
437                let prev = late_bound_idx;
438                late_bound_idx += 1;
439                prev
440            } else {
441                idx - late_bound_idx
442            };
443
444            (param_id == *lifetime_param_id).then(|| (idx, param_data.is_late_bound()))
445        })
446    }
447}