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