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