1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs::ids::UnstableSalsaId;
3use cairo_lang_diagnostics::{DiagnosticAdded, DiagnosticNote, Maybe};
4use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
5use cairo_lang_semantic::corelib::panic_destruct_trait_fn;
6use cairo_lang_semantic::items::functions::ImplGenericFunctionId;
7use cairo_lang_semantic::items::imp::ImplLongId;
8use cairo_lang_semantic::{GenericArgumentId, TypeLongId};
9use cairo_lang_syntax::node::{TypedStablePtr, ast};
10use cairo_lang_utils::{
11 Intern, LookupIntern, define_short_id, extract_matches, try_extract_matches,
12};
13use defs::diagnostic_utils::StableLocation;
14use defs::ids::{ExternFunctionId, FreeFunctionId};
15use semantic::items::functions::GenericFunctionId;
16use semantic::substitution::{GenericSubstitution, SubstitutionRewriter};
17use semantic::{ExprVar, Mutability};
18use smol_str::SmolStr;
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(extract_matches!(
111 function.get_concrete(db.upcast()).generic_function,
112 GenericFunctionId::Impl
113 )
114 .function
115 == panic_destruct_trait_fn(db.upcast())),
116 _ => Ok(false),
117 }
118 }
119}
120
121impl UnstableSalsaId for ConcreteFunctionWithBodyId {
122 fn get_internal_id(&self) -> &salsa::InternId {
123 &self.0
124 }
125}
126impl ConcreteFunctionWithBodyLongId {
127 pub fn function_with_body_id(&self, db: &dyn LoweringGroup) -> FunctionWithBodyId {
128 let semantic_db = db.upcast();
129 let long_id = match *self {
130 ConcreteFunctionWithBodyLongId::Semantic(id) => {
131 FunctionWithBodyLongId::Semantic(id.function_with_body_id(semantic_db))
132 }
133 ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, key }) => {
134 FunctionWithBodyLongId::Generated {
135 parent: parent.function_with_body_id(semantic_db),
136 key,
137 }
138 }
139 };
140 long_id.intern(db)
141 }
142 pub fn substitution(&self, db: &dyn LoweringGroup) -> Maybe<GenericSubstitution> {
143 let semantic_db = db.upcast();
144 match self {
145 ConcreteFunctionWithBodyLongId::Semantic(id) => id.substitution(semantic_db),
146 ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, .. }) => {
147 parent.substitution(semantic_db)
148 }
149 }
150 }
151 pub fn function_id(&self, db: &dyn LoweringGroup) -> Maybe<FunctionId> {
152 let semantic_db = db.upcast();
153 let long_id = match self {
154 ConcreteFunctionWithBodyLongId::Semantic(id) => {
155 FunctionLongId::Semantic(id.function_id(semantic_db)?)
156 }
157 ConcreteFunctionWithBodyLongId::Generated(generated) => {
158 FunctionLongId::Generated(*generated)
159 }
160 };
161 Ok(long_id.intern(db))
162 }
163 pub fn base_semantic_function(
164 &self,
165 _db: &dyn LoweringGroup,
166 ) -> semantic::ConcreteFunctionWithBodyId {
167 match *self {
168 ConcreteFunctionWithBodyLongId::Semantic(id) => id,
169 ConcreteFunctionWithBodyLongId::Generated(generated) => generated.parent,
170 }
171 }
172 pub fn name(&self, db: &dyn LoweringGroup) -> SmolStr {
173 match self {
174 ConcreteFunctionWithBodyLongId::Semantic(semantic) => semantic.name(db.upcast()),
175 ConcreteFunctionWithBodyLongId::Generated(generated) => generated.name(db),
176 }
177 }
178}
179impl ConcreteFunctionWithBodyId {
180 pub fn from_semantic(
181 db: &dyn LoweringGroup,
182 semantic: semantic::ConcreteFunctionWithBodyId,
183 ) -> Self {
184 ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db)
185 }
186 pub fn function_with_body_id(&self, db: &dyn LoweringGroup) -> FunctionWithBodyId {
187 self.lookup_intern(db).function_with_body_id(db)
188 }
189 pub fn substitution(&self, db: &dyn LoweringGroup) -> Maybe<GenericSubstitution> {
190 self.lookup_intern(db).substitution(db)
191 }
192 pub fn function_id(&self, db: &dyn LoweringGroup) -> Maybe<FunctionId> {
193 self.lookup_intern(db).function_id(db)
194 }
195 pub fn name(&self, db: &dyn LoweringGroup) -> SmolStr {
196 self.lookup_intern(db).name(db)
197 }
198 pub fn signature(&self, db: &dyn LoweringGroup) -> Maybe<Signature> {
199 let generic_signature = self.function_with_body_id(db).signature(db)?;
200 let substitution = self.substitution(db)?;
201 SubstitutionRewriter { db: db.upcast(), substitution: &substitution }
202 .rewrite(generic_signature)
203 }
204 pub fn from_no_generics_free(
205 db: &dyn LoweringGroup,
206 free_function_id: FreeFunctionId,
207 ) -> Option<Self> {
208 let semantic = semantic::ConcreteFunctionWithBodyId::from_no_generics_free(
209 db.upcast(),
210 free_function_id,
211 )?;
212 Some(ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db))
213 }
214 pub fn base_semantic_function(
215 &self,
216 db: &dyn LoweringGroup,
217 ) -> semantic::ConcreteFunctionWithBodyId {
218 self.lookup_intern(db).base_semantic_function(db)
219 }
220 pub fn stable_location(&self, db: &dyn LoweringGroup) -> Maybe<StableLocation> {
221 let semantic_db = db.upcast();
222 Ok(match self.lookup_intern(db) {
223 ConcreteFunctionWithBodyLongId::Semantic(id) => id.stable_location(semantic_db),
224 ConcreteFunctionWithBodyLongId::Generated(generated) => {
225 let parent_id = generated.parent.function_with_body_id(semantic_db);
226 match generated.key {
227 GeneratedFunctionKey::Loop(expr_id) => StableLocation::new(
228 db.function_body(parent_id)?.arenas.exprs[expr_id].stable_ptr().untyped(),
229 ),
230 GeneratedFunctionKey::TraitFunc(_, stable_location) => stable_location,
231 }
232 }
233 })
234 }
235}
236
237#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
239pub enum FunctionLongId {
240 Semantic(semantic::FunctionId),
242 Generated(GeneratedFunction),
244}
245define_short_id!(
246 FunctionId,
247 FunctionLongId,
248 LoweringGroup,
249 lookup_intern_lowering_function,
250 intern_lowering_function
251);
252impl FunctionLongId {
253 pub fn body(&self, db: &dyn LoweringGroup) -> Maybe<Option<ConcreteFunctionWithBodyId>> {
254 let semantic_db = db.upcast();
255 Ok(Some(match *self {
256 FunctionLongId::Semantic(id) => {
257 let concrete_function = id.get_concrete(semantic_db);
258 if let GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function }) =
259 concrete_function.generic_function
260 {
261 if let ImplLongId::GeneratedImpl(imp) = db.lookup_intern_impl(impl_id) {
262 let semantic_db = db.upcast();
263 let concrete_trait = imp.concrete_trait(semantic_db);
264
265 assert!(
266 [
267 semantic::corelib::destruct_trait_fn(semantic_db),
268 semantic::corelib::panic_destruct_trait_fn(semantic_db),
269 semantic::corelib::fn_once_call_trait_fn(semantic_db),
270 semantic::corelib::fn_call_trait_fn(semantic_db),
271 ]
272 .contains(&function)
273 );
274
275 let generic_args = concrete_trait.generic_args(semantic_db);
276 let Some(GenericArgumentId::Type(ty)) = generic_args.first() else {
277 unreachable!("Expected Generated Impl to have a type argument");
278 };
279 let TypeLongId::Closure(ty) = ty.lookup_intern(semantic_db) else {
280 unreachable!("Expected Generated Impl to have a closure type argument");
281 };
282
283 let Some(parent) =
284 ty.parent_function?.get_concrete(semantic_db).body(semantic_db)?
285 else {
286 return Ok(None);
287 };
288 return Ok(Some(
289 GeneratedFunction {
290 parent,
291 key: GeneratedFunctionKey::TraitFunc(id, ty.wrapper_location),
292 }
293 .body(db),
294 ));
295 }
296 }
297
298 let Some(body) = concrete_function.body(semantic_db)? else {
299 return Ok(None);
300 };
301 ConcreteFunctionWithBodyLongId::Semantic(body).intern(db)
302 }
303 FunctionLongId::Generated(generated) => generated.body(db),
304 }))
305 }
306 pub fn signature(&self, db: &dyn LoweringGroup) -> Maybe<Signature> {
307 match self {
308 FunctionLongId::Semantic(semantic) => {
309 Ok(Signature::from_semantic(db, db.concrete_function_signature(*semantic)?))
310 }
311 FunctionLongId::Generated(generated) => generated.body(db).signature(db),
312 }
313 }
314 pub fn name(&self, db: &dyn LoweringGroup) -> SmolStr {
315 match *self {
316 FunctionLongId::Semantic(semantic) => semantic.name(db.upcast()),
317 FunctionLongId::Generated(generated) => generated.name(db),
318 }
319 }
320 pub fn semantic_full_path(&self, db: &dyn LoweringGroup) -> String {
324 match self {
325 FunctionLongId::Semantic(id) => id.full_name(db.upcast()),
326 FunctionLongId::Generated(generated) => generated.parent.full_path(db.upcast()),
327 }
328 }
329}
330impl FunctionId {
331 pub fn body(&self, db: &dyn LoweringGroup) -> Maybe<Option<ConcreteFunctionWithBodyId>> {
332 self.lookup_intern(db).body(db)
333 }
334 pub fn signature(&self, db: &dyn LoweringGroup) -> Maybe<Signature> {
335 self.lookup_intern(db).signature(db)
336 }
337 pub fn name(&self, db: &dyn LoweringGroup) -> SmolStr {
338 self.lookup_intern(db).name(db)
339 }
340 pub fn semantic_full_path(&self, db: &dyn LoweringGroup) -> String {
341 self.lookup_intern(db).semantic_full_path(db)
342 }
343 pub fn get_extern(
346 &self,
347 db: &dyn LoweringGroup,
348 ) -> Option<(ExternFunctionId, Vec<GenericArgumentId>)> {
349 let semantic = try_extract_matches!(self.lookup_intern(db), FunctionLongId::Semantic)?;
350 let concrete = semantic.get_concrete(db.upcast());
351 Some((
352 try_extract_matches!(concrete.generic_function, GenericFunctionId::Extern)?,
353 concrete.generic_args,
354 ))
355 }
356}
357pub trait SemanticFunctionIdEx {
358 fn lowered(&self, db: &dyn LoweringGroup) -> FunctionId;
359}
360impl SemanticFunctionIdEx for semantic::FunctionId {
361 fn lowered(&self, db: &dyn LoweringGroup) -> FunctionId {
362 FunctionLongId::Semantic(*self).intern(db)
363 }
364}
365impl<'a> DebugWithDb<dyn LoweringGroup + 'a> for FunctionLongId {
366 fn fmt(
367 &self,
368 f: &mut std::fmt::Formatter<'_>,
369 db: &(dyn LoweringGroup + 'a),
370 ) -> std::fmt::Result {
371 match self {
372 FunctionLongId::Semantic(semantic) => semantic.fmt(f, db),
373 FunctionLongId::Generated(generated) => {
374 write!(f, "{}", generated.name(db))
375 }
376 }
377 }
378}
379
380#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
382pub enum GeneratedFunctionKey {
383 Loop(semantic::ExprId),
385 TraitFunc(semantic::FunctionId, StableLocation),
386}
387
388#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
390pub struct GeneratedFunction {
391 pub parent: semantic::ConcreteFunctionWithBodyId,
392 pub key: GeneratedFunctionKey,
393}
394impl GeneratedFunction {
395 pub fn body(&self, db: &dyn LoweringGroup) -> ConcreteFunctionWithBodyId {
396 let GeneratedFunction { parent, key } = *self;
397 let long_id = ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, key });
398 long_id.intern(db)
399 }
400 pub fn name(&self, db: &dyn LoweringGroup) -> SmolStr {
401 match self.key {
402 GeneratedFunctionKey::Loop(expr_id) => {
403 format!("{}[expr{}]", self.parent.full_path(db.upcast()), expr_id.index()).into()
404 }
405 GeneratedFunctionKey::TraitFunc(trait_func, _) => {
406 format!("{:?}", trait_func.debug(db)).into()
407 }
408 }
409 }
410}
411
412#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash)]
414#[debug_db(dyn LoweringGroup + 'a)]
415pub struct Signature {
416 pub params: Vec<semantic::ExprVarMemberPath>,
418 pub extra_rets: Vec<semantic::ExprVarMemberPath>,
420 pub return_type: semantic::TypeId,
422 pub implicits: Vec<semantic::TypeId>,
424 #[dont_rewrite]
426 pub panicable: bool,
427 #[dont_rewrite]
429 #[hide_field_debug_with_db]
430 pub location: LocationId,
431}
432impl Signature {
433 pub fn from_semantic(db: &dyn LoweringGroup, value: semantic::Signature) -> Self {
434 let semantic::Signature { params, return_type, implicits, panicable, stable_ptr } = value;
435 let ref_params = params
436 .iter()
437 .filter(|param| param.mutability == Mutability::Reference)
438 .map(|param| parameter_as_member_path(param.clone()))
439 .collect();
440 let params: Vec<semantic::ExprVarMemberPath> =
441 params.into_iter().map(parameter_as_member_path).collect();
442 Self {
443 params,
444 extra_rets: ref_params,
445 return_type,
446 implicits,
447 panicable,
448 location: LocationId::from_stable_location(
449 db,
450 StableLocation::new(stable_ptr.untyped()),
451 ),
452 }
453 }
454 pub fn is_fully_concrete(&self, db: &dyn LoweringGroup) -> bool {
455 let semantic_db = db.upcast();
456 self.params.iter().all(|param| param.ty().is_fully_concrete(semantic_db))
457 && self.extra_rets.iter().all(|param| param.ty().is_fully_concrete(semantic_db))
458 && self.return_type.is_fully_concrete(semantic_db)
459 && self.implicits.iter().all(|ty| ty.is_fully_concrete(semantic_db))
460 }
461}
462semantic::add_rewrite!(<'a>, SubstitutionRewriter<'a>, DiagnosticAdded, Signature);
463
464pub(crate) fn parameter_as_member_path(param: semantic::Parameter) -> semantic::ExprVarMemberPath {
466 let semantic::Parameter { id, ty, stable_ptr, .. } = param;
467 semantic::ExprVarMemberPath::Var(ExprVar {
468 var: semantic::VarId::Param(id),
469 ty,
470 stable_ptr: ast::ExprPtr(stable_ptr.0),
471 })
472}
473
474define_short_id!(LocationId, Location, LoweringGroup, lookup_intern_location, intern_location);
475impl LocationId {
476 pub fn from_stable_location(
477 db: &dyn LoweringGroup,
478 stable_location: StableLocation,
479 ) -> LocationId {
480 Location::new(stable_location).intern(db)
481 }
482
483 pub fn with_note(&self, db: &dyn LoweringGroup, note: DiagnosticNote) -> LocationId {
485 self.lookup_intern(db).with_note(note).intern(db)
486 }
487
488 pub fn with_auto_generation_note(
490 &self,
491 db: &dyn LoweringGroup,
492 logic_name: &str,
493 ) -> LocationId {
494 self.with_note(
495 db,
496 DiagnosticNote::text_only(format!(
497 "this error originates in auto-generated {logic_name} logic."
498 )),
499 )
500 }
501}