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