cairo_lang_semantic/items/
generics.rs

1use std::fmt::Write;
2use std::hash::Hash;
3use std::sync::Arc;
4
5use cairo_lang_debug::DebugWithDb;
6use cairo_lang_defs::ids::{
7    GenericItemId, GenericKind, GenericModuleItemId, GenericParamId, GenericParamLongId,
8    LanguageElementId, LookupItemId, ModuleId, TraitId, TraitTypeId,
9};
10use cairo_lang_diagnostics::{Diagnostics, Maybe, MaybeAsRef, skip_diagnostic};
11use cairo_lang_filesystem::db::FilesGroup;
12use cairo_lang_filesystem::ids::Tracked;
13use cairo_lang_proc_macros::{DebugWithDb, HeapSize, SemanticObject};
14use cairo_lang_syntax as syntax;
15use cairo_lang_syntax::node::ast::{AssociatedItemConstraints, OptionAssociatedItemConstraints};
16use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode, ast};
17use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
18use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
19use cairo_lang_utils::{Intern, extract_matches};
20use salsa::Database;
21use syntax::node::TypedStablePtr;
22
23use super::constant::{ConstValue, ConstValueId};
24use super::imp::{ImplHead, ImplId, ImplLongId, NegativeImplId};
25use super::resolve_trait_path;
26use super::trt::ConcreteTraitTypeId;
27use crate::corelib::CorelibSemantic;
28use crate::diagnostic::{
29    NotFoundItemType, SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder,
30};
31use crate::expr::fmt::CountingWriter;
32use crate::expr::inference::InferenceId;
33use crate::expr::inference::canonic::ResultNoErrEx;
34use crate::items::imp::NegativeImplLongId;
35use crate::items::trt::TraitSemantic;
36use crate::lookup_item::LookupItemEx;
37use crate::resolve::{
38    ResolutionContext, ResolvedConcreteItem, ResolvedGenericItem, Resolver, ResolverData,
39};
40use crate::substitution::SemanticRewriter;
41use crate::types::{
42    ImplTypeId, ShallowGenericArg, TypeHead, maybe_resolve_shallow_generic_arg_type, resolve_type,
43};
44use crate::{ConcreteTraitId, ConcreteTraitLongId, SemanticDiagnostic, TypeId, TypeLongId};
45
46/// Generic argument.
47/// A value assigned to a generic parameter.
48/// May be a type, impl, constant, etc..
49#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
50pub enum GenericArgumentId<'db> {
51    Type(TypeId<'db>),
52    Constant(ConstValueId<'db>),
53    Impl(ImplId<'db>),
54    NegImpl(NegativeImplId<'db>),
55}
56impl<'db> GenericArgumentId<'db> {
57    pub fn kind(&self) -> GenericKind {
58        match self {
59            GenericArgumentId::Type(_) => GenericKind::Type,
60            GenericArgumentId::Constant(_) => GenericKind::Const,
61            GenericArgumentId::Impl(_) => GenericKind::Impl,
62            GenericArgumentId::NegImpl(_) => GenericKind::NegImpl,
63        }
64    }
65    pub fn format(&self, db: &dyn Database) -> String {
66        match self {
67            GenericArgumentId::Type(ty) => ty.format(db),
68            GenericArgumentId::Constant(value) => value.format(db),
69            GenericArgumentId::Impl(imp) => imp.format(db),
70            GenericArgumentId::NegImpl(_) => "_".into(),
71        }
72    }
73    /// Returns the [GenericArgumentHead] for a generic argument if available.
74    pub fn head(&self, db: &'db dyn Database) -> Option<GenericArgumentHead<'db>> {
75        Some(match self {
76            GenericArgumentId::Type(ty) => GenericArgumentHead::Type(ty.head(db)?),
77            GenericArgumentId::Constant(_) => GenericArgumentHead::Const,
78            GenericArgumentId::Impl(impl_id) => GenericArgumentHead::Impl(impl_id.head(db)?),
79            GenericArgumentId::NegImpl(_) => GenericArgumentHead::NegImpl,
80        })
81    }
82    /// Returns true if the generic argument does not depend on any generics.
83    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
84        match self {
85            GenericArgumentId::Type(id) => id.is_fully_concrete(db),
86            GenericArgumentId::Constant(id) => id.is_fully_concrete(db),
87            GenericArgumentId::Impl(id) => id.is_fully_concrete(db),
88            GenericArgumentId::NegImpl(id) => id.is_fully_concrete(db),
89        }
90    }
91    /// Returns true if the generic argument does not depend on impl or type variables.
92    pub fn is_var_free(&self, db: &dyn Database) -> bool {
93        match self {
94            GenericArgumentId::Type(id) => id.is_var_free(db),
95            GenericArgumentId::Constant(id) => id.is_var_free(db),
96            GenericArgumentId::Impl(id) => id.is_var_free(db),
97            GenericArgumentId::NegImpl(id) => id.is_var_free(db),
98        }
99    }
100    /// Short name of the generic argument.
101    pub fn short_name(&self, db: &dyn Database) -> String {
102        if let GenericArgumentId::Type(ty) = self { ty.short_name(db) } else { self.format(db) }
103    }
104
105    /// A utility function for extracting the generic parameters arguments from a generic parameter.
106    pub fn extract_generic_params(
107        &self,
108        db: &'db dyn Database,
109        generic_parameters: &mut OrderedHashSet<GenericParamId<'db>>,
110    ) -> Maybe<()> {
111        match self {
112            GenericArgumentId::Type(ty) => {
113                ty.long(db).extract_generic_params(db, generic_parameters)?
114            }
115            GenericArgumentId::Constant(const_value_id) => {
116                const_value_id.ty(db)?.long(db).extract_generic_params(db, generic_parameters)?
117            }
118            GenericArgumentId::Impl(impl_id) => {
119                for garg in impl_id.concrete_trait(db)?.generic_args(db) {
120                    garg.extract_generic_params(db, generic_parameters)?;
121                }
122            }
123            GenericArgumentId::NegImpl(negative_impl_id) => {
124                for garg in negative_impl_id.concrete_trait(db)?.generic_args(db) {
125                    garg.extract_generic_params(db, generic_parameters)?;
126                }
127            }
128        }
129
130        Ok(())
131    }
132}
133impl<'db> DebugWithDb<'db> for GenericArgumentId<'db> {
134    type Db = dyn Database;
135
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
137        match self {
138            GenericArgumentId::Type(id) => write!(f, "{:?}", id.debug(db)),
139            GenericArgumentId::Constant(id) => write!(f, "{:?}", id.debug(db)),
140            GenericArgumentId::Impl(id) => write!(f, "{:?}", id.debug(db)),
141            GenericArgumentId::NegImpl(_) => write!(f, "_"),
142        }
143    }
144}
145
146/// Head of a generic argument.
147///
148/// A non-param non-variable generic argument has a head, which represents the kind of the root node
149/// in its tree. This is used for caching queries for fast lookups when the generic argument is not
150/// completely inferred yet.
151#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
152pub enum GenericArgumentHead<'db> {
153    Type(TypeHead<'db>),
154    Impl(ImplHead<'db>),
155    Const,
156    NegImpl,
157}
158
159/// Generic parameter.
160#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, HeapSize, salsa::Update)]
161pub enum GenericParam<'db> {
162    Type(GenericParamType<'db>),
163    // TODO(spapini): Add expression.
164    Const(GenericParamConst<'db>),
165    Impl(GenericParamImpl<'db>),
166    NegImpl(GenericParamImpl<'db>),
167}
168impl<'db> GenericParam<'db> {
169    pub fn id(&self) -> GenericParamId<'db> {
170        match self {
171            GenericParam::Type(param) => param.id,
172            GenericParam::Const(param) => param.id,
173            GenericParam::Impl(param) => param.id,
174            GenericParam::NegImpl(param) => param.id,
175        }
176    }
177    pub fn kind(&self) -> GenericKind {
178        match self {
179            GenericParam::Type(_) => GenericKind::Type,
180            GenericParam::Const(_) => GenericKind::Const,
181            GenericParam::Impl(_) => GenericKind::Impl,
182            GenericParam::NegImpl(_) => GenericKind::NegImpl,
183        }
184    }
185    pub fn stable_ptr(&self, db: &'db dyn Database) -> ast::GenericParamPtr<'db> {
186        self.id().stable_ptr(db)
187    }
188    /// Returns the generic param as a generic argument.
189    pub fn as_arg(&self, db: &'db dyn Database) -> GenericArgumentId<'db> {
190        match self {
191            GenericParam::Type(param_type) => {
192                GenericArgumentId::Type(TypeLongId::GenericParameter(param_type.id).intern(db))
193            }
194            GenericParam::Const(param_const) => {
195                GenericArgumentId::Constant(ConstValue::Generic(param_const.id).intern(db))
196            }
197            GenericParam::Impl(param_impl) => {
198                GenericArgumentId::Impl(ImplLongId::GenericParameter(param_impl.id).intern(db))
199            }
200            GenericParam::NegImpl(param_neg_impl) => GenericArgumentId::NegImpl(
201                NegativeImplLongId::GenericParameter(param_neg_impl.id).intern(db),
202            ),
203        }
204    }
205}
206impl<'db> DebugWithDb<'db> for GenericParam<'db> {
207    type Db = dyn Database;
208
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
210        write!(f, "{:?}", self.id().debug(db))
211    }
212}
213
214/// Converts each generic param to a generic argument that passes the same generic param.
215pub fn generic_params_to_args<'db>(
216    params: &[GenericParam<'db>],
217    db: &'db dyn Database,
218) -> Vec<GenericArgumentId<'db>> {
219    params.iter().map(|param| param.as_arg(db)).collect()
220}
221
222#[derive(
223    Copy, Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject, HeapSize, salsa::Update,
224)]
225#[debug_db(dyn Database)]
226pub struct GenericParamType<'db> {
227    pub id: GenericParamId<'db>,
228}
229#[derive(
230    Copy, Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject, HeapSize, salsa::Update,
231)]
232#[debug_db(dyn Database)]
233pub struct GenericParamConst<'db> {
234    pub id: GenericParamId<'db>,
235    pub ty: TypeId<'db>,
236}
237#[derive(
238    Clone, Debug, PartialEq, Eq, Hash, DebugWithDb, SemanticObject, HeapSize, salsa::Update,
239)]
240#[debug_db(dyn Database)]
241pub struct GenericParamImpl<'db> {
242    pub id: GenericParamId<'db>,
243    pub concrete_trait: Maybe<ConcreteTraitId<'db>>,
244    pub type_constraints: OrderedHashMap<TraitTypeId<'db>, TypeId<'db>>,
245}
246
247/// The result of the computation of the semantic model of a generic parameter.
248#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
249#[debug_db(dyn Database)]
250pub struct GenericParamData<'db> {
251    pub generic_param: Maybe<GenericParam<'db>>,
252    pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
253    pub resolver_data: Arc<ResolverData<'db>>,
254}
255
256/// The result of the computation of the semantic model of a generic parameters list.
257#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
258#[debug_db(dyn Database)]
259pub struct GenericParamsData<'db> {
260    pub generic_params: Vec<GenericParam<'db>>,
261    pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
262    pub resolver_data: Arc<ResolverData<'db>>,
263}
264
265/// Query implementation of [GenericsSemantic::generic_impl_param_trait].
266#[salsa::tracked]
267fn generic_impl_param_trait<'db>(
268    db: &'db dyn Database,
269    generic_param_id: GenericParamId<'db>,
270) -> Maybe<TraitId<'db>> {
271    let module_id = generic_param_id.parent_module(db);
272    let option_generic_params_syntax = generic_param_generic_params_list(db, generic_param_id)?;
273    let generic_params_syntax = extract_matches!(
274        option_generic_params_syntax,
275        ast::OptionWrappedGenericParamList::WrappedGenericParamList
276    );
277    let generic_param_syntax = generic_params_syntax
278        .generic_params(db)
279        .elements(db)
280        .find(|param_syntax| {
281            GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db)
282                == generic_param_id
283        })
284        .unwrap();
285
286    let trait_path_syntax = match generic_param_syntax {
287        ast::GenericParam::ImplNamed(syntax) => syntax.trait_path(db),
288        ast::GenericParam::ImplAnonymous(syntax) => syntax.trait_path(db),
289        _ => {
290            panic!("generic_impl_param_trait() called on a non impl generic param.")
291        }
292    };
293
294    let mut diagnostics = SemanticDiagnostics::default();
295    let inference_id = InferenceId::GenericImplParamTrait(generic_param_id);
296    // TODO(spapini): We should not create a new resolver -  we are missing the other generic params
297    // in the context.
298    // Remove also GenericImplParamTrait.
299    let mut resolver = Resolver::new(db, module_id, inference_id);
300
301    resolve_trait_path(db, &mut diagnostics, &mut resolver, &trait_path_syntax)
302}
303
304/// Query implementation of
305/// [GenericsSemantic::generic_impl_param_shallow_trait_generic_args].
306#[salsa::tracked(returns(ref))]
307fn generic_impl_param_shallow_trait_generic_args<'db>(
308    db: &'db dyn Database,
309    generic_param_id: GenericParamId<'db>,
310) -> Maybe<Vec<(GenericParamId<'db>, ShallowGenericArg<'db>)>> {
311    let db: &dyn Database = db;
312    let module_id = generic_param_id.parent_module(db);
313    let mut diagnostics: cairo_lang_diagnostics::DiagnosticsBuilder<'_, SemanticDiagnostic<'_>> =
314        SemanticDiagnostics::default();
315    let parent_item_id = generic_param_id.generic_item(db);
316    let lookup_item: LookupItemId<'_> = parent_item_id.into();
317    let context_resolver_data = lookup_item.resolver_context(db)?;
318    let inference_id = InferenceId::GenericParam(generic_param_id);
319    let mut resolver =
320        Resolver::with_data(db, (*context_resolver_data).clone_with_inference_id(db, inference_id));
321    resolver.set_feature_config(
322        &lookup_item,
323        &lookup_item.untyped_stable_ptr(db).lookup(db),
324        &mut diagnostics,
325    );
326    let generic_params_syntax = extract_matches!(
327        generic_param_generic_params_list(db, generic_param_id)?,
328        ast::OptionWrappedGenericParamList::WrappedGenericParamList
329    );
330
331    let mut opt_generic_param_syntax = None;
332    for param_syntax in generic_params_syntax.generic_params(db).elements(db) {
333        let cur_generic_param_id =
334            GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db);
335        resolver.add_generic_param(cur_generic_param_id);
336
337        if cur_generic_param_id == generic_param_id {
338            opt_generic_param_syntax = Some(param_syntax);
339            break;
340        }
341    }
342
343    let generic_param_syntax =
344        opt_generic_param_syntax.expect("Query called on a non existing generic param.");
345
346    let trait_path_syntax = match generic_param_syntax {
347        ast::GenericParam::ImplNamed(syntax) => syntax.trait_path(db),
348        ast::GenericParam::ImplAnonymous(syntax) => syntax.trait_path(db),
349        ast::GenericParam::NegativeImpl(syntax) => syntax.trait_path(db),
350        _ => {
351            unreachable!(
352                "generic_impl_param_shallow_trait_generic_args called on a non impl generic param."
353            )
354        }
355    };
356
357    let ResolvedGenericItem::Trait(trait_id) = resolver.resolve_generic_path_with_args(
358        &mut diagnostics,
359        &trait_path_syntax,
360        NotFoundItemType::Trait,
361        ResolutionContext::Default,
362    )?
363    else {
364        return Err(skip_diagnostic());
365    };
366    let generic_params = db
367        .trait_generic_params_ids(trait_id)?
368        .iter()
369        .map(|param_syntax| {
370            GenericParamLongId(trait_id.parent_module(db), param_syntax.stable_ptr(db)).intern(db)
371        })
372        .collect::<Vec<_>>();
373
374    let elements = trait_path_syntax.segments(db).elements(db);
375    let Some(last) = elements.last() else {
376        return Ok(Vec::new());
377    };
378
379    match last {
380        ast::PathSegment::Simple(_) => Ok(Vec::new()),
381        ast::PathSegment::WithGenericArgs(path_segment_with_generic_args) => {
382            let generic_args =
383                path_segment_with_generic_args.generic_args(db).generic_args(db).elements_vec(db);
384
385            let arg_syntax_per_param = resolver.get_arg_syntax_per_param(
386                &mut diagnostics,
387                &generic_params,
388                &generic_args,
389            )?;
390            Ok(generic_params
391                .iter()
392                .filter_map(|generic_param| {
393                    let expr = arg_syntax_per_param.get(generic_param)?;
394                    let x = maybe_resolve_shallow_generic_arg_type(
395                        db,
396                        &mut diagnostics,
397                        &mut resolver,
398                        expr,
399                    )?;
400                    Some((*generic_param, x))
401                })
402                .collect::<Vec<_>>())
403        }
404        ast::PathSegment::Missing(_) => Ok(Vec::new()),
405    }
406}
407
408/// Returns data about a generic param.
409#[salsa::tracked(cycle_result=generic_param_data_cycle, returns(ref))]
410fn generic_param_data<'db>(
411    db: &'db dyn Database,
412    generic_param_id: GenericParamId<'db>,
413    in_cycle: bool,
414) -> Maybe<GenericParamData<'db>> {
415    if in_cycle {
416        let mut diagnostics = SemanticDiagnostics::default();
417        return Ok(GenericParamData {
418            generic_param: Err(diagnostics.report(
419                generic_param_id.stable_ptr(db).untyped(),
420                SemanticDiagnosticKind::ImplRequirementCycle,
421            )),
422            diagnostics: diagnostics.build(),
423            resolver_data: Arc::new(ResolverData::new(
424                generic_param_id.parent_module(db),
425                InferenceId::GenericParam(generic_param_id),
426            )),
427        });
428    }
429    let module_id = generic_param_id.parent_module(db);
430    let mut diagnostics = SemanticDiagnostics::default();
431    let parent_item_id = generic_param_id.generic_item(db);
432    let lookup_item: LookupItemId<'_> = parent_item_id.into();
433    let context_resolver_data = lookup_item.resolver_context(db)?;
434    let inference_id = InferenceId::GenericParam(generic_param_id);
435    let mut resolver =
436        Resolver::with_data(db, (*context_resolver_data).clone_with_inference_id(db, inference_id));
437    resolver.set_feature_config(
438        &lookup_item,
439        &lookup_item.untyped_stable_ptr(db).lookup(db),
440        &mut diagnostics,
441    );
442    let generic_params_syntax = extract_matches!(
443        generic_param_generic_params_list(db, generic_param_id)?,
444        ast::OptionWrappedGenericParamList::WrappedGenericParamList
445    );
446
447    let mut opt_generic_param_syntax = None;
448    for param_syntax in generic_params_syntax.generic_params(db).elements(db) {
449        let cur_generic_param_id =
450            GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db);
451        resolver.add_generic_param(cur_generic_param_id);
452
453        if cur_generic_param_id == generic_param_id {
454            opt_generic_param_syntax = Some(param_syntax);
455        }
456    }
457    let generic_param_syntax =
458        opt_generic_param_syntax.expect("Query called on a non existing generic param.");
459    let param_semantic = semantic_from_generic_param_ast(
460        db,
461        &mut resolver,
462        &mut diagnostics,
463        module_id,
464        &generic_param_syntax,
465        parent_item_id,
466    )?;
467    let inference = &mut resolver.inference();
468    inference.finalize(&mut diagnostics, generic_param_syntax.stable_ptr(db).untyped());
469
470    let param_semantic = inference.rewrite(param_semantic).no_err();
471    let resolver_data = Arc::new(resolver.data);
472    Ok(GenericParamData {
473        generic_param: Ok(param_semantic),
474        diagnostics: diagnostics.build(),
475        resolver_data,
476    })
477}
478
479/// Cycle handling for [generic_param_data].
480fn generic_param_data_cycle<'db>(
481    db: &'db dyn Database,
482    generic_param_id: GenericParamId<'db>,
483    _in_cycle: bool,
484) -> Maybe<GenericParamData<'db>> {
485    generic_param_data(db, generic_param_id, true).clone()
486}
487
488/// Query implementation of [GenericsSemantic::generic_params_type_constraints].
489#[salsa::tracked(returns(ref))]
490fn generic_params_type_constraints<'db>(
491    db: &'db dyn Database,
492    _tracked: Tracked,
493    generic_params: Vec<GenericParamId<'db>>,
494) -> Vec<(TypeId<'db>, TypeId<'db>)> {
495    let mut constraints = vec![];
496    for param in &generic_params {
497        let Ok(GenericParam::Impl(imp)) = db.generic_param_semantic(*param) else {
498            continue;
499        };
500        let Ok(concrete_trait_id) = imp.concrete_trait else {
501            continue;
502        };
503        for (trait_ty, ty1) in imp.type_constraints {
504            let impl_type = TypeLongId::ImplType(ImplTypeId::new(
505                ImplLongId::GenericParameter(*param).intern(db),
506                trait_ty,
507                db,
508            ))
509            .intern(db);
510            constraints.push((impl_type, ty1));
511        }
512        let ConcreteTraitLongId { trait_id, generic_args } = concrete_trait_id.long(db);
513        if trait_id != &db.core_info().type_eq_trt {
514            continue;
515        }
516        let [GenericArgumentId::Type(ty0), GenericArgumentId::Type(ty1)] = generic_args.as_slice()
517        else {
518            unreachable!("TypeEqual should have 2 arguments");
519        };
520        constraints.push((*ty0, *ty1));
521    }
522    constraints
523}
524
525// --- Helpers ---
526
527/// Returns the generic parameters list AST node of a generic parameter.
528fn generic_param_generic_params_list<'db>(
529    db: &'db dyn Database,
530    generic_param_id: GenericParamId<'db>,
531) -> Maybe<ast::OptionWrappedGenericParamList<'db>> {
532    let generic_param_long_id = generic_param_id.long(db);
533
534    // The generic params list is 2 level up the tree.
535    let wrapped_generic_param_list = generic_param_long_id.1.0.nth_parent(db, 2);
536
537    Ok(ast::OptionWrappedGenericParamListPtr(wrapped_generic_param_list).lookup(db))
538}
539
540/// Returns the semantic model of a generic parameters list given the list AST, and updates the
541/// diagnostics and resolver accordingly.
542pub fn semantic_generic_params<'db>(
543    db: &'db dyn Database,
544    diagnostics: &mut SemanticDiagnostics<'db>,
545    resolver: &mut Resolver<'db>,
546    module_id: ModuleId<'db>,
547    generic_params: &ast::OptionWrappedGenericParamList<'db>,
548) -> Vec<GenericParam<'db>> {
549    semantic_generic_params_ex(db, diagnostics, resolver, module_id, generic_params, false)
550}
551
552pub fn semantic_generic_params_ex<'db>(
553    db: &'db dyn Database,
554    diagnostics: &mut SemanticDiagnostics<'db>,
555    resolver: &mut Resolver<'db>,
556    module_id: ModuleId<'db>,
557    generic_params: &ast::OptionWrappedGenericParamList<'db>,
558    in_cycle: bool,
559) -> Vec<GenericParam<'db>> {
560    match generic_params {
561        syntax::node::ast::OptionWrappedGenericParamList::Empty(_) => vec![],
562        syntax::node::ast::OptionWrappedGenericParamList::WrappedGenericParamList(syntax) => syntax
563            .generic_params(db)
564            .elements(db)
565            .filter_map(|param_syntax| {
566                let generic_param_id =
567                    GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db);
568                let data = generic_param_data(db, generic_param_id, in_cycle).as_ref().ok()?;
569                let generic_param = data.generic_param.clone();
570                diagnostics.extend(data.diagnostics.clone());
571                resolver.add_generic_param(generic_param_id);
572                resolver.data.used_uses.extend(data.resolver_data.used_uses.iter().copied());
573                generic_param.ok()
574            })
575            .collect(),
576    }
577}
578
579/// Returns true if negative impls are enabled in the module.
580fn are_negative_impls_enabled<'db>(db: &dyn Database, module_id: ModuleId<'db>) -> bool {
581    let owning_crate = module_id.owning_crate(db);
582    let Some(config) = db.crate_config(owning_crate) else { return false };
583    config.settings.experimental_features.negative_impls
584}
585
586/// Returns true if associated_item_constraints is enabled in the module.
587fn is_associated_item_constraints_enabled(db: &dyn Database, module_id: ModuleId<'_>) -> bool {
588    let owning_crate = module_id.owning_crate(db);
589    db.crate_config(owning_crate)
590        .is_some_and(|c| c.settings.experimental_features.associated_item_constraints)
591}
592
593/// Computes the semantic model of a generic parameter give its ast.
594fn semantic_from_generic_param_ast<'db>(
595    db: &'db dyn Database,
596    resolver: &mut Resolver<'db>,
597    diagnostics: &mut SemanticDiagnostics<'db>,
598    module_id: ModuleId<'db>,
599    param_syntax: &ast::GenericParam<'db>,
600    parent_item_id: GenericItemId<'db>,
601) -> Maybe<GenericParam<'db>> {
602    let id = GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db);
603    let mut item_constraints_into_option = |constraint| match constraint {
604        OptionAssociatedItemConstraints::Empty(_) => None,
605        OptionAssociatedItemConstraints::AssociatedItemConstraints(associated_type_args) => {
606            if !is_associated_item_constraints_enabled(db, module_id) {
607                diagnostics.report(
608                    associated_type_args.stable_ptr(db),
609                    SemanticDiagnosticKind::TypeConstraintsSyntaxNotEnabled,
610                );
611            }
612            Some(associated_type_args)
613        }
614    };
615    Ok(match param_syntax {
616        ast::GenericParam::Type(_) => GenericParam::Type(GenericParamType { id }),
617        ast::GenericParam::Const(syntax) => {
618            let ty = resolve_type(db, diagnostics, resolver, &syntax.ty(db));
619            GenericParam::Const(GenericParamConst { id, ty })
620        }
621        ast::GenericParam::ImplNamed(syntax) => {
622            let path_syntax = syntax.trait_path(db);
623            let item_constrains = item_constraints_into_option(syntax.type_constrains(db));
624            GenericParam::Impl(impl_generic_param_semantic(
625                db,
626                resolver,
627                diagnostics,
628                &path_syntax,
629                item_constrains,
630                id,
631            ))
632        }
633        ast::GenericParam::ImplAnonymous(syntax) => {
634            let path_syntax = syntax.trait_path(db);
635            let item_constrains = item_constraints_into_option(syntax.type_constrains(db));
636            GenericParam::Impl(impl_generic_param_semantic(
637                db,
638                resolver,
639                diagnostics,
640                &path_syntax,
641                item_constrains,
642                id,
643            ))
644        }
645        ast::GenericParam::NegativeImpl(syntax) => {
646            if !are_negative_impls_enabled(db, module_id) {
647                diagnostics.report(
648                    param_syntax.stable_ptr(db),
649                    SemanticDiagnosticKind::NegativeImplsNotEnabled,
650                );
651            }
652
653            if !matches!(parent_item_id, GenericItemId::ModuleItem(GenericModuleItemId::Impl(_))) {
654                diagnostics.report(
655                    param_syntax.stable_ptr(db),
656                    SemanticDiagnosticKind::NegativeImplsOnlyOnImpls,
657                );
658            }
659
660            let path_syntax = syntax.trait_path(db);
661
662            let neg_impl =
663                impl_generic_param_semantic(db, resolver, diagnostics, &path_syntax, None, id);
664            for param in db.trait_generic_params(neg_impl.concrete_trait?.trait_id(db))? {
665                if matches!(param, GenericParam::Type(_) | GenericParam::Const(_)) {
666                    continue;
667                }
668                diagnostics.report(
669                    param.stable_ptr(db),
670                    SemanticDiagnosticKind::OnlyTypeOrConstParamsInNegImpl,
671                );
672            }
673
674            GenericParam::NegImpl(neg_impl)
675        }
676    })
677}
678
679/// Computes the semantic model of an impl generic parameter given its trait path.
680fn impl_generic_param_semantic<'db>(
681    db: &'db dyn Database,
682    resolver: &mut Resolver<'db>,
683    diagnostics: &mut SemanticDiagnostics<'db>,
684    path_syntax: &ast::ExprPath<'db>,
685    item_constraints: Option<AssociatedItemConstraints<'db>>,
686    id: GenericParamId<'db>,
687) -> GenericParamImpl<'db> {
688    let concrete_trait = resolver
689        .resolve_concrete_path(diagnostics, path_syntax, NotFoundItemType::Trait)
690        .and_then(|resolved_item| match resolved_item {
691            ResolvedConcreteItem::Trait(id) | ResolvedConcreteItem::SelfTrait(id) => Ok(id),
692            _ => Err(diagnostics
693                .report(path_syntax.stable_ptr(db), SemanticDiagnosticKind::UnknownTrait)),
694        });
695    let type_constraints = concrete_trait
696        .ok()
697        .and_then(|concrete_trait| {
698            item_constraints.map(|type_constraints| (concrete_trait, type_constraints))
699        })
700        .map(|(concrete_trait_id, constraints)| {
701            let mut map = OrderedHashMap::default();
702
703            for constraint in constraints.associated_item_constraints(db).elements(db) {
704                let Ok(trait_type_id_opt) = db.trait_type_by_name(
705                    concrete_trait_id.trait_id(db),
706                    constraint.item(db).text(db),
707                ) else {
708                    continue;
709                };
710                let Some(trait_type_id) = trait_type_id_opt else {
711                    diagnostics.report(
712                        constraint.stable_ptr(db),
713                        SemanticDiagnosticKind::NonTraitTypeConstrained {
714                            identifier: constraint.item(db).text(db),
715                            concrete_trait_id,
716                        },
717                    );
718                    return map;
719                };
720
721                let concrete_trait_type_id =
722                    ConcreteTraitTypeId::new_from_data(db, concrete_trait_id, trait_type_id);
723                match map.entry(trait_type_id) {
724                    Entry::Vacant(entry) => {
725                        entry.insert(resolve_type(
726                            db,
727                            diagnostics,
728                            resolver,
729                            &constraint.value(db),
730                        ));
731                    }
732                    Entry::Occupied(_) => {
733                        diagnostics.report(
734                            path_syntax.stable_ptr(db),
735                            SemanticDiagnosticKind::DuplicateTypeConstraint {
736                                concrete_trait_type_id,
737                            },
738                        );
739                    }
740                }
741            }
742            map
743        })
744        .unwrap_or_default();
745
746    GenericParamImpl { id, concrete_trait, type_constraints }
747}
748
749/// Formats a list of generic arguments.
750pub fn fmt_generic_args(
751    generic_args: &[GenericArgumentId<'_>],
752    f: &mut CountingWriter<'_, '_>,
753    db: &dyn Database,
754) -> std::fmt::Result {
755    let mut generic_args = generic_args.iter();
756    if let Some(first) = generic_args.next() {
757        // Soft limit for the number of chars in the formatted type.
758        const CHARS_BOUND: usize = 500;
759        write!(f, "::<")?;
760        write!(f, "{}", &first.format(db))?;
761
762        for arg in generic_args {
763            write!(f, ", ")?;
764            if f.count() > CHARS_BOUND {
765                // If the formatted type is becoming too long, add short version of arguments.
766                write!(f, "{}", &arg.short_name(db))?;
767            } else {
768                write!(f, "{}", &arg.format(db))?;
769            }
770        }
771        write!(f, ">")?;
772    }
773    Ok(())
774}
775
776/// Trait for generic param-related semantic queries.
777pub trait GenericParamSemantic<'db>: Database {
778    /// Returns the semantic data of a generic param.
779    fn generic_param_semantic(
780        &'db self,
781        generic_param: GenericParamId<'db>,
782    ) -> Maybe<GenericParam<'db>> {
783        generic_param_data(self.as_dyn_database(), generic_param, false)
784            .maybe_as_ref()?
785            .generic_param
786            .clone()
787    }
788    /// Returns the semantic diagnostics of a generic param.
789    fn generic_param_diagnostics(
790        &'db self,
791        generic_param: GenericParamId<'db>,
792    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
793        generic_param_data(self.as_dyn_database(), generic_param, false)
794            .as_ref()
795            .map(|data| data.diagnostics.clone())
796            .unwrap_or_default()
797    }
798    /// Returns the resolver data of a generic param.
799    fn generic_param_resolver_data(
800        &'db self,
801        generic_param: GenericParamId<'db>,
802    ) -> Maybe<Arc<ResolverData<'db>>> {
803        Ok(generic_param_data(self.as_dyn_database(), generic_param, false)
804            .maybe_as_ref()?
805            .resolver_data
806            .clone())
807    }
808    /// Returns the trait a generic param impl should implement.
809    /// Panics if the generic param is not an impl generic param.
810    fn generic_impl_param_trait(
811        &'db self,
812        generic_param_id: GenericParamId<'db>,
813    ) -> Maybe<TraitId<'db>> {
814        generic_impl_param_trait(self.as_dyn_database(), generic_param_id)
815    }
816    /// Returns the shallow generic args of a generic impl param.
817    fn generic_impl_param_shallow_trait_generic_args(
818        &'db self,
819        generic_param: GenericParamId<'db>,
820    ) -> Maybe<&'db [(GenericParamId<'db>, ShallowGenericArg<'db>)]> {
821        Ok(generic_impl_param_shallow_trait_generic_args(self.as_dyn_database(), generic_param)
822            .maybe_as_ref()?)
823    }
824    /// Returns the type constraints introduced by the generic params.
825    fn generic_params_type_constraints(
826        &'db self,
827        generic_params: Vec<GenericParamId<'db>>,
828    ) -> &'db [(TypeId<'db>, TypeId<'db>)] {
829        generic_params_type_constraints(self.as_dyn_database(), (), generic_params)
830    }
831}
832impl<'db, T: Database + ?Sized> GenericParamSemantic<'db> for T {}