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::{GenericArgumentId, TypeLongId};
13use cairo_lang_syntax::node::ast::ExprPtr;
14use cairo_lang_syntax::node::kind::SyntaxKind;
15use cairo_lang_syntax::node::{TypedStablePtr, ast};
16use cairo_lang_utils::{Intern, define_short_id, try_extract_matches};
17use defs::diagnostic_utils::StableLocation;
18use defs::ids::{ExternFunctionId, FreeFunctionId};
19use itertools::zip_eq;
20use salsa::Database;
21use semantic::items::functions::GenericFunctionId;
22use semantic::substitution::{GenericSubstitution, SubstitutionRewriter};
23use semantic::{ExprVar, Mutability};
24use {cairo_lang_defs as defs, cairo_lang_semantic as semantic};
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)]
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)]
86pub enum ConcreteFunctionWithBodyLongId<'db> {
87    Semantic(semantic::ConcreteFunctionWithBodyId<'db>),
88    Generated(GeneratedFunction<'db>),
89    Specialized(SpecializedFunction<'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(SpecializedFunction<'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.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.clone())
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.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) => specialized.full_path(db),
184        }
185    }
186}
187impl<'db> ConcreteFunctionWithBodyId<'db> {
188    pub fn from_semantic(
189        db: &'db dyn Database,
190        semantic: semantic::ConcreteFunctionWithBodyId<'db>,
191    ) -> Self {
192        ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db)
193    }
194    pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
195        self.long(db).substitution(db)
196    }
197    pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
198        self.long(db).function_id(db)
199    }
200    pub fn full_path(&self, db: &dyn Database) -> String {
201        self.long(db).full_path(db)
202    }
203    pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
204        match self.generic_or_specialized(db) {
205            GenericOrSpecialized::Generic(id) => {
206                let generic_signature = id.signature(db)?;
207                self.substitution(db)?.substitute(db, generic_signature)
208            }
209            GenericOrSpecialized::Specialized(specialized) => specialized.signature(db),
210        }
211    }
212    pub fn from_no_generics_free(
213        db: &'db dyn Database,
214        free_function_id: FreeFunctionId<'db>,
215    ) -> Option<Self> {
216        let semantic =
217            semantic::ConcreteFunctionWithBodyId::from_no_generics_free(db, free_function_id)?;
218        Some(ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db))
219    }
220    pub fn base_semantic_function(
221        &self,
222        db: &'db dyn Database,
223    ) -> semantic::ConcreteFunctionWithBodyId<'db> {
224        self.long(db).base_semantic_function(db)
225    }
226    pub fn stable_location(&self, db: &'db dyn Database) -> Maybe<StableLocation<'db>> {
227        Ok(match self.long(db) {
228            ConcreteFunctionWithBodyLongId::Semantic(id) => id.stable_location(db),
229            ConcreteFunctionWithBodyLongId::Generated(generated) => match generated.key {
230                GeneratedFunctionKey::Loop(stable_ptr) => StableLocation::new(stable_ptr.untyped()),
231                GeneratedFunctionKey::TraitFunc(_, stable_location) => stable_location,
232            },
233            ConcreteFunctionWithBodyLongId::Specialized(specialized_function) => {
234                specialized_function.base.stable_location(db)?
235            }
236        })
237    }
238}
239
240/// Function.
241#[derive(Clone, Debug, Hash, PartialEq, Eq)]
242pub enum FunctionLongId<'db> {
243    /// An original function from the user code.
244    Semantic(semantic::FunctionId<'db>),
245    /// A function generated by the compiler.
246    Generated(GeneratedFunction<'db>),
247    /// A specialized function.
248    Specialized(SpecializedFunction<'db>),
249}
250define_short_id!(FunctionId, FunctionLongId<'db>);
251impl<'db> FunctionLongId<'db> {
252    pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
253        Ok(Some(match self {
254            FunctionLongId::Semantic(id) => {
255                let concrete_function = id.get_concrete(db);
256                if let GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function }) =
257                    concrete_function.generic_function
258                    && let ImplLongId::GeneratedImpl(imp) = impl_id.long(db)
259                {
260                    let concrete_trait = imp.concrete_trait(db);
261                    let info = db.core_info();
262                    assert!(
263                        [info.destruct_fn, info.panic_destruct_fn, info.call_fn, info.call_once_fn]
264                            .contains(&function)
265                    );
266
267                    let generic_args = concrete_trait.generic_args(db);
268                    let Some(GenericArgumentId::Type(ty)) = generic_args.first() else {
269                        unreachable!("Expected Generated Impl to have a type argument");
270                    };
271                    let TypeLongId::Closure(ty) = ty.long(db) else {
272                        unreachable!("Expected Generated Impl to have a closure type argument");
273                    };
274
275                    let Some(parent) = ty.parent_function?.get_concrete(db).body(db)? else {
276                        return Ok(None);
277                    };
278                    return Ok(Some(
279                        GeneratedFunction {
280                            parent,
281                            key: GeneratedFunctionKey::TraitFunc(function, ty.wrapper_location),
282                        }
283                        .body(db),
284                    ));
285                }
286
287                let Some(body) = concrete_function.body(db)? else {
288                    return Ok(None);
289                };
290                ConcreteFunctionWithBodyLongId::Semantic(body).intern(db)
291            }
292            FunctionLongId::Generated(generated) => generated.body(db),
293            FunctionLongId::Specialized(specialized) => {
294                ConcreteFunctionWithBodyLongId::Specialized(specialized.clone()).intern(db)
295            }
296        }))
297    }
298    pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
299        match self {
300            FunctionLongId::Semantic(semantic) => {
301                Ok(Signature::from_semantic(db, db.concrete_function_signature(*semantic)?))
302            }
303            FunctionLongId::Generated(generated) => generated.body(db).signature(db),
304            FunctionLongId::Specialized(specialized) => specialized.signature(db),
305        }
306    }
307    pub fn full_path(&self, db: &dyn Database) -> String {
308        format!("{:?}", self.debug(db))
309    }
310    /// Returns the full path of the relevant semantic function:
311    /// - If the function itself is semantic (non generated), its own full path.
312    /// - If the function is generated, then its (semantic) parent's full path.
313    pub fn semantic_full_path(&self, db: &dyn Database) -> String {
314        match self {
315            FunctionLongId::Semantic(id) => id.full_path(db),
316            FunctionLongId::Generated(generated) => generated.parent.full_path(db),
317            FunctionLongId::Specialized(specialized) => specialized.full_path(db),
318        }
319    }
320}
321impl<'db> FunctionId<'db> {
322    pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
323        self.long(db).body(db)
324    }
325    pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
326        self.long(db).signature(db)
327    }
328    pub fn full_path(&self, db: &dyn Database) -> String {
329        self.long(db).full_path(db)
330    }
331    pub fn semantic_full_path(&self, db: &dyn Database) -> String {
332        self.long(db).semantic_full_path(db)
333    }
334    /// Returns the function as an `ExternFunctionId` and its generic arguments, if it is an
335    /// `extern` functions.
336    pub fn get_extern(
337        &self,
338        db: &'db dyn Database,
339    ) -> Option<(ExternFunctionId<'db>, Vec<GenericArgumentId<'db>>)> {
340        let semantic = try_extract_matches!(self.long(db), FunctionLongId::Semantic)?;
341        let concrete = semantic.get_concrete(db);
342        Some((
343            try_extract_matches!(concrete.generic_function, GenericFunctionId::Extern)?,
344            concrete.generic_args,
345        ))
346    }
347}
348pub trait SemanticFunctionIdEx<'db> {
349    fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db>;
350}
351impl<'db> SemanticFunctionIdEx<'db> for semantic::FunctionId<'db> {
352    fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db> {
353        let ret = FunctionLongId::Semantic(*self).intern(db);
354        // If the function is generated, we need to check if it has a body, so we can return its
355        // generated function id.
356        // TODO(orizi): This is a hack, we should have a better way to do this.
357        if let Ok(Some(body)) = ret.body(db)
358            && let Ok(id) = body.function_id(db)
359        {
360            return id;
361        }
362        ret
363    }
364}
365impl<'a> DebugWithDb<'a> for FunctionLongId<'a> {
366    type Db = dyn Database;
367    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
368        match self {
369            FunctionLongId::Semantic(semantic) => write!(f, "{:?}", semantic.debug(db)),
370            FunctionLongId::Generated(generated) => write!(f, "{:?}", generated.debug(db)),
371            FunctionLongId::Specialized(specialized) => write!(f, "{:?}", specialized.debug(db)),
372        }
373    }
374}
375
376/// A key for a generated functions.
377#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
378pub enum GeneratedFunctionKey<'db> {
379    /// Generated loop functions are identified by the loop expr_id.
380    Loop(ExprPtr<'db>),
381    TraitFunc(TraitFunctionId<'db>, StableLocation<'db>),
382}
383
384/// Generated function.
385#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
386pub struct GeneratedFunction<'db> {
387    pub parent: semantic::ConcreteFunctionWithBodyId<'db>,
388    pub key: GeneratedFunctionKey<'db>,
389}
390impl<'db> GeneratedFunction<'db> {
391    pub fn body(&self, db: &'db dyn Database) -> ConcreteFunctionWithBodyId<'db> {
392        let long_id = ConcreteFunctionWithBodyLongId::Generated(*self);
393        long_id.intern(db)
394    }
395    pub fn full_path(&self, db: &dyn Database) -> String {
396        format!("{:?}", self.debug(db))
397    }
398}
399impl<'a> DebugWithDb<'a> for GeneratedFunction<'a> {
400    type Db = dyn Database;
401    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
402        match self.key {
403            GeneratedFunctionKey::Loop(expr_ptr) => {
404                let mut func_ptr = expr_ptr.untyped();
405                while !matches!(
406                    func_ptr.kind(db),
407                    SyntaxKind::FunctionWithBody | SyntaxKind::TraitItemFunction
408                ) {
409                    func_ptr = func_ptr.parent(db)
410                }
411
412                let span = expr_ptr.0.lookup(db).span(db);
413                let function_start = func_ptr.lookup(db).span(db).start.as_u32();
414                write!(
415                    f,
416                    "{:?}[{}-{}]",
417                    self.parent.debug(db),
418                    span.start.as_u32() - function_start,
419                    span.end.as_u32() - function_start
420                )
421            }
422            GeneratedFunctionKey::TraitFunc(trait_func, loc) => {
423                let trait_id = trait_func.trait_id(db);
424                write!(
425                    f,
426                    "Generated `{}::{}` for {{closure@{:?}}}",
427                    trait_id.full_path(db),
428                    trait_func.name(db).long(db),
429                    loc.debug(db),
430                )
431            }
432        }
433    }
434}
435
436/// Specialized function
437///
438/// Specialized functions are generated by the compiler some of the function
439/// arguments are known at compile time and the resulting specialized function is smaller
440/// than the original one.
441///
442/// Specialized functions are identified by the base function and the arguments.
443#[derive(Clone, Debug, Hash, PartialEq, Eq)]
444pub struct SpecializedFunction<'db> {
445    /// The base function.
446    pub base: crate::ids::ConcreteFunctionWithBodyId<'db>,
447    /// Optional const assignments for the arguments.
448    pub args: Arc<[Option<SpecializationArg<'db>>]>,
449}
450
451impl<'db> SpecializedFunction<'db> {
452    pub fn body(&self, db: &'db dyn Database) -> ConcreteFunctionWithBodyId<'db> {
453        let long_id = ConcreteFunctionWithBodyLongId::Specialized(self.clone());
454        long_id.intern(db)
455    }
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        base_sign.params = zip_eq(base_sign.params, self.args.iter())
464            .filter_map(|(param, arg)| arg.is_none().then_some(param))
465            .collect::<Vec<_>>();
466
467        Ok(base_sign)
468    }
469}
470impl<'a> DebugWithDb<'a> for SpecializedFunction<'a> {
471    type Db = dyn Database;
472    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
473        write!(f, "{}{{", self.base.full_path(db))?;
474        for arg in self.args.iter() {
475            match arg {
476                Some(value) => write!(f, "{:?}, ", value.debug(db))?,
477                None => write!(f, "None, ")?,
478            }
479        }
480        write!(f, "}}")
481    }
482}
483
484/// Lowered signature of a function.
485#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
486#[debug_db(dyn Database)]
487pub struct Signature<'db> {
488    /// Input params.
489    pub params: Vec<semantic::ExprVarMemberPath<'db>>,
490    /// Extra returns - e.g. ref params
491    pub extra_rets: Vec<semantic::ExprVarMemberPath<'db>>,
492    /// Return type.
493    pub return_type: semantic::TypeId<'db>,
494    /// Explicit implicit requirements.
495    pub implicits: Vec<semantic::TypeId<'db>>,
496    /// Panicable.
497    #[dont_rewrite]
498    pub panicable: bool,
499    /// Location.
500    #[dont_rewrite]
501    #[hide_field_debug_with_db]
502    pub location: LocationId<'db>,
503}
504impl<'db> Signature<'db> {
505    pub fn from_semantic(db: &'db dyn Database, value: &semantic::Signature<'db>) -> Self {
506        let semantic::Signature {
507            params,
508            return_type,
509            implicits,
510            panicable,
511            stable_ptr,
512            is_const: _,
513        } = value;
514        let ref_params = params
515            .iter()
516            .filter(|param| param.mutability == Mutability::Reference)
517            .map(|param| parameter_as_member_path(param.clone()))
518            .collect();
519        let params: Vec<semantic::ExprVarMemberPath<'_>> =
520            params.iter().cloned().map(parameter_as_member_path).collect();
521        Self {
522            params,
523            extra_rets: ref_params,
524            return_type: *return_type,
525            implicits: implicits.clone(),
526            panicable: *panicable,
527            location: LocationId::from_stable_location(
528                db,
529                StableLocation::new(stable_ptr.untyped()),
530            ),
531        }
532    }
533    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
534        self.params.iter().all(|param| param.ty().is_fully_concrete(db))
535            && self.extra_rets.iter().all(|param| param.ty().is_fully_concrete(db))
536            && self.return_type.is_fully_concrete(db)
537            && self.implicits.iter().all(|ty| ty.is_fully_concrete(db))
538    }
539}
540semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, Signature<'a>);
541
542/// Converts a [semantic::Parameter] to a [semantic::ExprVarMemberPath].
543pub(crate) fn parameter_as_member_path<'db>(
544    param: semantic::Parameter<'db>,
545) -> semantic::ExprVarMemberPath<'db> {
546    let semantic::Parameter { id, ty, stable_ptr, .. } = param;
547    semantic::ExprVarMemberPath::Var(ExprVar {
548        var: semantic::VarId::Param(id),
549        ty,
550        stable_ptr: ast::ExprPtr(stable_ptr.0),
551    })
552}
553
554define_short_id!(LocationId, Location<'db>);
555impl<'db> LocationId<'db> {
556    pub fn from_stable_location(
557        db: &'db dyn Database,
558        stable_location: StableLocation<'db>,
559    ) -> LocationId<'db> {
560        Location::new(stable_location).intern(db)
561    }
562
563    /// Adds a note to the location.
564    pub fn with_note(&self, db: &'db dyn Database, note: DiagnosticNote<'db>) -> LocationId<'db> {
565        self.long(db).clone().with_note(note).intern(db)
566    }
567
568    /// Adds a note that this location was generated while compiling an auto-generated function.
569    pub fn with_auto_generation_note(
570        &self,
571        db: &'db dyn Database,
572        logic_name: &str,
573    ) -> LocationId<'db> {
574        self.with_note(
575            db,
576            DiagnosticNote::text_only(format!(
577                "this error originates in auto-generated {logic_name} logic."
578            )),
579        )
580    }
581}