Skip to main content

cairo_lang_semantic/items/
generics.rs

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