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 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
374pub 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
384pub 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
419fn 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 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
435pub 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
480fn 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
487fn 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
497fn 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
571fn 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
642pub 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}