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