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