Skip to main content

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