cairo_lang_lowering/
ids.rs

1use std::sync::Arc;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_defs::ids::{
5    NamedLanguageElementId, TopLevelLanguageElementId, TraitFunctionId, UnstableSalsaId,
6};
7use cairo_lang_diagnostics::{DiagnosticAdded, DiagnosticNote, Maybe};
8use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
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::kind::SyntaxKind;
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};
25use {cairo_lang_defs as defs, cairo_lang_semantic as semantic};
26
27use crate::Location;
28use crate::db::LoweringGroup;
29use crate::ids::semantic::substitution::SemanticRewriter;
30use crate::specialization::SpecializationArg;
31
32#[derive(Clone, Debug, Hash, PartialEq, Eq)]
33pub enum FunctionWithBodyLongId<'db> {
34    Semantic(defs::ids::FunctionWithBodyId<'db>),
35    Generated { parent: defs::ids::FunctionWithBodyId<'db>, key: GeneratedFunctionKey<'db> },
36}
37define_short_id!(FunctionWithBodyId, FunctionWithBodyLongId<'db>);
38impl<'db> FunctionWithBodyLongId<'db> {
39    pub fn base_semantic_function(
40        &self,
41        _db: &'db dyn Database,
42    ) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
43        match self {
44            FunctionWithBodyLongId::Semantic(id) => *id,
45            FunctionWithBodyLongId::Generated { parent, .. } => *parent,
46        }
47    }
48    pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyLongId<'db>> {
49        Ok(match self {
50            FunctionWithBodyLongId::Semantic(semantic) => ConcreteFunctionWithBodyLongId::Semantic(
51                semantic::ConcreteFunctionWithBodyId::from_generic(db, *semantic)?,
52            ),
53            FunctionWithBodyLongId::Generated { parent, key } => {
54                ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
55                    parent: semantic::ConcreteFunctionWithBodyId::from_generic(db, *parent)?,
56                    key: *key,
57                })
58            }
59        })
60    }
61}
62impl<'db> FunctionWithBodyId<'db> {
63    pub fn base_semantic_function(
64        &self,
65        db: &'db dyn Database,
66    ) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
67        self.long(db).base_semantic_function(db)
68    }
69    pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
70        Ok(db.function_with_body_lowering(*self)?.signature.clone())
71    }
72    pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyId<'db>> {
73        Ok(self.long(db).to_concrete(db)?.intern(db))
74    }
75}
76pub trait SemanticFunctionWithBodyIdEx<'db> {
77    fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db>;
78}
79impl<'db> SemanticFunctionWithBodyIdEx<'db> for cairo_lang_defs::ids::FunctionWithBodyId<'db> {
80    fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db> {
81        FunctionWithBodyLongId::Semantic(*self).intern(db)
82    }
83}
84
85/// Concrete function with body.
86#[derive(Clone, Debug, Hash, PartialEq, Eq)]
87pub enum ConcreteFunctionWithBodyLongId<'db> {
88    Semantic(semantic::ConcreteFunctionWithBodyId<'db>),
89    Generated(GeneratedFunction<'db>),
90    Specialized(SpecializedFunction<'db>),
91}
92define_short_id!(ConcreteFunctionWithBodyId, ConcreteFunctionWithBodyLongId<'db>);
93
94// The result of `generic_or_specialized`.
95pub enum GenericOrSpecialized<'db> {
96    Generic(FunctionWithBodyId<'db>),
97    Specialized(SpecializedFunction<'db>),
98}
99
100impl<'db> ConcreteFunctionWithBodyId<'db> {
101    pub fn is_panic_destruct_fn(&self, db: &'db dyn Database) -> Maybe<bool> {
102        match self.long(db) {
103            ConcreteFunctionWithBodyLongId::Semantic(semantic_func) => {
104                semantic_func.is_panic_destruct_fn(db)
105            }
106            ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
107                parent: _,
108                key: GeneratedFunctionKey::TraitFunc(function, _),
109            }) => Ok(function == &db.core_info().panic_destruct_fn),
110            _ => Ok(false),
111        }
112    }
113
114    /// Returns the generic version of the function if it exists, otherwise the function is a
115    /// specialized function and the `SpecializedFunction` struct is returned.
116    pub fn generic_or_specialized(&self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
117        self.long(db).clone().generic_or_specialized(db)
118    }
119}
120
121impl<'db> UnstableSalsaId for ConcreteFunctionWithBodyId<'db> {
122    fn get_internal_id(&self) -> salsa::Id {
123        self.as_intern_id()
124    }
125}
126impl<'db> ConcreteFunctionWithBodyLongId<'db> {
127    /// Returns the generic `FunctionWithLongId` if one exists, otherwise returns the specialized
128    /// function.
129    pub fn generic_or_specialized(self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
130        let long_id = match self {
131            ConcreteFunctionWithBodyLongId::Semantic(id) => {
132                FunctionWithBodyLongId::Semantic(id.function_with_body_id(db))
133            }
134            ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, key }) => {
135                FunctionWithBodyLongId::Generated { parent: parent.function_with_body_id(db), key }
136            }
137            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
138                return GenericOrSpecialized::Specialized(specialized);
139            }
140        };
141        GenericOrSpecialized::Generic(long_id.intern(db))
142    }
143    pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
144        match self {
145            ConcreteFunctionWithBodyLongId::Semantic(id) => id.substitution(db),
146            ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, .. }) => {
147                parent.substitution(db)
148            }
149            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
150                specialized.base.substitution(db)
151            }
152        }
153    }
154    pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
155        let long_id = match self {
156            ConcreteFunctionWithBodyLongId::Semantic(id) => {
157                FunctionLongId::Semantic(id.function_id(db)?)
158            }
159            ConcreteFunctionWithBodyLongId::Generated(generated) => {
160                FunctionLongId::Generated(*generated)
161            }
162            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
163                FunctionLongId::Specialized(specialized.clone())
164            }
165        };
166        Ok(long_id.intern(db))
167    }
168    pub fn base_semantic_function(
169        &self,
170        db: &'db dyn Database,
171    ) -> semantic::ConcreteFunctionWithBodyId<'db> {
172        match self {
173            ConcreteFunctionWithBodyLongId::Semantic(id) => *id,
174            ConcreteFunctionWithBodyLongId::Generated(generated) => generated.parent,
175            ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
176                specialized.base.base_semantic_function(db)
177            }
178        }
179    }
180    pub fn full_path(&self, db: &dyn Database) -> String {
181        match self {
182            ConcreteFunctionWithBodyLongId::Semantic(semantic) => semantic.full_path(db),
183            ConcreteFunctionWithBodyLongId::Generated(generated) => generated.full_path(db),
184            ConcreteFunctionWithBodyLongId::Specialized(specialized) => specialized.full_path(db),
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.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.base.stable_location(db)?
236            }
237        })
238    }
239}
240
241/// Function.
242#[derive(Clone, Debug, Hash, PartialEq, Eq)]
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(SpecializedFunction<'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.wrapper_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.clone()).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.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.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, salsa::Update)]
381pub enum GeneratedFunctionKey<'db> {
382    /// Generated loop functions are identified by the loop expr_id.
383    Loop(ExprPtr<'db>),
384    TraitFunc(TraitFunctionId<'db>, StableLocation<'db>),
385}
386
387/// Generated function.
388#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
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)]
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: Arc<[SpecializationArg<'db>]>,
452}
453
454impl<'db> SpecializedFunction<'db> {
455    pub fn body(&self, db: &'db dyn Database) -> ConcreteFunctionWithBodyId<'db> {
456        let long_id = ConcreteFunctionWithBodyLongId::Specialized(self.clone());
457        long_id.intern(db)
458    }
459    pub fn full_path(&self, db: &dyn Database) -> String {
460        format!("{:?}", self.debug(db))
461    }
462
463    pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
464        let mut base_sign = self.base.signature(db)?;
465
466        let mut params = vec![];
467        let mut stack = vec![];
468        for (param, arg) in zip_eq(base_sign.params.iter().rev(), self.args.iter().rev()) {
469            stack.push((param.clone(), arg));
470        }
471
472        while let Some((param, arg)) = stack.pop() {
473            match arg {
474                SpecializationArg::Const { .. } => {}
475                SpecializationArg::Snapshot(inner) => {
476                    let desnap_ty = *extract_matches!(param.ty.long(db), TypeLongId::Snapshot);
477                    stack.push((
478                        LoweredParam { ty: desnap_ty, stable_ptr: param.stable_ptr },
479                        inner.as_ref(),
480                    ));
481                }
482                SpecializationArg::Enum { variant, payload } => {
483                    let lowered_param =
484                        LoweredParam { ty: variant.ty, stable_ptr: param.stable_ptr };
485                    stack.push((lowered_param, payload.as_ref()));
486                }
487                SpecializationArg::Array(ty, values) => {
488                    for arg in values.iter().rev() {
489                        let lowered_param = LoweredParam { ty: *ty, stable_ptr: param.stable_ptr };
490                        stack.push((lowered_param, arg));
491                    }
492                }
493                SpecializationArg::Struct(specialization_args) => {
494                    let ty = param.ty;
495                    let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct)) = ty.long(db)
496                    else {
497                        unreachable!("Expected a concrete struct type");
498                    };
499                    let Ok(inner_param) = db.concrete_struct_members(*concrete_struct) else {
500                        continue;
501                    };
502                    for ((_, inner_param), arg) in
503                        zip_eq(inner_param.iter().rev(), specialization_args.iter().rev())
504                    {
505                        let lowered_param =
506                            LoweredParam { ty: inner_param.ty, stable_ptr: param.stable_ptr };
507                        stack.push((lowered_param, arg));
508                    }
509                }
510                SpecializationArg::NotSpecialized => {
511                    params.push(param.clone());
512                }
513            }
514        }
515
516        base_sign.params = params;
517
518        Ok(base_sign)
519    }
520}
521impl<'a> DebugWithDb<'a> for SpecializedFunction<'a> {
522    type Db = dyn Database;
523    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
524        write!(f, "{}{{", self.base.full_path(db))?;
525        for arg in self.args.iter() {
526            write!(f, "{:?}, ", arg.debug(db))?;
527        }
528        write!(f, "}}")
529    }
530}
531
532/// Signature for lowering a function.
533#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
534#[debug_db(dyn Database)]
535pub struct EnrichedSemanticSignature<'db> {
536    /// Input params.
537    pub params: Vec<semantic::ExprVarMemberPath<'db>>,
538    /// Extra returns - e.g. ref params
539    pub extra_rets: Vec<semantic::ExprVarMemberPath<'db>>,
540    /// Return type.
541    pub return_type: semantic::TypeId<'db>,
542    /// Explicit implicit requirements.
543    pub implicits: Vec<semantic::TypeId<'db>>,
544    /// Panicable.
545    #[dont_rewrite]
546    pub panicable: bool,
547    /// Location.
548    #[dont_rewrite]
549    #[hide_field_debug_with_db]
550    pub location: LocationId<'db>,
551}
552impl<'db> EnrichedSemanticSignature<'db> {
553    pub fn from_semantic(db: &'db dyn Database, value: &semantic::Signature<'db>) -> Self {
554        let semantic::Signature {
555            params,
556            return_type,
557            implicits,
558            panicable,
559            stable_ptr,
560            is_const: _,
561        } = value;
562        let ref_params = params
563            .iter()
564            .filter(|param| param.mutability == Mutability::Reference)
565            .map(|param| parameter_as_member_path(param.clone()))
566            .collect();
567        let params: Vec<semantic::ExprVarMemberPath<'_>> =
568            params.iter().cloned().map(parameter_as_member_path).collect();
569        Self {
570            params,
571            extra_rets: ref_params,
572            return_type: *return_type,
573            implicits: implicits.clone(),
574            panicable: *panicable,
575            location: LocationId::from_stable_location(
576                db,
577                StableLocation::new(stable_ptr.untyped()),
578            ),
579        }
580    }
581    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
582        self.params.iter().all(|param| param.ty().is_fully_concrete(db))
583            && self.extra_rets.iter().all(|param| param.ty().is_fully_concrete(db))
584            && self.return_type.is_fully_concrete(db)
585            && self.implicits.iter().all(|ty| ty.is_fully_concrete(db))
586    }
587}
588semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, EnrichedSemanticSignature<'a>);
589
590#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
591#[debug_db(dyn Database)]
592/// Represents a parameter of a lowered function.
593pub struct LoweredParam<'db> {
594    pub ty: semantic::TypeId<'db>,
595    #[dont_rewrite]
596    pub stable_ptr: ast::ExprPtr<'db>,
597}
598semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, LoweredParam<'a>);
599
600/// Lowered signature of a function.
601#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
602#[debug_db(dyn Database)]
603pub struct Signature<'db> {
604    /// Input params.
605    pub params: Vec<LoweredParam<'db>>, // Vec<semantic::ExprVarMemberPath<'db>>,
606    /// Extra returns - e.g. ref params
607    pub extra_rets: Vec<semantic::ExprVarMemberPath<'db>>,
608    /// Return type.
609    pub return_type: semantic::TypeId<'db>,
610    /// Explicit implicit requirements.
611    pub implicits: Vec<semantic::TypeId<'db>>,
612    /// Panicable.
613    #[dont_rewrite]
614    pub panicable: bool,
615    /// Location.
616    #[dont_rewrite]
617    #[hide_field_debug_with_db]
618    pub location: LocationId<'db>,
619}
620semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, Signature<'a>);
621
622impl<'db> From<EnrichedSemanticSignature<'db>> for Signature<'db> {
623    fn from(signature: EnrichedSemanticSignature<'db>) -> Self {
624        Signature {
625            params: signature
626                .params
627                .iter()
628                .map(|param| LoweredParam { ty: param.ty(), stable_ptr: param.stable_ptr() })
629                .collect(),
630            extra_rets: signature.extra_rets,
631            return_type: signature.return_type,
632            implicits: signature.implicits,
633            panicable: signature.panicable,
634            location: signature.location,
635        }
636    }
637}
638
639/// Converts a [semantic::Parameter] to a [semantic::ExprVarMemberPath].
640pub(crate) fn parameter_as_member_path<'db>(
641    param: semantic::Parameter<'db>,
642) -> semantic::ExprVarMemberPath<'db> {
643    let semantic::Parameter { id, ty, stable_ptr, .. } = param;
644    semantic::ExprVarMemberPath::Var(ExprVar {
645        var: semantic::VarId::Param(id),
646        ty,
647        stable_ptr: ast::ExprPtr(stable_ptr.0),
648    })
649}
650
651define_short_id!(LocationId, Location<'db>);
652impl<'db> LocationId<'db> {
653    pub fn from_stable_location(
654        db: &'db dyn Database,
655        stable_location: StableLocation<'db>,
656    ) -> LocationId<'db> {
657        Location::new(stable_location).intern(db)
658    }
659
660    /// Adds a note to the location.
661    pub fn with_note(&self, db: &'db dyn Database, note: DiagnosticNote<'db>) -> LocationId<'db> {
662        self.long(db).clone().with_note(note).intern(db)
663    }
664
665    /// Adds a note that this location was generated while compiling an auto-generated function.
666    pub fn with_auto_generation_note(
667        &self,
668        db: &'db dyn Database,
669        logic_name: &str,
670    ) -> LocationId<'db> {
671        self.with_note(
672            db,
673            DiagnosticNote::text_only(format!(
674                "this error originates in auto-generated {logic_name} logic."
675            )),
676        )
677    }
678}