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    /// Uses memoization via `visited` to avoid re-traversing shared subtypes in DAG structures.
106    pub fn extract_generic_params(
107        &self,
108        db: &'db dyn Database,
109        generic_parameters: &mut OrderedHashSet<GenericParamId<'db>>,
110        visited: &mut OrderedHashSet<TypeId<'db>>,
111    ) -> Maybe<()> {
112        match self {
113            GenericArgumentId::Type(ty) => {
114                ty.extract_generic_params(db, generic_parameters, visited)?
115            }
116            GenericArgumentId::Constant(const_value_id) => {
117                const_value_id.extract_generic_params(db, generic_parameters, visited)?;
118            }
119            GenericArgumentId::Impl(impl_id) => {
120                for garg in impl_id.concrete_trait(db)?.generic_args(db) {
121                    garg.extract_generic_params(db, generic_parameters, visited)?;
122                }
123            }
124            GenericArgumentId::NegImpl(negative_impl_id) => {
125                for garg in negative_impl_id.concrete_trait(db)?.generic_args(db) {
126                    garg.extract_generic_params(db, generic_parameters, visited)?;
127                }
128            }
129        }
130
131        Ok(())
132    }
133}
134impl<'db> DebugWithDb<'db> for GenericArgumentId<'db> {
135    type Db = dyn Database;
136
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
138        match self {
139            GenericArgumentId::Type(id) => write!(f, "{:?}", id.debug(db)),
140            GenericArgumentId::Constant(id) => write!(f, "{:?}", id.debug(db)),
141            GenericArgumentId::Impl(id) => write!(f, "{:?}", id.debug(db)),
142            GenericArgumentId::NegImpl(_) => write!(f, "_"),
143        }
144    }
145}
146
147/// Head of a generic argument.
148///
149/// A non-param non-variable generic argument has a head, which represents the kind of the root node
150/// in its tree. This is used for caching queries for fast lookups when the generic argument is not
151/// completely inferred yet.
152#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
153pub enum GenericArgumentHead<'db> {
154    Type(TypeHead<'db>),
155    Impl(ImplHead<'db>),
156    Const,
157    NegImpl,
158}
159
160/// Generic parameter.
161#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, HeapSize, salsa::Update)]
162pub enum GenericParam<'db> {
163    Type(GenericParamType<'db>),
164    // TODO(spapini): Add expression.
165    Const(GenericParamConst<'db>),
166    Impl(GenericParamImpl<'db>),
167    NegImpl(GenericParamImpl<'db>),
168}
169impl<'db> GenericParam<'db> {
170    pub fn id(&self) -> GenericParamId<'db> {
171        match self {
172            GenericParam::Type(param) => param.id,
173            GenericParam::Const(param) => param.id,
174            GenericParam::Impl(param) => param.id,
175            GenericParam::NegImpl(param) => param.id,
176        }
177    }
178    pub fn kind(&self) -> GenericKind {
179        match self {
180            GenericParam::Type(_) => GenericKind::Type,
181            GenericParam::Const(_) => GenericKind::Const,
182            GenericParam::Impl(_) => GenericKind::Impl,
183            GenericParam::NegImpl(_) => GenericKind::NegImpl,
184        }
185    }
186    pub fn stable_ptr(&self, db: &'db dyn Database) -> ast::GenericParamPtr<'db> {
187        self.id().stable_ptr(db)
188    }
189    /// Returns the generic param as a generic argument.
190    pub fn as_arg(&self, db: &'db dyn Database) -> GenericArgumentId<'db> {
191        match self {
192            GenericParam::Type(param_type) => {
193                GenericArgumentId::Type(TypeLongId::GenericParameter(param_type.id).intern(db))
194            }
195            GenericParam::Const(param_const) => {
196                GenericArgumentId::Constant(ConstValue::Generic(param_const.id).intern(db))
197            }
198            GenericParam::Impl(param_impl) => {
199                GenericArgumentId::Impl(ImplLongId::GenericParameter(param_impl.id).intern(db))
200            }
201            GenericParam::NegImpl(param_neg_impl) => GenericArgumentId::NegImpl(
202                NegativeImplLongId::GenericParameter(param_neg_impl.id).intern(db),
203            ),
204        }
205    }
206}
207impl<'db> DebugWithDb<'db> for GenericParam<'db> {
208    type Db = dyn Database;
209
210    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
211        write!(f, "{:?}", self.id().debug(db))
212    }
213}
214
215/// Converts each generic param to a generic argument that passes the same generic param.
216pub fn generic_params_to_args<'db>(
217    params: &[GenericParam<'db>],
218    db: &'db dyn Database,
219) -> Vec<GenericArgumentId<'db>> {
220    params.iter().map(|param| param.as_arg(db)).collect()
221}
222
223#[derive(
224    Copy, Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject, HeapSize, salsa::Update,
225)]
226#[debug_db(dyn Database)]
227pub struct GenericParamType<'db> {
228    pub id: GenericParamId<'db>,
229}
230#[derive(
231    Copy, Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject, HeapSize, salsa::Update,
232)]
233#[debug_db(dyn Database)]
234pub struct GenericParamConst<'db> {
235    pub id: GenericParamId<'db>,
236    pub ty: TypeId<'db>,
237}
238#[derive(
239    Clone, Debug, PartialEq, Eq, Hash, DebugWithDb, SemanticObject, HeapSize, salsa::Update,
240)]
241#[debug_db(dyn Database)]
242pub struct GenericParamImpl<'db> {
243    pub id: GenericParamId<'db>,
244    pub concrete_trait: Maybe<ConcreteTraitId<'db>>,
245    pub type_constraints: OrderedHashMap<TraitTypeId<'db>, TypeId<'db>>,
246}
247
248/// The result of the computation of the semantic model of a generic parameter.
249#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
250#[debug_db(dyn Database)]
251pub struct GenericParamData<'db> {
252    pub generic_param: Maybe<GenericParam<'db>>,
253    pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
254    pub resolver_data: Arc<ResolverData<'db>>,
255}
256
257/// The result of the computation of the semantic model of a generic parameters list.
258#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, salsa::Update)]
259#[debug_db(dyn Database)]
260pub struct GenericParamsData<'db> {
261    pub generic_params: Vec<GenericParam<'db>>,
262    pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
263    pub resolver_data: Arc<ResolverData<'db>>,
264}
265
266/// Query implementation of [GenericsSemantic::generic_impl_param_trait].
267#[salsa::tracked]
268fn generic_impl_param_trait<'db>(
269    db: &'db dyn Database,
270    generic_param_id: GenericParamId<'db>,
271) -> Maybe<TraitId<'db>> {
272    let module_id = generic_param_id.parent_module(db);
273    let option_generic_params_syntax = generic_param_generic_params_list(db, generic_param_id)?;
274    let generic_params_syntax = extract_matches!(
275        option_generic_params_syntax,
276        ast::OptionWrappedGenericParamList::WrappedGenericParamList
277    );
278    let generic_param_syntax = generic_params_syntax
279        .generic_params(db)
280        .elements(db)
281        .find(|param_syntax| {
282            GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db)
283                == generic_param_id
284        })
285        .unwrap();
286
287    let trait_path_syntax = match generic_param_syntax {
288        ast::GenericParam::ImplNamed(syntax) => syntax.trait_path(db),
289        ast::GenericParam::ImplAnonymous(syntax) => syntax.trait_path(db),
290        _ => {
291            panic!("generic_impl_param_trait() called on a non impl generic param.")
292        }
293    };
294
295    let mut diagnostics = SemanticDiagnostics::new(module_id);
296    let inference_id = InferenceId::GenericImplParamTrait(generic_param_id);
297    // TODO(spapini): We should not create a new resolver -  we are missing the other generic params
298    // in the context.
299    // Remove also GenericImplParamTrait.
300    let mut resolver = Resolver::new(db, module_id, inference_id);
301
302    resolve_trait_path(db, &mut diagnostics, &mut resolver, &trait_path_syntax)
303}
304
305/// Query implementation of
306/// [GenericsSemantic::generic_impl_param_shallow_trait_generic_args].
307#[salsa::tracked(returns(ref))]
308fn generic_impl_param_shallow_trait_generic_args<'db>(
309    db: &'db dyn Database,
310    generic_param_id: GenericParamId<'db>,
311) -> Maybe<Vec<(GenericParamId<'db>, ShallowGenericArg<'db>)>> {
312    let db: &dyn Database = db;
313    let module_id = generic_param_id.parent_module(db);
314    let mut diagnostics = SemanticDiagnostics::new(module_id);
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    let module_id = generic_param_id.parent_module(db);
416    if in_cycle {
417        let mut diagnostics = SemanticDiagnostics::new(module_id);
418        return Ok(GenericParamData {
419            generic_param: Err(diagnostics.report(
420                generic_param_id.stable_ptr(db).untyped(),
421                SemanticDiagnosticKind::ImplRequirementCycle,
422            )),
423            diagnostics: diagnostics.build(),
424            resolver_data: Arc::new(ResolverData::new(
425                generic_param_id.parent_module(db),
426                InferenceId::GenericParam(generic_param_id),
427            )),
428        });
429    }
430    let mut diagnostics = SemanticDiagnostics::new(module_id);
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    _id: salsa::Id,
483    generic_param_id: GenericParamId<'db>,
484    _in_cycle: bool,
485) -> Maybe<GenericParamData<'db>> {
486    generic_param_data(db, generic_param_id, true).clone()
487}
488
489/// Query implementation of [GenericsSemantic::generic_params_type_constraints].
490#[salsa::tracked(returns(ref))]
491fn generic_params_type_constraints<'db>(
492    db: &'db dyn Database,
493    _tracked: Tracked,
494    generic_params: Vec<GenericParamId<'db>>,
495) -> Vec<(TypeId<'db>, TypeId<'db>)> {
496    let mut constraints = vec![];
497    for param in &generic_params {
498        let Ok(GenericParam::Impl(imp)) = db.generic_param_semantic(*param) else {
499            continue;
500        };
501        let Ok(concrete_trait_id) = imp.concrete_trait else {
502            continue;
503        };
504        for (trait_ty, ty1) in imp.type_constraints {
505            let impl_type = TypeLongId::ImplType(ImplTypeId::new(
506                ImplLongId::GenericParameter(*param).intern(db),
507                trait_ty,
508                db,
509            ))
510            .intern(db);
511            constraints.push((impl_type, ty1));
512        }
513        let ConcreteTraitLongId { trait_id, generic_args } = concrete_trait_id.long(db);
514        if trait_id != &db.core_info().type_eq_trt {
515            continue;
516        }
517        let [GenericArgumentId::Type(ty0), GenericArgumentId::Type(ty1)] = generic_args.as_slice()
518        else {
519            unreachable!("TypeEqual should have 2 arguments");
520        };
521        constraints.push((*ty0, *ty1));
522    }
523    constraints
524}
525
526// --- Helpers ---
527
528/// Returns the generic parameters list AST node of a generic parameter.
529fn generic_param_generic_params_list<'db>(
530    db: &'db dyn Database,
531    generic_param_id: GenericParamId<'db>,
532) -> Maybe<ast::OptionWrappedGenericParamList<'db>> {
533    let generic_param_long_id = generic_param_id.long(db);
534
535    // The generic params list is 2 level up the tree.
536    let wrapped_generic_param_list = generic_param_long_id.1.0.nth_parent(db, 2);
537
538    Ok(ast::OptionWrappedGenericParamListPtr(wrapped_generic_param_list).lookup(db))
539}
540
541/// Returns the semantic model of a generic parameters list given the list AST, and updates the
542/// diagnostics and resolver accordingly.
543pub fn semantic_generic_params<'db>(
544    db: &'db dyn Database,
545    diagnostics: &mut SemanticDiagnostics<'db>,
546    resolver: &mut Resolver<'db>,
547    module_id: ModuleId<'db>,
548    generic_params: &ast::OptionWrappedGenericParamList<'db>,
549) -> Vec<GenericParam<'db>> {
550    semantic_generic_params_ex(db, diagnostics, resolver, module_id, generic_params, false)
551}
552
553pub fn semantic_generic_params_ex<'db>(
554    db: &'db dyn Database,
555    diagnostics: &mut SemanticDiagnostics<'db>,
556    resolver: &mut Resolver<'db>,
557    module_id: ModuleId<'db>,
558    generic_params: &ast::OptionWrappedGenericParamList<'db>,
559    in_cycle: bool,
560) -> Vec<GenericParam<'db>> {
561    match generic_params {
562        syntax::node::ast::OptionWrappedGenericParamList::Empty(_) => vec![],
563        syntax::node::ast::OptionWrappedGenericParamList::WrappedGenericParamList(syntax) => syntax
564            .generic_params(db)
565            .elements(db)
566            .filter_map(|param_syntax| {
567                let generic_param_id =
568                    GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db);
569                let data = generic_param_data(db, generic_param_id, in_cycle).as_ref().ok()?;
570                let generic_param = data.generic_param.clone();
571                diagnostics.extend(data.diagnostics.clone());
572                resolver.add_generic_param(generic_param_id);
573                resolver.data.used_uses.extend(data.resolver_data.used_uses.iter().copied());
574                generic_param.ok()
575            })
576            .collect(),
577    }
578}
579
580/// Returns true if negative impls are enabled in the module.
581fn are_negative_impls_enabled<'db>(db: &dyn Database, module_id: ModuleId<'db>) -> bool {
582    let owning_crate = module_id.owning_crate(db);
583    let Some(config) = db.crate_config(owning_crate) else { return false };
584    config.settings.experimental_features.negative_impls
585}
586
587/// Returns true if associated_item_constraints is enabled in the module.
588fn is_associated_item_constraints_enabled(db: &dyn Database, module_id: ModuleId<'_>) -> bool {
589    let owning_crate = module_id.owning_crate(db);
590    db.crate_config(owning_crate)
591        .is_some_and(|c| c.settings.experimental_features.associated_item_constraints)
592}
593
594/// Computes the semantic model of a generic parameter give its ast.
595fn semantic_from_generic_param_ast<'db>(
596    db: &'db dyn Database,
597    resolver: &mut Resolver<'db>,
598    diagnostics: &mut SemanticDiagnostics<'db>,
599    module_id: ModuleId<'db>,
600    param_syntax: &ast::GenericParam<'db>,
601    parent_item_id: GenericItemId<'db>,
602) -> Maybe<GenericParam<'db>> {
603    let id = GenericParamLongId(module_id, param_syntax.stable_ptr(db)).intern(db);
604    let mut item_constraints_into_option = |constraint| match constraint {
605        OptionAssociatedItemConstraints::Empty(_) => None,
606        OptionAssociatedItemConstraints::AssociatedItemConstraints(associated_type_args) => {
607            if !is_associated_item_constraints_enabled(db, module_id) {
608                diagnostics.report(
609                    associated_type_args.stable_ptr(db),
610                    SemanticDiagnosticKind::TypeConstraintsSyntaxNotEnabled,
611                );
612            }
613            Some(associated_type_args)
614        }
615    };
616    Ok(match param_syntax {
617        ast::GenericParam::Type(_) => GenericParam::Type(GenericParamType { id }),
618        ast::GenericParam::Const(syntax) => {
619            let ty = resolve_type(db, diagnostics, resolver, &syntax.ty(db));
620            GenericParam::Const(GenericParamConst { id, ty })
621        }
622        ast::GenericParam::ImplNamed(syntax) => {
623            let path_syntax = syntax.trait_path(db);
624            let item_constrains = item_constraints_into_option(syntax.type_constrains(db));
625            GenericParam::Impl(impl_generic_param_semantic(
626                db,
627                resolver,
628                diagnostics,
629                &path_syntax,
630                item_constrains,
631                id,
632            ))
633        }
634        ast::GenericParam::ImplAnonymous(syntax) => {
635            let path_syntax = syntax.trait_path(db);
636            let item_constrains = item_constraints_into_option(syntax.type_constrains(db));
637            GenericParam::Impl(impl_generic_param_semantic(
638                db,
639                resolver,
640                diagnostics,
641                &path_syntax,
642                item_constrains,
643                id,
644            ))
645        }
646        ast::GenericParam::NegativeImpl(syntax) => {
647            if !are_negative_impls_enabled(db, module_id) {
648                diagnostics.report(
649                    param_syntax.stable_ptr(db),
650                    SemanticDiagnosticKind::NegativeImplsNotEnabled,
651                );
652            }
653
654            if !matches!(parent_item_id, GenericItemId::ModuleItem(GenericModuleItemId::Impl(_))) {
655                diagnostics.report(
656                    param_syntax.stable_ptr(db),
657                    SemanticDiagnosticKind::NegativeImplsOnlyOnImpls,
658                );
659            }
660
661            let path_syntax = syntax.trait_path(db);
662
663            let neg_impl =
664                impl_generic_param_semantic(db, resolver, diagnostics, &path_syntax, None, id);
665            for param in db.trait_generic_params(neg_impl.concrete_trait?.trait_id(db))? {
666                if matches!(param, GenericParam::Type(_) | GenericParam::Const(_)) {
667                    continue;
668                }
669                diagnostics.report(
670                    param.stable_ptr(db),
671                    SemanticDiagnosticKind::OnlyTypeOrConstParamsInNegImpl,
672                );
673            }
674
675            GenericParam::NegImpl(neg_impl)
676        }
677    })
678}
679
680/// Computes the semantic model of an impl generic parameter given its trait path.
681fn impl_generic_param_semantic<'db>(
682    db: &'db dyn Database,
683    resolver: &mut Resolver<'db>,
684    diagnostics: &mut SemanticDiagnostics<'db>,
685    path_syntax: &ast::ExprPath<'db>,
686    item_constraints: Option<AssociatedItemConstraints<'db>>,
687    id: GenericParamId<'db>,
688) -> GenericParamImpl<'db> {
689    let concrete_trait = resolver
690        .resolve_concrete_path(diagnostics, path_syntax, NotFoundItemType::Trait)
691        .and_then(|resolved_item| match resolved_item {
692            ResolvedConcreteItem::Trait(id) | ResolvedConcreteItem::SelfTrait(id) => Ok(id),
693            _ => Err(diagnostics
694                .report(path_syntax.stable_ptr(db), SemanticDiagnosticKind::UnknownTrait)),
695        });
696    let type_constraints = concrete_trait
697        .ok()
698        .and_then(|concrete_trait| {
699            item_constraints.map(|type_constraints| (concrete_trait, type_constraints))
700        })
701        .map(|(concrete_trait_id, constraints)| {
702            let mut map = OrderedHashMap::default();
703
704            for constraint in constraints.associated_item_constraints(db).elements(db) {
705                let Ok(trait_type_id_opt) = db.trait_type_by_name(
706                    concrete_trait_id.trait_id(db),
707                    constraint.item(db).text(db),
708                ) else {
709                    continue;
710                };
711                let Some(trait_type_id) = trait_type_id_opt else {
712                    diagnostics.report(
713                        constraint.stable_ptr(db),
714                        SemanticDiagnosticKind::NonTraitTypeConstrained {
715                            identifier: constraint.item(db).text(db),
716                            concrete_trait_id,
717                        },
718                    );
719                    return map;
720                };
721
722                let concrete_trait_type_id =
723                    ConcreteTraitTypeId::new_from_data(db, concrete_trait_id, trait_type_id);
724                match map.entry(trait_type_id) {
725                    Entry::Vacant(entry) => {
726                        entry.insert(resolve_type(
727                            db,
728                            diagnostics,
729                            resolver,
730                            &constraint.value(db),
731                        ));
732                    }
733                    Entry::Occupied(_) => {
734                        diagnostics.report(
735                            path_syntax.stable_ptr(db),
736                            SemanticDiagnosticKind::DuplicateTypeConstraint {
737                                concrete_trait_type_id,
738                            },
739                        );
740                    }
741                }
742            }
743            map
744        })
745        .unwrap_or_default();
746
747    GenericParamImpl { id, concrete_trait, type_constraints }
748}
749
750/// A wrapper around std::fmt::Formatter that counts the number of characters written so far.
751struct CountingWriter<'a, 'b> {
752    inner: &'a mut std::fmt::Formatter<'b>,
753    count: usize,
754}
755
756impl<'a, 'b> CountingWriter<'a, 'b> {
757    pub fn new(inner: &'a mut std::fmt::Formatter<'b>) -> Self {
758        Self { inner, count: 0 }
759    }
760
761    pub fn count(&self) -> usize {
762        self.count
763    }
764}
765
766impl<'a, 'b> std::fmt::Write for CountingWriter<'a, 'b> {
767    fn write_str(&mut self, s: &str) -> std::fmt::Result {
768        self.count += s.len();
769        self.inner.write_str(s)
770    }
771}
772
773/// Returns a displayable structure for concrete id formatting.
774/// The display additionally limits the number of characters written during the process.
775pub fn displayable_concrete<'db, 'a: 'db, Name: Display>(
776    db: &'db dyn Database,
777    name: &'a Name,
778    generic_args: &'a [GenericArgumentId<'db>],
779) -> impl Display + 'a + 'db {
780    DisplayableConcrete { db, name, generic_args }
781}
782
783/// The helper struct for `with_generic_args_formatter`.
784struct DisplayableConcrete<'a, 'db, Name> {
785    db: &'db dyn Database,
786    name: &'a Name,
787    generic_args: &'a [GenericArgumentId<'db>],
788}
789impl<Name: Display> Display for DisplayableConcrete<'_, '_, Name> {
790    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
791        let mut f = CountingWriter::new(f);
792        write!(f, "{}", &self.name)?;
793        let mut generic_args = self.generic_args.iter();
794        if let Some(first) = generic_args.next() {
795            // Soft limit for the number of chars in the formatted type.
796            const CHARS_BOUND: usize = 500;
797            write!(f, "::<")?;
798            write!(f, "{}", &first.format(self.db))?;
799
800            for arg in generic_args {
801                write!(f, ", ")?;
802                if f.count() > CHARS_BOUND {
803                    // If the formatted type is becoming too long, add short version of arguments.
804                    write!(f, "{}", &arg.short_name(self.db))?;
805                } else {
806                    write!(f, "{}", &arg.format(self.db))?;
807                }
808            }
809            write!(f, ">")?;
810        }
811        Ok(())
812    }
813}
814
815/// Trait for generic param-related semantic queries.
816pub trait GenericParamSemantic<'db>: Database {
817    /// Returns the semantic data of a generic param.
818    fn generic_param_semantic(
819        &'db self,
820        generic_param: GenericParamId<'db>,
821    ) -> Maybe<GenericParam<'db>> {
822        generic_param_data(self.as_dyn_database(), generic_param, false)
823            .maybe_as_ref()?
824            .generic_param
825            .clone()
826    }
827    /// Returns the semantic diagnostics of a generic param.
828    fn generic_param_diagnostics(
829        &'db self,
830        generic_param: GenericParamId<'db>,
831    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
832        generic_param_data(self.as_dyn_database(), generic_param, false)
833            .as_ref()
834            .map(|data| data.diagnostics.clone())
835            .unwrap_or_default()
836    }
837    /// Returns the resolver data of a generic param.
838    fn generic_param_resolver_data(
839        &'db self,
840        generic_param: GenericParamId<'db>,
841    ) -> Maybe<Arc<ResolverData<'db>>> {
842        Ok(generic_param_data(self.as_dyn_database(), generic_param, false)
843            .maybe_as_ref()?
844            .resolver_data
845            .clone())
846    }
847    /// Returns the trait a generic param impl should implement.
848    /// Panics if the generic param is not an impl generic param.
849    fn generic_impl_param_trait(
850        &'db self,
851        generic_param_id: GenericParamId<'db>,
852    ) -> Maybe<TraitId<'db>> {
853        generic_impl_param_trait(self.as_dyn_database(), generic_param_id)
854    }
855    /// Returns the shallow generic args of a generic impl param.
856    fn generic_impl_param_shallow_trait_generic_args(
857        &'db self,
858        generic_param: GenericParamId<'db>,
859    ) -> Maybe<&'db [(GenericParamId<'db>, ShallowGenericArg<'db>)]> {
860        Ok(generic_impl_param_shallow_trait_generic_args(self.as_dyn_database(), generic_param)
861            .maybe_as_ref()?)
862    }
863    /// Returns the type constraints introduced by the generic params.
864    fn generic_params_type_constraints(
865        &'db self,
866        generic_params: Vec<GenericParamId<'db>>,
867    ) -> &'db [(TypeId<'db>, TypeId<'db>)] {
868        generic_params_type_constraints(self.as_dyn_database(), (), generic_params)
869    }
870}
871impl<'db, T: Database + ?Sized> GenericParamSemantic<'db> for T {}