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#[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 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 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 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 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#[derive(Clone, Debug, Hash, PartialEq, Eq)]
116pub enum GenericArgumentHead {
117 Type(TypeHead),
118 Impl(ImplHead),
119 Const,
120 NegImpl,
121}
122
123#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
125pub enum GenericParam {
126 Type(GenericParamType),
127 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 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
178pub 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#[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#[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
233pub 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
243pub 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
253pub 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
261pub 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 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
301pub 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
376pub 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
386pub 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
421fn 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 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
437pub 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
482fn 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
489fn 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
499fn 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
573fn 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
644pub 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}