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,
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, try_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::{TypeHead, resolve_type};
34use crate::{ConcreteTraitId, 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<ConcreteTraitTypeId, 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
386fn generic_param_generic_params_list(
390 db: &dyn SemanticGroup,
391 generic_param_id: GenericParamId,
392) -> Maybe<ast::OptionWrappedGenericParamList> {
393 let generic_param_long_id = generic_param_id.lookup_intern(db);
394
395 let syntax_db = db.upcast();
397 let wrapped_generic_param_list = generic_param_long_id.1.0.nth_parent(syntax_db, 2);
398
399 Ok(ast::OptionWrappedGenericParamListPtr(wrapped_generic_param_list).lookup(syntax_db))
400}
401
402pub fn semantic_generic_params(
405 db: &dyn SemanticGroup,
406 diagnostics: &mut SemanticDiagnostics,
407 resolver: &mut Resolver<'_>,
408 module_file_id: ModuleFileId,
409 generic_params: &ast::OptionWrappedGenericParamList,
410) -> Vec<GenericParam> {
411 semantic_generic_params_ex(db, diagnostics, resolver, module_file_id, generic_params, false)
412}
413
414pub fn semantic_generic_params_ex(
415 db: &dyn SemanticGroup,
416 diagnostics: &mut SemanticDiagnostics,
417 resolver: &mut Resolver<'_>,
418 module_file_id: ModuleFileId,
419 generic_params: &ast::OptionWrappedGenericParamList,
420 in_cycle: bool,
421) -> Vec<GenericParam> {
422 let syntax_db = db.upcast();
423 match generic_params {
424 syntax::node::ast::OptionWrappedGenericParamList::Empty(_) => vec![],
425 syntax::node::ast::OptionWrappedGenericParamList::WrappedGenericParamList(syntax) => syntax
426 .generic_params(syntax_db)
427 .elements(syntax_db)
428 .iter()
429 .filter_map(|param_syntax| {
430 let generic_param_id =
431 GenericParamLongId(module_file_id, param_syntax.stable_ptr()).intern(db);
432 let generic_param_data =
433 db.priv_generic_param_data(generic_param_id, in_cycle).ok()?;
434 let generic_param = generic_param_data.generic_param;
435 diagnostics.extend(generic_param_data.diagnostics);
436 resolver.add_generic_param(generic_param_id);
437 resolver
438 .data
439 .used_items
440 .extend(generic_param_data.resolver_data.used_items.iter().copied());
441 generic_param.ok()
442 })
443 .collect(),
444 }
445}
446
447fn are_negative_impls_enabled(db: &dyn SemanticGroup, module_file_id: ModuleFileId) -> bool {
449 let owning_crate = module_file_id.0.owning_crate(db.upcast());
450 let Some(config) = db.crate_config(owning_crate) else { return false };
451 config.settings.experimental_features.negative_impls
452}
453
454fn is_associated_item_constraints_enabled(
456 db: &dyn SemanticGroup,
457 module_file_id: ModuleFileId,
458) -> bool {
459 let owning_crate = module_file_id.0.owning_crate(db.upcast());
460 db.crate_config(owning_crate)
461 .is_some_and(|c| c.settings.experimental_features.associated_item_constraints)
462}
463
464fn semantic_from_generic_param_ast(
466 db: &dyn SemanticGroup,
467 resolver: &mut Resolver<'_>,
468 diagnostics: &mut SemanticDiagnostics,
469 module_file_id: ModuleFileId,
470 param_syntax: &ast::GenericParam,
471 parent_item_id: GenericItemId,
472) -> GenericParam {
473 let id = GenericParamLongId(module_file_id, param_syntax.stable_ptr()).intern(db);
474 let mut item_constraints_into_option = |constraint| match constraint {
475 OptionAssociatedItemConstraints::Empty(_) => None,
476 OptionAssociatedItemConstraints::AssociatedItemConstraints(associated_type_args) => {
477 if !is_associated_item_constraints_enabled(db, module_file_id) {
478 diagnostics.report(
479 associated_type_args.stable_ptr(),
480 SemanticDiagnosticKind::TypeConstraintsSyntaxNotEnabled,
481 );
482 }
483 Some(associated_type_args)
484 }
485 };
486 match param_syntax {
487 ast::GenericParam::Type(_) => GenericParam::Type(GenericParamType { id }),
488 ast::GenericParam::Const(syntax) => {
489 let ty = resolve_type(db, diagnostics, resolver, &syntax.ty(db.upcast()));
490 GenericParam::Const(GenericParamConst { id, ty })
491 }
492 ast::GenericParam::ImplNamed(syntax) => {
493 let path_syntax = syntax.trait_path(db.upcast());
494 let item_constrains = item_constraints_into_option(syntax.type_constrains(db.upcast()));
495 GenericParam::Impl(impl_generic_param_semantic(
496 db,
497 resolver,
498 diagnostics,
499 &path_syntax,
500 item_constrains,
501 id,
502 ))
503 }
504 ast::GenericParam::ImplAnonymous(syntax) => {
505 let path_syntax = syntax.trait_path(db.upcast());
506 let item_constrains = item_constraints_into_option(syntax.type_constrains(db.upcast()));
507 GenericParam::Impl(impl_generic_param_semantic(
508 db,
509 resolver,
510 diagnostics,
511 &path_syntax,
512 item_constrains,
513 id,
514 ))
515 }
516 ast::GenericParam::NegativeImpl(syntax) => {
517 if !are_negative_impls_enabled(db, module_file_id) {
518 diagnostics.report(param_syntax, SemanticDiagnosticKind::NegativeImplsNotEnabled);
519 }
520
521 if !matches!(parent_item_id, GenericItemId::ModuleItem(GenericModuleItemId::Impl(_))) {
522 diagnostics.report(param_syntax, SemanticDiagnosticKind::NegativeImplsOnlyOnImpls);
523 }
524
525 let path_syntax = syntax.trait_path(db.upcast());
526 GenericParam::NegImpl(impl_generic_param_semantic(
527 db,
528 resolver,
529 diagnostics,
530 &path_syntax,
531 None,
532 id,
533 ))
534 }
535 }
536}
537
538fn impl_generic_param_semantic(
540 db: &dyn SemanticGroup,
541 resolver: &mut Resolver<'_>,
542 diagnostics: &mut SemanticDiagnostics,
543 path_syntax: &ast::ExprPath,
544 item_constraints: Option<AssociatedItemConstraints>,
545 id: GenericParamId,
546) -> GenericParamImpl {
547 let concrete_trait = resolver
548 .resolve_concrete_path(diagnostics, path_syntax, NotFoundItemType::Trait)
549 .and_then(|resolved_item| {
550 try_extract_matches!(resolved_item, ResolvedConcreteItem::Trait).ok_or_else(|| {
551 diagnostics.report(path_syntax, SemanticDiagnosticKind::UnknownTrait)
552 })
553 });
554 let type_constraints = concrete_trait
555 .ok()
556 .and_then(|concrete_trait| {
557 item_constraints.map(|type_constraints| (concrete_trait, type_constraints))
558 })
559 .map(|(concrete_trait_id, constraints)| {
560 let mut map = OrderedHashMap::default();
561
562 for constraint in
563 constraints.associated_item_constraints(db.upcast()).elements(db.upcast())
564 {
565 let Some(trait_type_id) = db
566 .trait_type_by_name(
567 concrete_trait_id.trait_id(db),
568 constraint.item(db.upcast()).text(db.upcast()),
569 )
570 .unwrap()
571 else {
572 diagnostics.report(
573 constraint.stable_ptr(),
574 SemanticDiagnosticKind::NonTraitTypeConstrained {
575 identifier: constraint.item(db.upcast()).text(db.upcast()),
576 concrete_trait_id,
577 },
578 );
579 return map;
580 };
581
582 let concrete_trait_type_id =
583 ConcreteTraitTypeId::new(db, concrete_trait_id, trait_type_id);
584 match map.entry(concrete_trait_type_id) {
585 Entry::Vacant(entry) => {
586 entry.insert(resolve_type(
587 db,
588 diagnostics,
589 resolver,
590 &constraint.value(db.upcast()),
591 ));
592 }
593 Entry::Occupied(_) => {
594 diagnostics.report(
595 path_syntax,
596 SemanticDiagnosticKind::DuplicateTypeConstraint {
597 concrete_trait_type_id,
598 },
599 );
600 }
601 }
602 }
603 map
604 })
605 .unwrap_or_default();
606
607 GenericParamImpl { id, concrete_trait, type_constraints }
608}