Skip to main content

cairo_lang_lowering/
ids.rs

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