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.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/// Concrete function with body.
88#[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/// Function.
226#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
227pub enum FunctionLongId {
228    /// An original function from the user code.
229    Semantic(semantic::FunctionId),
230    /// A function generated by the compiler.
231    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    /// Returns the full path of the relevant semantic function:
306    /// - If the function itself is semantic (non generated), its own full path.
307    /// - If the function is generated, then its (semantic) parent's full path.
308    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    /// Returns the function as an `ExternFunctionId` and its generic arguments, if it is an
329    /// `extern` functions.
330    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 the function is generated, we need to check if it has a body, so we can return its
349        // generated function id.
350        // TODO(orizi): This is a hack, we should have a better way to do this.
351        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/// A key for a generated functions.
373#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
374pub enum GeneratedFunctionKey {
375    /// Generated loop functions are identified by the loop expr_id.
376    Loop(ExprPtr),
377    TraitFunc(TraitFunctionId, StableLocation),
378}
379
380/// Generated function.
381#[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/// Lowered signature of a function.
436#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash)]
437#[debug_db(dyn LoweringGroup + 'a)]
438pub struct Signature {
439    /// Input params.
440    pub params: Vec<semantic::ExprVarMemberPath>,
441    /// Extra returns - e.g. ref params
442    pub extra_rets: Vec<semantic::ExprVarMemberPath>,
443    /// Return type.
444    pub return_type: semantic::TypeId,
445    /// Explicit implicit requirements.
446    pub implicits: Vec<semantic::TypeId>,
447    /// Panicable.
448    #[dont_rewrite]
449    pub panicable: bool,
450    /// Location.
451    #[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
494/// Converts a [semantic::Parameter] to a [semantic::ExprVarMemberPath].
495pub(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    /// Adds a note to the location.
514    pub fn with_note(&self, db: &dyn LoweringGroup, note: DiagnosticNote) -> LocationId {
515        self.lookup_intern(db).with_note(note).intern(db)
516    }
517
518    /// Adds a note that this location was generated while compiling an auto-generated function.
519    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}