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