cairo_lang_semantic/items/
generics.rs

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