1use 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#[derive(Clone, PartialEq, Eq, Debug, Hash)]
26pub struct TypeParamData {
27 pub name: Option<Name>,
29 pub default: Option<TypeRefId>,
30 pub provenance: TypeParamProvenance,
31}
32
33#[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#[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#[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#[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 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 #[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 #[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 #[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}