Skip to main content

cairo_lang_lowering/
ids.rs

1use std::iter::repeat;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_defs as defs;
5use cairo_lang_defs::ids::{NamedLanguageElementId, TopLevelLanguageElementId, TraitFunctionId};
6use cairo_lang_diagnostics::{DiagnosticAdded, DiagnosticNote, Maybe, MaybeAsRef};
7use cairo_lang_proc_macros::{DebugWithDb, HeapSize, SemanticObject};
8use cairo_lang_semantic as semantic;
9use cairo_lang_semantic::corelib::CorelibSemantic;
10use cairo_lang_semantic::items::functions::{FunctionsSemantic, ImplGenericFunctionId};
11use cairo_lang_semantic::items::imp::ImplLongId;
12use cairo_lang_semantic::items::structure::StructSemantic;
13use cairo_lang_semantic::{ConcreteTypeId, GenericArgumentId, TypeLongId};
14use cairo_lang_syntax::node::ast::ExprPtr;
15use cairo_lang_syntax::node::helpers::generated_function_path;
16use cairo_lang_syntax::node::{TypedStablePtr, ast};
17use cairo_lang_utils::{Intern, define_short_id, extract_matches, try_extract_matches};
18use defs::diagnostic_utils::StableLocation;
19use defs::ids::{ExternFunctionId, FreeFunctionId};
20use itertools::zip_eq;
21use salsa::Database;
22use semantic::items::functions::GenericFunctionId;
23use semantic::substitution::{GenericSubstitution, SubstitutionRewriter};
24use semantic::{ExprVar, Mutability};
25
26use crate::db::LoweringGroup;
27use crate::ids::semantic::substitution::SemanticRewriter;
28use crate::specialization::SpecializationArg;
29use crate::{Location, LoweringStage};
30
31#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, HeapSize)]
32pub enum FunctionWithBodyLongId<'db> {
33    Semantic(defs::ids::FunctionWithBodyId<'db>),
34    Generated { parent: defs::ids::FunctionWithBodyId<'db>, key: GeneratedFunctionKey<'db> },
35}
36define_short_id!(FunctionWithBodyId, FunctionWithBodyLongId<'db>);
37impl<'db> FunctionWithBodyLongId<'db> {
38    pub fn base_semantic_function(
39        &self,
40        _db: &'db dyn Database,
41    ) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
42        match self {
43            FunctionWithBodyLongId::Semantic(id) => *id,
44            FunctionWithBodyLongId::Generated { parent, .. } => *parent,
45        }
46    }
47    pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyLongId<'db>> {
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: *key,
56                })
57            }
58        })
59    }
60}
61impl<'db> FunctionWithBodyId<'db> {
62    pub fn base_semantic_function(
63        &self,
64        db: &'db dyn Database,
65    ) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
66        self.long(db).base_semantic_function(db)
67    }
68    /// Returns the signature of the generic, pre-monomorphization function.
69    ///
70    /// [`LoweringStage`] deliberately does not apply here: the staged lowerings all run on
71    /// concrete (monomorphized) functions, so the only lowering this id has is
72    /// `function_with_body_lowering`.
73    pub fn signature(&self, db: &'db dyn Database) -> Maybe<&'db Signature<'db>> {
74        Ok(&db.function_with_body_lowering(*self)?.signature)
75    }
76    pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyId<'db>> {
77        Ok(self.long(db).to_concrete(db)?.intern(db))
78    }
79}
80pub trait SemanticFunctionWithBodyIdEx<'db> {
81    fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db>;
82}
83impl<'db> SemanticFunctionWithBodyIdEx<'db> for cairo_lang_defs::ids::FunctionWithBodyId<'db> {
84    fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db> {
85        FunctionWithBodyLongId::Semantic(*self).intern(db)
86    }
87}
88
89/// Concrete function with body.
90#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, HeapSize)]
91pub enum ConcreteFunctionWithBodyLongId<'db> {
92    Semantic(semantic::ConcreteFunctionWithBodyId<'db>),
93    Generated(GeneratedFunction<'db>),
94    Specialized(SpecializedFunctionId<'db>),
95}
96define_short_id!(ConcreteFunctionWithBodyId, ConcreteFunctionWithBodyLongId<'db>);
97
98// The result of `generic_or_specialized`.
99pub enum GenericOrSpecialized<'db> {
100    Generic(FunctionWithBodyId<'db>),
101    Specialized(SpecializedFunctionId<'db>),
102}
103
104impl<'db> ConcreteFunctionWithBodyId<'db> {
105    pub fn is_panic_destruct_fn(&self, db: &'db dyn Database) -> Maybe<bool> {
106        match self.long(db) {
107            ConcreteFunctionWithBodyLongId::Semantic(semantic_func) => {
108                semantic_func.is_panic_destruct_fn(db)
109            }
110            ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
111                parent: _,
112                key: GeneratedFunctionKey::TraitFunc(function, _),
113            }) => Ok(function == &db.core_info().panic_destruct_fn),
114            _ => Ok(false),
115        }
116    }
117
118    /// Returns the generic version of the function if it exists, otherwise the function is a
119    /// specialized function and the `SpecializedFunction` struct is returned.
120    pub fn generic_or_specialized(&self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
121        self.long(db).clone().generic_or_specialized(db)
122    }
123}
124
125impl<'db> ConcreteFunctionWithBodyLongId<'db> {
126    /// Returns the generic `FunctionWithLongId` if one exists, otherwise returns the specialized
127    /// function.
128    pub fn generic_or_specialized(self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
129        let long_id = match self {
130            ConcreteFunctionWithBodyLongId::Semantic(id) => {
131                FunctionWithBodyLongId::Semantic(id.function_with_body_id(db))
132            }
133            ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, key }) => {
134                FunctionWithBodyLongId::Generated { parent: parent.function_with_body_id(db), key }
135            }
136            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
137                return GenericOrSpecialized::Specialized(specialized);
138            }
139        };
140        GenericOrSpecialized::Generic(long_id.intern(db))
141    }
142    pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
143        match self {
144            ConcreteFunctionWithBodyLongId::Semantic(id) => id.substitution(db),
145            ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, .. }) => {
146                parent.substitution(db)
147            }
148            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
149                specialized.long(db).base.substitution(db)
150            }
151        }
152    }
153    pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
154        let long_id = match self {
155            ConcreteFunctionWithBodyLongId::Semantic(id) => {
156                FunctionLongId::Semantic(id.function_id(db)?)
157            }
158            ConcreteFunctionWithBodyLongId::Generated(generated) => {
159                FunctionLongId::Generated(*generated)
160            }
161            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
162                FunctionLongId::Specialized(*specialized)
163            }
164        };
165        Ok(long_id.intern(db))
166    }
167    pub fn base_semantic_function(
168        &self,
169        db: &'db dyn Database,
170    ) -> semantic::ConcreteFunctionWithBodyId<'db> {
171        match self {
172            ConcreteFunctionWithBodyLongId::Semantic(id) => *id,
173            ConcreteFunctionWithBodyLongId::Generated(generated) => generated.parent,
174            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
175                specialized.long(db).base.base_semantic_function(db)
176            }
177        }
178    }
179    pub fn full_path(&self, db: &dyn Database) -> String {
180        format!("{:?}", self.debug(db))
181    }
182}
183impl<'db> DebugWithDb<'db> for ConcreteFunctionWithBodyLongId<'db> {
184    type Db = dyn Database;
185    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
186        match self {
187            ConcreteFunctionWithBodyLongId::Semantic(semantic) => {
188                write!(f, "{:?}", semantic.debug(db))
189            }
190            ConcreteFunctionWithBodyLongId::Generated(generated) => {
191                write!(f, "{:?}", generated.debug(db))
192            }
193            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
194                write!(f, "{:?}", specialized.debug(db))
195            }
196        }
197    }
198}
199impl<'db> ConcreteFunctionWithBodyId<'db> {
200    pub fn from_semantic(
201        db: &'db dyn Database,
202        semantic: semantic::ConcreteFunctionWithBodyId<'db>,
203    ) -> Self {
204        ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db)
205    }
206    pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
207        self.long(db).substitution(db)
208    }
209    pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
210        self.long(db).function_id(db)
211    }
212    pub fn full_path(&self, db: &dyn Database) -> String {
213        self.long(db).full_path(db)
214    }
215    /// Returns the signature of the function as it is at the given [`LoweringStage`].
216    pub fn signature(
217        &self,
218        db: &'db dyn Database,
219        stage: LoweringStage,
220    ) -> Maybe<&'db Signature<'db>> {
221        if stage != LoweringStage::Monomorphized {
222            return Ok(&db.lowered_body(*self, stage)?.signature);
223        }
224        // The `Monomorphized` signature is derived from the semantic one, to prevent cycles.
225        monomorphized_signature(db, *self).maybe_as_ref()
226    }
227    /// Returns the generic function if `self` is not a specialized function.
228    pub fn generic(&self, db: &'db dyn Database) -> Option<FunctionWithBodyId<'db>> {
229        match self.generic_or_specialized(db) {
230            GenericOrSpecialized::Generic(id) => Some(id),
231            GenericOrSpecialized::Specialized(_) => None,
232        }
233    }
234    pub fn from_no_generics_free(
235        db: &'db dyn Database,
236        free_function_id: FreeFunctionId<'db>,
237    ) -> Option<Self> {
238        let semantic =
239            semantic::ConcreteFunctionWithBodyId::from_no_generics_free(db, free_function_id)?;
240        Some(ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db))
241    }
242    pub fn base_semantic_function(
243        &self,
244        db: &'db dyn Database,
245    ) -> semantic::ConcreteFunctionWithBodyId<'db> {
246        self.long(db).base_semantic_function(db)
247    }
248    pub fn stable_location(&self, db: &'db dyn Database) -> Maybe<StableLocation<'db>> {
249        Ok(match self.long(db) {
250            ConcreteFunctionWithBodyLongId::Semantic(id) => id.stable_location(db),
251            ConcreteFunctionWithBodyLongId::Generated(generated) => match generated.key {
252                GeneratedFunctionKey::Loop(stable_ptr) => StableLocation::new(stable_ptr.untyped()),
253                GeneratedFunctionKey::TraitFunc(_, stable_location) => stable_location,
254            },
255            ConcreteFunctionWithBodyLongId::Specialized(specialized_function) => {
256                specialized_function.long(db).base.stable_location(db)?
257            }
258        })
259    }
260}
261
262/// The `Monomorphized` signature of a concrete function: for a generic function, the generic
263/// signature with the function's substitution applied; for a specialized function, computed from
264/// its base signature and specialization args.
265#[salsa::tracked(returns(ref))]
266fn monomorphized_signature<'db>(
267    db: &'db dyn Database,
268    function: ConcreteFunctionWithBodyId<'db>,
269) -> Maybe<Signature<'db>> {
270    match function.generic_or_specialized(db) {
271        GenericOrSpecialized::Generic(id) => {
272            let generic_signature = id.signature(db)?.clone();
273            function.substitution(db)?.substitute(db, generic_signature)
274        }
275        GenericOrSpecialized::Specialized(specialized) => {
276            let function = specialized.long(db);
277            let mut base_sign = function.base.signature(db, LoweringStage::Monomorphized)?.clone();
278
279            let mut params = vec![];
280            let mut stack: Vec<_> =
281                zip_eq(base_sign.params.into_iter().rev(), function.args.iter().rev()).collect();
282
283            while let Some((param_ty, arg)) = stack.pop() {
284                match arg {
285                    SpecializationArg::Const { .. } => {}
286                    SpecializationArg::Snapshot(inner) => {
287                        let desnap_ty = *extract_matches!(param_ty.long(db), TypeLongId::Snapshot);
288                        stack.push((desnap_ty, inner.as_ref()));
289                    }
290                    SpecializationArg::Enum { variant, payload } => {
291                        stack.push((variant.ty, payload.as_ref()));
292                    }
293                    SpecializationArg::Array(ty, values) => {
294                        stack.extend(repeat(*ty).zip(values.iter().rev()));
295                    }
296                    SpecializationArg::Struct(args) => match param_ty.long(db) {
297                        TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct)) => {
298                            let Ok(members) = db.concrete_struct_members(*concrete_struct) else {
299                                continue;
300                            };
301                            let tys = members.values().map(|m| m.ty);
302                            stack.extend(zip_eq(tys.rev(), args.iter().rev()));
303                        }
304                        TypeLongId::Tuple(tys) => {
305                            stack.extend(zip_eq(tys.iter().rev().copied(), args.iter().rev()));
306                        }
307                        TypeLongId::FixedSizeArray { type_id, .. } => {
308                            stack.extend(repeat(*type_id).zip(args.iter().rev()));
309                        }
310                        _ => unreachable!("Expected a struct, tuple, or fixed-size array type"),
311                    },
312                    SpecializationArg::NotSpecialized => {
313                        params.push(param_ty);
314                    }
315                }
316            }
317
318            base_sign.params = params;
319
320            Ok(base_sign)
321        }
322    }
323}
324
325/// Function.
326#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, HeapSize)]
327pub enum FunctionLongId<'db> {
328    /// An original function from the user code.
329    Semantic(semantic::FunctionId<'db>),
330    /// A function generated by the compiler.
331    Generated(GeneratedFunction<'db>),
332    /// A specialized function.
333    Specialized(SpecializedFunctionId<'db>),
334}
335define_short_id!(FunctionId, FunctionLongId<'db>);
336impl<'db> FunctionLongId<'db> {
337    pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
338        Ok(Some(match self {
339            FunctionLongId::Semantic(id) => {
340                let concrete_function = id.get_concrete(db);
341                if let GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function }) =
342                    concrete_function.generic_function
343                    && let ImplLongId::GeneratedImpl(imp) = impl_id.long(db)
344                {
345                    let concrete_trait = imp.concrete_trait(db);
346                    let info = db.core_info();
347                    assert!(
348                        [info.destruct_fn, info.panic_destruct_fn, info.call_fn, info.call_once_fn]
349                            .contains(&function)
350                    );
351
352                    let generic_args = concrete_trait.generic_args(db);
353                    let Some(GenericArgumentId::Type(ty)) = generic_args.first() else {
354                        unreachable!("Expected Generated Impl to have a type argument");
355                    };
356                    let TypeLongId::Closure(ty) = ty.long(db) else {
357                        unreachable!("Expected Generated Impl to have a closure type argument");
358                    };
359
360                    let Some(parent) = ty.parent_function?.get_concrete(db).body(db)? else {
361                        return Ok(None);
362                    };
363                    return Ok(Some(
364                        GeneratedFunction {
365                            parent,
366                            key: GeneratedFunctionKey::TraitFunc(function, ty.params_location),
367                        }
368                        .body(db),
369                    ));
370                }
371
372                let Some(body) = concrete_function.body(db)? else {
373                    return Ok(None);
374                };
375                ConcreteFunctionWithBodyLongId::Semantic(body).intern(db)
376            }
377            FunctionLongId::Generated(generated) => generated.body(db),
378            FunctionLongId::Specialized(specialized) => {
379                ConcreteFunctionWithBodyLongId::Specialized(*specialized).intern(db)
380            }
381        }))
382    }
383    /// Returns the signature of the function as it is at the given [`LoweringStage`].
384    pub fn signature(
385        &self,
386        db: &'db dyn Database,
387        stage: LoweringStage,
388    ) -> Maybe<&'db Signature<'db>> {
389        if let Some(body) = self.body(db)? {
390            body.signature(db, stage)
391        } else if let FunctionLongId::Semantic(semantic) = self {
392            semantic_function_signature(db, *semantic).maybe_as_ref()
393        } else {
394            unreachable!(
395                "Generated and specialized functions always have a body: {}",
396                self.full_path(db)
397            );
398        }
399    }
400    pub fn full_path(&self, db: &dyn Database) -> String {
401        format!("{:?}", self.debug(db))
402    }
403    /// Returns the full path of the relevant semantic function:
404    /// - If the function itself is semantic (non generated), its own full path.
405    /// - If the function is generated, then its (semantic) parent's full path.
406    pub fn semantic_full_path(&self, db: &dyn Database) -> String {
407        match self {
408            FunctionLongId::Semantic(id) => id.full_path(db),
409            FunctionLongId::Generated(generated) => generated.parent.full_path(db),
410            FunctionLongId::Specialized(specialized) => specialized.long(db).full_path(db),
411        }
412    }
413}
414
415/// The stage-invariant signature of a bodyless (e.g. `extern`) semantic function, converted to
416/// the lowered [`Signature`] representation.
417#[salsa::tracked(returns(ref))]
418fn semantic_function_signature<'db>(
419    db: &'db dyn Database,
420    function: semantic::FunctionId<'db>,
421) -> Maybe<Signature<'db>> {
422    Ok(EnrichedSemanticSignature::from_semantic(db, db.concrete_function_signature(function)?)
423        .into())
424}
425impl<'db> FunctionId<'db> {
426    pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
427        self.long(db).body(db)
428    }
429    pub fn signature(
430        &self,
431        db: &'db dyn Database,
432        stage: LoweringStage,
433    ) -> Maybe<&'db Signature<'db>> {
434        self.long(db).signature(db, stage)
435    }
436    pub fn full_path(&self, db: &dyn Database) -> String {
437        self.long(db).full_path(db)
438    }
439    pub fn semantic_full_path(&self, db: &dyn Database) -> String {
440        self.long(db).semantic_full_path(db)
441    }
442    /// Returns the function as an `ExternFunctionId` and its generic arguments, if it is an
443    /// `extern` functions.
444    pub fn get_extern(
445        &self,
446        db: &'db dyn Database,
447    ) -> Option<(ExternFunctionId<'db>, Vec<GenericArgumentId<'db>>)> {
448        let semantic = try_extract_matches!(self.long(db), FunctionLongId::Semantic)?;
449        let concrete = semantic.get_concrete(db);
450        Some((
451            try_extract_matches!(concrete.generic_function, GenericFunctionId::Extern)?,
452            concrete.generic_args,
453        ))
454    }
455}
456pub trait SemanticFunctionIdEx<'db> {
457    fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db>;
458}
459impl<'db> SemanticFunctionIdEx<'db> for semantic::FunctionId<'db> {
460    fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db> {
461        let ret = FunctionLongId::Semantic(*self).intern(db);
462        // If the function is generated, we need to check if it has a body, so we can return its
463        // generated function id.
464        // TODO(orizi): This is a hack, we should have a better way to do this.
465        if let Ok(Some(body)) = ret.body(db)
466            && let Ok(id) = body.function_id(db)
467        {
468            return id;
469        }
470        ret
471    }
472}
473impl<'a> DebugWithDb<'a> for FunctionLongId<'a> {
474    type Db = dyn Database;
475    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
476        match self {
477            FunctionLongId::Semantic(semantic) => write!(f, "{:?}", semantic.debug(db)),
478            FunctionLongId::Generated(generated) => write!(f, "{:?}", generated.debug(db)),
479            FunctionLongId::Specialized(specialized) => write!(f, "{:?}", specialized.debug(db)),
480        }
481    }
482}
483
484/// A key for generated functions.
485#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HeapSize, salsa::SalsaValue)]
486pub enum GeneratedFunctionKey<'db> {
487    /// Generated loop functions are identified by the loop's AST pointer (ExprPtr).
488    Loop(ExprPtr<'db>),
489    TraitFunc(TraitFunctionId<'db>, StableLocation<'db>),
490}
491
492/// Generated function.
493#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, HeapSize)]
494pub struct GeneratedFunction<'db> {
495    pub parent: semantic::ConcreteFunctionWithBodyId<'db>,
496    pub key: GeneratedFunctionKey<'db>,
497}
498impl<'db> GeneratedFunction<'db> {
499    pub fn body(&self, db: &'db dyn Database) -> ConcreteFunctionWithBodyId<'db> {
500        let long_id = ConcreteFunctionWithBodyLongId::Generated(*self);
501        long_id.intern(db)
502    }
503    pub fn full_path(&self, db: &dyn Database) -> String {
504        format!("{:?}", self.debug(db))
505    }
506}
507impl<'a> DebugWithDb<'a> for GeneratedFunction<'a> {
508    type Db = dyn Database;
509    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
510        match self.key {
511            GeneratedFunctionKey::Loop(expr_ptr) => {
512                write!(
513                    f,
514                    "{:?}::{}",
515                    self.parent.debug(db),
516                    generated_function_path(db, expr_ptr.0.lookup(db))
517                )
518            }
519            GeneratedFunctionKey::TraitFunc(trait_func, loc) => {
520                let trait_id = trait_func.trait_id(db);
521                write!(
522                    f,
523                    "Generated `{}::{}` for {:?}::{}",
524                    trait_id.full_path(db),
525                    trait_func.name(db).long(db),
526                    self.parent.debug(db),
527                    generated_function_path(db, loc.syntax_node(db)),
528                )
529            }
530        }
531    }
532}
533
534/// Specialized function
535///
536/// Specialized functions are generated by the compiler some of the function
537/// arguments are known at compile time and the resulting specialized function is smaller
538/// than the original one.
539///
540/// Specialized functions are identified by the base function and the arguments.
541#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, HeapSize)]
542pub struct SpecializedFunction<'db> {
543    /// The base function.
544    pub base: crate::ids::ConcreteFunctionWithBodyId<'db>,
545    /// Optional const assignments for the arguments.
546    pub args: Vec<SpecializationArg<'db>>,
547}
548define_short_id!(SpecializedFunctionId, SpecializedFunction<'db>);
549
550impl<'db> SpecializedFunction<'db> {
551    pub fn full_path(&self, db: &dyn Database) -> String {
552        format!("{:?}", self.debug(db))
553    }
554}
555
556impl<'a> DebugWithDb<'a> for SpecializedFunction<'a> {
557    type Db = dyn Database;
558    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
559        write!(f, "{}{{", self.base.full_path(db))?;
560        for arg in self.args.iter() {
561            write!(f, "{:?}, ", arg.debug(db))?;
562        }
563        write!(f, "}}")
564    }
565}
566
567/// Signature for lowering a function.
568#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::SalsaValue)]
569#[debug_db(dyn Database)]
570pub struct EnrichedSemanticSignature<'db> {
571    /// Input params.
572    pub params: Vec<semantic::ExprVarMemberPath<'db>>,
573    /// Extra returns - e.g. ref params.
574    pub extra_rets: Vec<semantic::ExprVarMemberPath<'db>>,
575    /// Return type.
576    pub return_type: semantic::TypeId<'db>,
577    /// Explicit implicit requirements.
578    pub implicits: Vec<semantic::TypeId<'db>>,
579    /// Panicable.
580    #[dont_rewrite]
581    pub panicable: bool,
582    /// Location.
583    #[dont_rewrite]
584    #[hide_field_debug_with_db]
585    pub location: LocationId<'db>,
586}
587impl<'db> EnrichedSemanticSignature<'db> {
588    pub fn from_semantic(db: &'db dyn Database, value: &semantic::Signature<'db>) -> Self {
589        let semantic::Signature {
590            params,
591            return_type,
592            implicits,
593            panicable,
594            stable_ptr,
595            is_const: _,
596        } = value;
597        let ref_params = params
598            .iter()
599            .filter(|param| param.mutability == Mutability::Reference)
600            .map(|param| parameter_as_member_path(param.clone()))
601            .collect();
602        let params: Vec<semantic::ExprVarMemberPath<'_>> =
603            params.iter().cloned().map(parameter_as_member_path).collect();
604        Self {
605            params,
606            extra_rets: ref_params,
607            return_type: *return_type,
608            implicits: implicits.clone(),
609            panicable: *panicable,
610            location: LocationId::from_stable_location(
611                db,
612                StableLocation::new(stable_ptr.untyped()),
613            ),
614        }
615    }
616    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
617        self.params.iter().all(|param| param.ty().is_fully_concrete(db))
618            && self.extra_rets.iter().all(|param| param.ty().is_fully_concrete(db))
619            && self.return_type.is_fully_concrete(db)
620            && self.implicits.iter().all(|ty| ty.is_fully_concrete(db))
621    }
622}
623semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, EnrichedSemanticSignature<'a>);
624
625/// Lowered signature of a function.
626#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::SalsaValue)]
627#[debug_db(dyn Database)]
628pub struct Signature<'db> {
629    /// Types of the input params.
630    pub params: Vec<semantic::TypeId<'db>>,
631    /// Types of the extra returns, returned before `return_type` - e.g. ref params.
632    pub extra_rets: Vec<semantic::TypeId<'db>>,
633    /// Return type.
634    pub return_type: semantic::TypeId<'db>,
635    /// The implicit requirements declared in the function's signature.
636    ///
637    /// Only meaningful before `LowerImplicits` ran - from `LoweringStage::Final` on the implicits
638    /// appear explicitly in `params` and `extra_rets`, and this is empty.
639    pub declared_implicits: Vec<semantic::TypeId<'db>>,
640    /// Panicable.
641    #[dont_rewrite]
642    pub panicable: bool,
643    /// Location.
644    #[dont_rewrite]
645    #[hide_field_debug_with_db]
646    pub location: LocationId<'db>,
647}
648semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, Signature<'a>);
649
650impl<'db> From<EnrichedSemanticSignature<'db>> for Signature<'db> {
651    fn from(signature: EnrichedSemanticSignature<'db>) -> Self {
652        Signature {
653            params: signature.params.iter().map(|param| param.ty()).collect(),
654            extra_rets: signature.extra_rets.iter().map(|param| param.ty()).collect(),
655            return_type: signature.return_type,
656            declared_implicits: signature.implicits,
657            panicable: signature.panicable,
658            location: signature.location,
659        }
660    }
661}
662
663/// Converts a [semantic::Parameter] to a [semantic::ExprVarMemberPath].
664pub(crate) fn parameter_as_member_path<'db>(
665    param: semantic::Parameter<'db>,
666) -> semantic::ExprVarMemberPath<'db> {
667    let semantic::Parameter { id, ty, stable_ptr, .. } = param;
668    semantic::ExprVarMemberPath::Var(ExprVar {
669        var: semantic::VarId::Param(id),
670        ty,
671        stable_ptr: ast::ExprPtr(stable_ptr.0),
672    })
673}
674
675define_short_id!(LocationId, Location<'db>);
676impl<'db> LocationId<'db> {
677    pub fn from_stable_location(
678        db: &'db dyn Database,
679        stable_location: StableLocation<'db>,
680    ) -> LocationId<'db> {
681        Location::new(stable_location).intern(db)
682    }
683
684    /// Adds a note to the location.
685    pub fn with_note(&self, db: &'db dyn Database, note: DiagnosticNote<'db>) -> LocationId<'db> {
686        self.long(db).clone().with_note(note).intern(db)
687    }
688
689    /// Adds a note that this location was generated while compiling an auto-generated function.
690    pub fn with_auto_generation_note(
691        &self,
692        db: &'db dyn Database,
693        logic_name: &str,
694    ) -> LocationId<'db> {
695        self.with_note(
696            db,
697            DiagnosticNote::text_only(format!(
698                "this error originates in auto-generated {logic_name} logic."
699            )),
700        )
701    }
702}