cairo_lang_lowering/
ids.rs

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/// Concrete function with body.
85#[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/// Function.
214#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
215pub enum FunctionLongId {
216    /// An original function from the user code.
217    Semantic(semantic::FunctionId),
218    /// A function generated by the compiler.
219    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    /// Returns the full path of the relevant semantic function:
290    /// - If the function itself is semantic (non generated), its own full path.
291    /// - If the function is generated, then its (semantic) parent's full path.
292    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    /// Returns the function as an `ExternFunctionId` and its generic arguments, if it is an
313    /// `extern` functions.
314    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 the function is generated, we need to check if it has a body, so we can return its
333        // generated function id.
334        // TODO(orizi): This is a hack, we should have a better way to do this.
335        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/// A key for a generated functions.
357#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
358pub enum GeneratedFunctionKey {
359    /// Generated loop functions are identified by the loop expr_id.
360    Loop(ExprPtr),
361    TraitFunc(TraitFunctionId, StableLocation),
362}
363
364/// Generated function.
365#[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/// Lowered signature of a function.
420#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash)]
421#[debug_db(dyn LoweringGroup + 'a)]
422pub struct Signature {
423    /// Input params.
424    pub params: Vec<semantic::ExprVarMemberPath>,
425    /// Extra returns - e.g. ref params
426    pub extra_rets: Vec<semantic::ExprVarMemberPath>,
427    /// Return type.
428    pub return_type: semantic::TypeId,
429    /// Explicit implicit requirements.
430    pub implicits: Vec<semantic::TypeId>,
431    /// Panicable.
432    #[dont_rewrite]
433    pub panicable: bool,
434    /// Location.
435    #[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
477/// Converts a [semantic::Parameter] to a [semantic::ExprVarMemberPath].
478pub(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    /// Adds a note to the location.
497    pub fn with_note(&self, db: &dyn LoweringGroup, note: DiagnosticNote) -> LocationId {
498        self.lookup_intern(db).with_note(note).intern(db)
499    }
500
501    /// Adds a note that this location was generated while compiling an auto-generated function.
502    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}