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 generic_params_syntax.generic_params(syntax_db).elements(syntax_db) {
344        let cur_generic_param_id =
345            GenericParamLongId(module_file_id, param_syntax.stable_ptr()).intern(db);
346        resolver.add_generic_param(cur_generic_param_id);
347
348        if cur_generic_param_id == generic_param_id {
349            opt_generic_param_syntax = Some(param_syntax);
350        }
351    }
352    let generic_param_syntax =
353        opt_generic_param_syntax.expect("Query called on a non existing generic param.");
354    let param_semantic = semantic_from_generic_param_ast(
355        db,
356        &mut resolver,
357        &mut diagnostics,
358        module_file_id,
359        &generic_param_syntax,
360        parent_item_id,
361    );
362    let inference = &mut resolver.inference();
363    inference.finalize(&mut diagnostics, generic_param_syntax.stable_ptr().untyped());
364
365    let param_semantic = inference.rewrite(param_semantic).no_err();
366    let resolver_data = Arc::new(resolver.data);
367    Ok(GenericParamData {
368        generic_param: Ok(param_semantic),
369        diagnostics: diagnostics.build(),
370        resolver_data,
371    })
372}
373
374/// Cycle handling for [crate::db::SemanticGroup::priv_generic_param_data].
375pub fn priv_generic_param_data_cycle(
376    db: &dyn SemanticGroup,
377    _cycle: &salsa::Cycle,
378    generic_param_id: &GenericParamId,
379    _in_cycle: &bool,
380) -> Maybe<GenericParamData> {
381    priv_generic_param_data(db, *generic_param_id, true)
382}
383
384/// Query implementation of [crate::db::SemanticGroup::generic_params_type_constraints].
385pub fn generic_params_type_constraints(
386    db: &dyn SemanticGroup,
387    generic_params: Vec<GenericParamId>,
388) -> Vec<(TypeId, TypeId)> {
389    let mut constraints = vec![];
390    for param in &generic_params {
391        let Ok(GenericParam::Impl(imp)) = db.generic_param_semantic(*param) else {
392            continue;
393        };
394        let Ok(concrete_trait_id) = imp.concrete_trait else {
395            continue;
396        };
397        for (trait_ty, ty1) in imp.type_constraints {
398            let impl_type = TypeLongId::ImplType(ImplTypeId::new(
399                ImplLongId::GenericParameter(*param).intern(db),
400                trait_ty,
401                db,
402            ))
403            .intern(db);
404            constraints.push((impl_type, ty1));
405        }
406        let ConcreteTraitLongId { trait_id, generic_args } = concrete_trait_id.lookup_intern(db);
407        if trait_id != db.core_info().type_eq_trt {
408            continue;
409        }
410        let [GenericArgumentId::Type(ty0), GenericArgumentId::Type(ty1)] = generic_args.as_slice()
411        else {
412            unreachable!("TypeEqual should have 2 arguments");
413        };
414        constraints.push((*ty0, *ty1));
415    }
416    constraints
417}
418
419// --- Helpers ---
420
421/// Returns the generic parameters list AST node of a generic parameter.
422fn generic_param_generic_params_list(
423    db: &dyn SemanticGroup,
424    generic_param_id: GenericParamId,
425) -> Maybe<ast::OptionWrappedGenericParamList> {
426    let generic_param_long_id = generic_param_id.lookup_intern(db);
427
428    // The generic params list is 2 level up the tree.
429    let syntax_db = db.upcast();
430    let wrapped_generic_param_list = generic_param_long_id.1.0.nth_parent(syntax_db, 2);
431
432    Ok(ast::OptionWrappedGenericParamListPtr(wrapped_generic_param_list).lookup(syntax_db))
433}
434
435/// Returns the semantic model of a generic parameters list given the list AST, and updates the
436/// diagnostics and resolver accordingly.
437pub fn semantic_generic_params(
438    db: &dyn SemanticGroup,
439    diagnostics: &mut SemanticDiagnostics,
440    resolver: &mut Resolver<'_>,
441    module_file_id: ModuleFileId,
442    generic_params: &ast::OptionWrappedGenericParamList,
443) -> Vec<GenericParam> {
444    semantic_generic_params_ex(db, diagnostics, resolver, module_file_id, generic_params, false)
445}
446
447pub fn semantic_generic_params_ex(
448    db: &dyn SemanticGroup,
449    diagnostics: &mut SemanticDiagnostics,
450    resolver: &mut Resolver<'_>,
451    module_file_id: ModuleFileId,
452    generic_params: &ast::OptionWrappedGenericParamList,
453    in_cycle: bool,
454) -> Vec<GenericParam> {
455    let syntax_db = db.upcast();
456    match generic_params {
457        syntax::node::ast::OptionWrappedGenericParamList::Empty(_) => vec![],
458        syntax::node::ast::OptionWrappedGenericParamList::WrappedGenericParamList(syntax) => syntax
459            .generic_params(syntax_db)
460            .elements(syntax_db)
461            .iter()
462            .filter_map(|param_syntax| {
463                let generic_param_id =
464                    GenericParamLongId(module_file_id, param_syntax.stable_ptr()).intern(db);
465                let generic_param_data =
466                    db.priv_generic_param_data(generic_param_id, in_cycle).ok()?;
467                let generic_param = generic_param_data.generic_param;
468                diagnostics.extend(generic_param_data.diagnostics);
469                resolver.add_generic_param(generic_param_id);
470                resolver
471                    .data
472                    .used_items
473                    .extend(generic_param_data.resolver_data.used_items.iter().copied());
474                generic_param.ok()
475            })
476            .collect(),
477    }
478}
479
480/// Returns true if negative impls are enabled in the module.
481fn are_negative_impls_enabled(db: &dyn SemanticGroup, module_file_id: ModuleFileId) -> bool {
482    let owning_crate = module_file_id.0.owning_crate(db.upcast());
483    let Some(config) = db.crate_config(owning_crate) else { return false };
484    config.settings.experimental_features.negative_impls
485}
486
487/// Returns true if associated_item_constraints is enabled in the module.
488fn is_associated_item_constraints_enabled(
489    db: &dyn SemanticGroup,
490    module_file_id: ModuleFileId,
491) -> bool {
492    let owning_crate = module_file_id.0.owning_crate(db.upcast());
493    db.crate_config(owning_crate)
494        .is_some_and(|c| c.settings.experimental_features.associated_item_constraints)
495}
496
497/// Computes the semantic model of a generic parameter give its ast.
498fn semantic_from_generic_param_ast(
499    db: &dyn SemanticGroup,
500    resolver: &mut Resolver<'_>,
501    diagnostics: &mut SemanticDiagnostics,
502    module_file_id: ModuleFileId,
503    param_syntax: &ast::GenericParam,
504    parent_item_id: GenericItemId,
505) -> GenericParam {
506    let id = GenericParamLongId(module_file_id, param_syntax.stable_ptr()).intern(db);
507    let mut item_constraints_into_option = |constraint| match constraint {
508        OptionAssociatedItemConstraints::Empty(_) => None,
509        OptionAssociatedItemConstraints::AssociatedItemConstraints(associated_type_args) => {
510            if !is_associated_item_constraints_enabled(db, module_file_id) {
511                diagnostics.report(
512                    associated_type_args.stable_ptr(),
513                    SemanticDiagnosticKind::TypeConstraintsSyntaxNotEnabled,
514                );
515            }
516            Some(associated_type_args)
517        }
518    };
519    match param_syntax {
520        ast::GenericParam::Type(_) => GenericParam::Type(GenericParamType { id }),
521        ast::GenericParam::Const(syntax) => {
522            let ty = resolve_type(db, diagnostics, resolver, &syntax.ty(db.upcast()));
523            GenericParam::Const(GenericParamConst { id, ty })
524        }
525        ast::GenericParam::ImplNamed(syntax) => {
526            let path_syntax = syntax.trait_path(db.upcast());
527            let item_constrains = item_constraints_into_option(syntax.type_constrains(db.upcast()));
528            GenericParam::Impl(impl_generic_param_semantic(
529                db,
530                resolver,
531                diagnostics,
532                &path_syntax,
533                item_constrains,
534                id,
535            ))
536        }
537        ast::GenericParam::ImplAnonymous(syntax) => {
538            let path_syntax = syntax.trait_path(db.upcast());
539            let item_constrains = item_constraints_into_option(syntax.type_constrains(db.upcast()));
540            GenericParam::Impl(impl_generic_param_semantic(
541                db,
542                resolver,
543                diagnostics,
544                &path_syntax,
545                item_constrains,
546                id,
547            ))
548        }
549        ast::GenericParam::NegativeImpl(syntax) => {
550            if !are_negative_impls_enabled(db, module_file_id) {
551                diagnostics.report(param_syntax, SemanticDiagnosticKind::NegativeImplsNotEnabled);
552            }
553
554            if !matches!(parent_item_id, GenericItemId::ModuleItem(GenericModuleItemId::Impl(_))) {
555                diagnostics.report(param_syntax, SemanticDiagnosticKind::NegativeImplsOnlyOnImpls);
556            }
557
558            let path_syntax = syntax.trait_path(db.upcast());
559            GenericParam::NegImpl(impl_generic_param_semantic(
560                db,
561                resolver,
562                diagnostics,
563                &path_syntax,
564                None,
565                id,
566            ))
567        }
568    }
569}
570
571/// Computes the semantic model of an impl generic parameter given its trait path.
572fn impl_generic_param_semantic(
573    db: &dyn SemanticGroup,
574    resolver: &mut Resolver<'_>,
575    diagnostics: &mut SemanticDiagnostics,
576    path_syntax: &ast::ExprPath,
577    item_constraints: Option<AssociatedItemConstraints>,
578    id: GenericParamId,
579) -> GenericParamImpl {
580    let concrete_trait = resolver
581        .resolve_concrete_path(diagnostics, path_syntax, NotFoundItemType::Trait)
582        .and_then(|resolved_item| match resolved_item {
583            ResolvedConcreteItem::Trait(id) | ResolvedConcreteItem::SelfTrait(id) => Ok(id),
584            _ => Err(diagnostics.report(path_syntax, SemanticDiagnosticKind::UnknownTrait)),
585        });
586    let type_constraints = concrete_trait
587        .ok()
588        .and_then(|concrete_trait| {
589            item_constraints.map(|type_constraints| (concrete_trait, type_constraints))
590        })
591        .map(|(concrete_trait_id, constraints)| {
592            let mut map = OrderedHashMap::default();
593
594            for constraint in
595                constraints.associated_item_constraints(db.upcast()).elements(db.upcast())
596            {
597                let Ok(trait_type_id_opt) = db.trait_type_by_name(
598                    concrete_trait_id.trait_id(db),
599                    constraint.item(db.upcast()).text(db.upcast()),
600                ) else {
601                    continue;
602                };
603                let Some(trait_type_id) = trait_type_id_opt else {
604                    diagnostics.report(
605                        constraint.stable_ptr(),
606                        SemanticDiagnosticKind::NonTraitTypeConstrained {
607                            identifier: constraint.item(db.upcast()).text(db.upcast()),
608                            concrete_trait_id,
609                        },
610                    );
611                    return map;
612                };
613
614                let concrete_trait_type_id =
615                    ConcreteTraitTypeId::new(db, concrete_trait_id, trait_type_id);
616                match map.entry(trait_type_id) {
617                    Entry::Vacant(entry) => {
618                        entry.insert(resolve_type(
619                            db,
620                            diagnostics,
621                            resolver,
622                            &constraint.value(db.upcast()),
623                        ));
624                    }
625                    Entry::Occupied(_) => {
626                        diagnostics.report(
627                            path_syntax,
628                            SemanticDiagnosticKind::DuplicateTypeConstraint {
629                                concrete_trait_type_id,
630                            },
631                        );
632                    }
633                }
634            }
635            map
636        })
637        .unwrap_or_default();
638
639    GenericParamImpl { id, concrete_trait, type_constraints }
640}
641
642/// Formats a list of generic arguments.
643pub fn fmt_generic_args(
644    generic_args: &[GenericArgumentId],
645    f: &mut std::fmt::Formatter<'_>,
646    db: &(dyn SemanticGroup + 'static),
647) -> std::fmt::Result {
648    if !generic_args.is_empty() {
649        write!(f, "::<")?;
650        for (i, arg) in generic_args.iter().enumerate() {
651            if i > 0 {
652                write!(f, ", ")?;
653            }
654            write!(f, "{}", arg.format(db))?;
655        }
656        write!(f, ">")?;
657    }
658    Ok(())
659}