1use std::sync::Arc;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_defs::ids::{
5 NamedLanguageElementId, TopLevelLanguageElementId, TraitFunctionId, UnstableSalsaId,
6};
7use cairo_lang_diagnostics::{DiagnosticAdded, DiagnosticNote, Maybe};
8use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
9use cairo_lang_semantic::corelib::CorelibSemantic;
10use cairo_lang_semantic::items::functions::{FunctionsSemantic, ImplGenericFunctionId};
11use cairo_lang_semantic::items::imp::ImplLongId;
12use cairo_lang_semantic::{GenericArgumentId, TypeLongId};
13use cairo_lang_syntax::node::ast::ExprPtr;
14use cairo_lang_syntax::node::kind::SyntaxKind;
15use cairo_lang_syntax::node::{TypedStablePtr, ast};
16use cairo_lang_utils::{Intern, define_short_id, try_extract_matches};
17use defs::diagnostic_utils::StableLocation;
18use defs::ids::{ExternFunctionId, FreeFunctionId};
19use itertools::zip_eq;
20use salsa::Database;
21use semantic::items::functions::GenericFunctionId;
22use semantic::substitution::{GenericSubstitution, SubstitutionRewriter};
23use semantic::{ExprVar, Mutability};
24use {cairo_lang_defs as defs, cairo_lang_semantic as semantic};
25
26use crate::Location;
27use crate::db::LoweringGroup;
28use crate::ids::semantic::substitution::SemanticRewriter;
29use crate::specialization::SpecializationArg;
30
31#[derive(Clone, Debug, Hash, PartialEq, Eq)]
32pub enum FunctionWithBodyLongId<'db> {
33 Semantic(defs::ids::FunctionWithBodyId<'db>),
34 Generated { parent: defs::ids::FunctionWithBodyId<'db>, key: GeneratedFunctionKey<'db> },
35}
36define_short_id!(FunctionWithBodyId, FunctionWithBodyLongId<'db>);
37impl<'db> FunctionWithBodyLongId<'db> {
38 pub fn base_semantic_function(
39 &self,
40 _db: &'db dyn Database,
41 ) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
42 match self {
43 FunctionWithBodyLongId::Semantic(id) => *id,
44 FunctionWithBodyLongId::Generated { parent, .. } => *parent,
45 }
46 }
47 pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyLongId<'db>> {
48 Ok(match self {
49 FunctionWithBodyLongId::Semantic(semantic) => ConcreteFunctionWithBodyLongId::Semantic(
50 semantic::ConcreteFunctionWithBodyId::from_generic(db, *semantic)?,
51 ),
52 FunctionWithBodyLongId::Generated { parent, key } => {
53 ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
54 parent: semantic::ConcreteFunctionWithBodyId::from_generic(db, *parent)?,
55 key: *key,
56 })
57 }
58 })
59 }
60}
61impl<'db> FunctionWithBodyId<'db> {
62 pub fn base_semantic_function(
63 &self,
64 db: &'db dyn Database,
65 ) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
66 self.long(db).base_semantic_function(db)
67 }
68 pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
69 Ok(db.function_with_body_lowering(*self)?.signature.clone())
70 }
71 pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyId<'db>> {
72 Ok(self.long(db).to_concrete(db)?.intern(db))
73 }
74}
75pub trait SemanticFunctionWithBodyIdEx<'db> {
76 fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db>;
77}
78impl<'db> SemanticFunctionWithBodyIdEx<'db> for cairo_lang_defs::ids::FunctionWithBodyId<'db> {
79 fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db> {
80 FunctionWithBodyLongId::Semantic(*self).intern(db)
81 }
82}
83
84#[derive(Clone, Debug, Hash, PartialEq, Eq)]
86pub enum ConcreteFunctionWithBodyLongId<'db> {
87 Semantic(semantic::ConcreteFunctionWithBodyId<'db>),
88 Generated(GeneratedFunction<'db>),
89 Specialized(SpecializedFunction<'db>),
90}
91define_short_id!(ConcreteFunctionWithBodyId, ConcreteFunctionWithBodyLongId<'db>);
92
93pub enum GenericOrSpecialized<'db> {
95 Generic(FunctionWithBodyId<'db>),
96 Specialized(SpecializedFunction<'db>),
97}
98
99impl<'db> ConcreteFunctionWithBodyId<'db> {
100 pub fn is_panic_destruct_fn(&self, db: &'db dyn Database) -> Maybe<bool> {
101 match self.long(db) {
102 ConcreteFunctionWithBodyLongId::Semantic(semantic_func) => {
103 semantic_func.is_panic_destruct_fn(db)
104 }
105 ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
106 parent: _,
107 key: GeneratedFunctionKey::TraitFunc(function, _),
108 }) => Ok(function == &db.core_info().panic_destruct_fn),
109 _ => Ok(false),
110 }
111 }
112
113 pub fn generic_or_specialized(&self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
116 self.long(db).clone().generic_or_specialized(db)
117 }
118}
119
120impl<'db> UnstableSalsaId for ConcreteFunctionWithBodyId<'db> {
121 fn get_internal_id(&self) -> salsa::Id {
122 self.as_intern_id()
123 }
124}
125impl<'db> ConcreteFunctionWithBodyLongId<'db> {
126 pub fn generic_or_specialized(self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
129 let long_id = match self {
130 ConcreteFunctionWithBodyLongId::Semantic(id) => {
131 FunctionWithBodyLongId::Semantic(id.function_with_body_id(db))
132 }
133 ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, key }) => {
134 FunctionWithBodyLongId::Generated { parent: parent.function_with_body_id(db), key }
135 }
136 ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
137 return GenericOrSpecialized::Specialized(specialized);
138 }
139 };
140 GenericOrSpecialized::Generic(long_id.intern(db))
141 }
142 pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
143 match self {
144 ConcreteFunctionWithBodyLongId::Semantic(id) => id.substitution(db),
145 ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, .. }) => {
146 parent.substitution(db)
147 }
148 ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
149 specialized.base.substitution(db)
150 }
151 }
152 }
153 pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
154 let long_id = match self {
155 ConcreteFunctionWithBodyLongId::Semantic(id) => {
156 FunctionLongId::Semantic(id.function_id(db)?)
157 }
158 ConcreteFunctionWithBodyLongId::Generated(generated) => {
159 FunctionLongId::Generated(*generated)
160 }
161 ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
162 FunctionLongId::Specialized(specialized.clone())
163 }
164 };
165 Ok(long_id.intern(db))
166 }
167 pub fn base_semantic_function(
168 &self,
169 db: &'db dyn Database,
170 ) -> semantic::ConcreteFunctionWithBodyId<'db> {
171 match self {
172 ConcreteFunctionWithBodyLongId::Semantic(id) => *id,
173 ConcreteFunctionWithBodyLongId::Generated(generated) => generated.parent,
174 ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
175 specialized.base.base_semantic_function(db)
176 }
177 }
178 }
179 pub fn full_path(&self, db: &dyn Database) -> String {
180 match self {
181 ConcreteFunctionWithBodyLongId::Semantic(semantic) => semantic.full_path(db),
182 ConcreteFunctionWithBodyLongId::Generated(generated) => generated.full_path(db),
183 ConcreteFunctionWithBodyLongId::Specialized(specialized) => specialized.full_path(db),
184 }
185 }
186}
187impl<'db> ConcreteFunctionWithBodyId<'db> {
188 pub fn from_semantic(
189 db: &'db dyn Database,
190 semantic: semantic::ConcreteFunctionWithBodyId<'db>,
191 ) -> Self {
192 ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db)
193 }
194 pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
195 self.long(db).substitution(db)
196 }
197 pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
198 self.long(db).function_id(db)
199 }
200 pub fn full_path(&self, db: &dyn Database) -> String {
201 self.long(db).full_path(db)
202 }
203 pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
204 match self.generic_or_specialized(db) {
205 GenericOrSpecialized::Generic(id) => {
206 let generic_signature = id.signature(db)?;
207 self.substitution(db)?.substitute(db, generic_signature)
208 }
209 GenericOrSpecialized::Specialized(specialized) => specialized.signature(db),
210 }
211 }
212 pub fn from_no_generics_free(
213 db: &'db dyn Database,
214 free_function_id: FreeFunctionId<'db>,
215 ) -> Option<Self> {
216 let semantic =
217 semantic::ConcreteFunctionWithBodyId::from_no_generics_free(db, free_function_id)?;
218 Some(ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db))
219 }
220 pub fn base_semantic_function(
221 &self,
222 db: &'db dyn Database,
223 ) -> semantic::ConcreteFunctionWithBodyId<'db> {
224 self.long(db).base_semantic_function(db)
225 }
226 pub fn stable_location(&self, db: &'db dyn Database) -> Maybe<StableLocation<'db>> {
227 Ok(match self.long(db) {
228 ConcreteFunctionWithBodyLongId::Semantic(id) => id.stable_location(db),
229 ConcreteFunctionWithBodyLongId::Generated(generated) => match generated.key {
230 GeneratedFunctionKey::Loop(stable_ptr) => StableLocation::new(stable_ptr.untyped()),
231 GeneratedFunctionKey::TraitFunc(_, stable_location) => stable_location,
232 },
233 ConcreteFunctionWithBodyLongId::Specialized(specialized_function) => {
234 specialized_function.base.stable_location(db)?
235 }
236 })
237 }
238}
239
240#[derive(Clone, Debug, Hash, PartialEq, Eq)]
242pub enum FunctionLongId<'db> {
243 Semantic(semantic::FunctionId<'db>),
245 Generated(GeneratedFunction<'db>),
247 Specialized(SpecializedFunction<'db>),
249}
250define_short_id!(FunctionId, FunctionLongId<'db>);
251impl<'db> FunctionLongId<'db> {
252 pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
253 Ok(Some(match self {
254 FunctionLongId::Semantic(id) => {
255 let concrete_function = id.get_concrete(db);
256 if let GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function }) =
257 concrete_function.generic_function
258 && let ImplLongId::GeneratedImpl(imp) = impl_id.long(db)
259 {
260 let concrete_trait = imp.concrete_trait(db);
261 let info = db.core_info();
262 assert!(
263 [info.destruct_fn, info.panic_destruct_fn, info.call_fn, info.call_once_fn]
264 .contains(&function)
265 );
266
267 let generic_args = concrete_trait.generic_args(db);
268 let Some(GenericArgumentId::Type(ty)) = generic_args.first() else {
269 unreachable!("Expected Generated Impl to have a type argument");
270 };
271 let TypeLongId::Closure(ty) = ty.long(db) else {
272 unreachable!("Expected Generated Impl to have a closure type argument");
273 };
274
275 let Some(parent) = ty.parent_function?.get_concrete(db).body(db)? else {
276 return Ok(None);
277 };
278 return Ok(Some(
279 GeneratedFunction {
280 parent,
281 key: GeneratedFunctionKey::TraitFunc(function, ty.wrapper_location),
282 }
283 .body(db),
284 ));
285 }
286
287 let Some(body) = concrete_function.body(db)? else {
288 return Ok(None);
289 };
290 ConcreteFunctionWithBodyLongId::Semantic(body).intern(db)
291 }
292 FunctionLongId::Generated(generated) => generated.body(db),
293 FunctionLongId::Specialized(specialized) => {
294 ConcreteFunctionWithBodyLongId::Specialized(specialized.clone()).intern(db)
295 }
296 }))
297 }
298 pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
299 match self {
300 FunctionLongId::Semantic(semantic) => {
301 Ok(Signature::from_semantic(db, db.concrete_function_signature(*semantic)?))
302 }
303 FunctionLongId::Generated(generated) => generated.body(db).signature(db),
304 FunctionLongId::Specialized(specialized) => specialized.signature(db),
305 }
306 }
307 pub fn full_path(&self, db: &dyn Database) -> String {
308 format!("{:?}", self.debug(db))
309 }
310 pub fn semantic_full_path(&self, db: &dyn Database) -> String {
314 match self {
315 FunctionLongId::Semantic(id) => id.full_path(db),
316 FunctionLongId::Generated(generated) => generated.parent.full_path(db),
317 FunctionLongId::Specialized(specialized) => specialized.full_path(db),
318 }
319 }
320}
321impl<'db> FunctionId<'db> {
322 pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
323 self.long(db).body(db)
324 }
325 pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
326 self.long(db).signature(db)
327 }
328 pub fn full_path(&self, db: &dyn Database) -> String {
329 self.long(db).full_path(db)
330 }
331 pub fn semantic_full_path(&self, db: &dyn Database) -> String {
332 self.long(db).semantic_full_path(db)
333 }
334 pub fn get_extern(
337 &self,
338 db: &'db dyn Database,
339 ) -> Option<(ExternFunctionId<'db>, Vec<GenericArgumentId<'db>>)> {
340 let semantic = try_extract_matches!(self.long(db), FunctionLongId::Semantic)?;
341 let concrete = semantic.get_concrete(db);
342 Some((
343 try_extract_matches!(concrete.generic_function, GenericFunctionId::Extern)?,
344 concrete.generic_args,
345 ))
346 }
347}
348pub trait SemanticFunctionIdEx<'db> {
349 fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db>;
350}
351impl<'db> SemanticFunctionIdEx<'db> for semantic::FunctionId<'db> {
352 fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db> {
353 let ret = FunctionLongId::Semantic(*self).intern(db);
354 if let Ok(Some(body)) = ret.body(db)
358 && let Ok(id) = body.function_id(db)
359 {
360 return id;
361 }
362 ret
363 }
364}
365impl<'a> DebugWithDb<'a> for FunctionLongId<'a> {
366 type Db = dyn Database;
367 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
368 match self {
369 FunctionLongId::Semantic(semantic) => write!(f, "{:?}", semantic.debug(db)),
370 FunctionLongId::Generated(generated) => write!(f, "{:?}", generated.debug(db)),
371 FunctionLongId::Specialized(specialized) => write!(f, "{:?}", specialized.debug(db)),
372 }
373 }
374}
375
376#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
378pub enum GeneratedFunctionKey<'db> {
379 Loop(ExprPtr<'db>),
381 TraitFunc(TraitFunctionId<'db>, StableLocation<'db>),
382}
383
384#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
386pub struct GeneratedFunction<'db> {
387 pub parent: semantic::ConcreteFunctionWithBodyId<'db>,
388 pub key: GeneratedFunctionKey<'db>,
389}
390impl<'db> GeneratedFunction<'db> {
391 pub fn body(&self, db: &'db dyn Database) -> ConcreteFunctionWithBodyId<'db> {
392 let long_id = ConcreteFunctionWithBodyLongId::Generated(*self);
393 long_id.intern(db)
394 }
395 pub fn full_path(&self, db: &dyn Database) -> String {
396 format!("{:?}", self.debug(db))
397 }
398}
399impl<'a> DebugWithDb<'a> for GeneratedFunction<'a> {
400 type Db = dyn Database;
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
402 match self.key {
403 GeneratedFunctionKey::Loop(expr_ptr) => {
404 let mut func_ptr = expr_ptr.untyped();
405 while !matches!(
406 func_ptr.kind(db),
407 SyntaxKind::FunctionWithBody | SyntaxKind::TraitItemFunction
408 ) {
409 func_ptr = func_ptr.parent(db)
410 }
411
412 let span = expr_ptr.0.lookup(db).span(db);
413 let function_start = func_ptr.lookup(db).span(db).start.as_u32();
414 write!(
415 f,
416 "{:?}[{}-{}]",
417 self.parent.debug(db),
418 span.start.as_u32() - function_start,
419 span.end.as_u32() - function_start
420 )
421 }
422 GeneratedFunctionKey::TraitFunc(trait_func, loc) => {
423 let trait_id = trait_func.trait_id(db);
424 write!(
425 f,
426 "Generated `{}::{}` for {{closure@{:?}}}",
427 trait_id.full_path(db),
428 trait_func.name(db).long(db),
429 loc.debug(db),
430 )
431 }
432 }
433 }
434}
435
436#[derive(Clone, Debug, Hash, PartialEq, Eq)]
444pub struct SpecializedFunction<'db> {
445 pub base: crate::ids::ConcreteFunctionWithBodyId<'db>,
447 pub args: Arc<[Option<SpecializationArg<'db>>]>,
449}
450
451impl<'db> SpecializedFunction<'db> {
452 pub fn body(&self, db: &'db dyn Database) -> ConcreteFunctionWithBodyId<'db> {
453 let long_id = ConcreteFunctionWithBodyLongId::Specialized(self.clone());
454 long_id.intern(db)
455 }
456 pub fn full_path(&self, db: &dyn Database) -> String {
457 format!("{:?}", self.debug(db))
458 }
459
460 pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
461 let mut base_sign = self.base.signature(db)?;
462
463 base_sign.params = zip_eq(base_sign.params, self.args.iter())
464 .filter_map(|(param, arg)| arg.is_none().then_some(param))
465 .collect::<Vec<_>>();
466
467 Ok(base_sign)
468 }
469}
470impl<'a> DebugWithDb<'a> for SpecializedFunction<'a> {
471 type Db = dyn Database;
472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
473 write!(f, "{}{{", self.base.full_path(db))?;
474 for arg in self.args.iter() {
475 match arg {
476 Some(value) => write!(f, "{:?}, ", value.debug(db))?,
477 None => write!(f, "None, ")?,
478 }
479 }
480 write!(f, "}}")
481 }
482}
483
484#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
486#[debug_db(dyn Database)]
487pub struct Signature<'db> {
488 pub params: Vec<semantic::ExprVarMemberPath<'db>>,
490 pub extra_rets: Vec<semantic::ExprVarMemberPath<'db>>,
492 pub return_type: semantic::TypeId<'db>,
494 pub implicits: Vec<semantic::TypeId<'db>>,
496 #[dont_rewrite]
498 pub panicable: bool,
499 #[dont_rewrite]
501 #[hide_field_debug_with_db]
502 pub location: LocationId<'db>,
503}
504impl<'db> Signature<'db> {
505 pub fn from_semantic(db: &'db dyn Database, value: &semantic::Signature<'db>) -> Self {
506 let semantic::Signature {
507 params,
508 return_type,
509 implicits,
510 panicable,
511 stable_ptr,
512 is_const: _,
513 } = value;
514 let ref_params = params
515 .iter()
516 .filter(|param| param.mutability == Mutability::Reference)
517 .map(|param| parameter_as_member_path(param.clone()))
518 .collect();
519 let params: Vec<semantic::ExprVarMemberPath<'_>> =
520 params.iter().cloned().map(parameter_as_member_path).collect();
521 Self {
522 params,
523 extra_rets: ref_params,
524 return_type: *return_type,
525 implicits: implicits.clone(),
526 panicable: *panicable,
527 location: LocationId::from_stable_location(
528 db,
529 StableLocation::new(stable_ptr.untyped()),
530 ),
531 }
532 }
533 pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
534 self.params.iter().all(|param| param.ty().is_fully_concrete(db))
535 && self.extra_rets.iter().all(|param| param.ty().is_fully_concrete(db))
536 && self.return_type.is_fully_concrete(db)
537 && self.implicits.iter().all(|ty| ty.is_fully_concrete(db))
538 }
539}
540semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, Signature<'a>);
541
542pub(crate) fn parameter_as_member_path<'db>(
544 param: semantic::Parameter<'db>,
545) -> semantic::ExprVarMemberPath<'db> {
546 let semantic::Parameter { id, ty, stable_ptr, .. } = param;
547 semantic::ExprVarMemberPath::Var(ExprVar {
548 var: semantic::VarId::Param(id),
549 ty,
550 stable_ptr: ast::ExprPtr(stable_ptr.0),
551 })
552}
553
554define_short_id!(LocationId, Location<'db>);
555impl<'db> LocationId<'db> {
556 pub fn from_stable_location(
557 db: &'db dyn Database,
558 stable_location: StableLocation<'db>,
559 ) -> LocationId<'db> {
560 Location::new(stable_location).intern(db)
561 }
562
563 pub fn with_note(&self, db: &'db dyn Database, note: DiagnosticNote<'db>) -> LocationId<'db> {
565 self.long(db).clone().with_note(note).intern(db)
566 }
567
568 pub fn with_auto_generation_note(
570 &self,
571 db: &'db dyn Database,
572 logic_name: &str,
573 ) -> LocationId<'db> {
574 self.with_note(
575 db,
576 DiagnosticNote::text_only(format!(
577 "this error originates in auto-generated {logic_name} logic."
578 )),
579 )
580 }
581}