cairo_lang_lowering/cache/
mod.rs

1#[cfg(test)]
2#[path = "test.rs"]
3mod test;
4
5use std::ops::{Deref, DerefMut};
6use std::sync::Arc;
7
8use bincode::error::EncodeError;
9use cairo_lang_defs as defs;
10use cairo_lang_defs::cache::{
11    CachedCrateMetadata, DefCacheLoadingData, DefCacheSavingContext, LanguageElementCached,
12    SyntaxStablePtrIdCached,
13};
14use cairo_lang_defs::db::DefsGroup;
15use cairo_lang_defs::diagnostic_utils::StableLocation;
16use cairo_lang_defs::ids::{
17    FreeFunctionLongId, FunctionWithBodyId, ImplFunctionLongId, TraitFunctionLongId,
18};
19use cairo_lang_diagnostics::{DiagnosticAdded, Maybe, skip_diagnostic};
20use cairo_lang_filesystem::db::FilesGroup;
21use cairo_lang_filesystem::ids::CrateId;
22use cairo_lang_semantic::cache::{
23    ConcreteEnumCached, ConcreteVariantCached, ConstValueIdCached, ExprVarMemberPathCached,
24    ImplIdCached, MatchArmSelectorCached, SemanticCacheLoadingData, SemanticCacheSavingContext,
25    SemanticCacheSavingData, SemanticConcreteFunctionWithBodyCached, SemanticFunctionIdCached,
26    TypeIdCached, generate_crate_def_cache, generate_crate_semantic_cache,
27};
28use cairo_lang_semantic::db::SemanticGroup;
29use cairo_lang_semantic::expr::inference::InferenceError;
30use cairo_lang_semantic::items::function_with_body::FunctionWithBodySemantic;
31use cairo_lang_semantic::items::imp::ImplSemantic;
32use cairo_lang_semantic::items::macro_call::module_macro_modules;
33use cairo_lang_semantic::items::trt::TraitSemantic;
34use cairo_lang_semantic::types::TypeInfo;
35use cairo_lang_syntax::node::TypedStablePtr;
36use cairo_lang_syntax::node::ast::{ExprPtr, FunctionWithBodyPtr, TraitItemFunctionPtr};
37use cairo_lang_utils::Intern;
38use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
39use id_arena::Arena;
40use itertools::chain;
41use salsa::Database;
42use serde::{Deserialize, Serialize};
43use thiserror::Error;
44
45use crate::blocks::BlocksBuilder;
46use crate::ids::{
47    FunctionId, FunctionLongId, GeneratedFunction, GeneratedFunctionKey, LocationId, LoweredParam,
48    Signature,
49};
50use crate::lower::{MultiLowering, lower_semantic_function};
51use crate::objects::{
52    BlockId, MatchExternInfo, Statement, StatementCall, StatementConst, StatementStructDestructure,
53    VariableId,
54};
55use crate::{
56    Block, BlockEnd, Location, Lowered, MatchArm, MatchEnumInfo, MatchEnumValue, MatchInfo,
57    StatementDesnap, StatementEnumConstruct, StatementSnapshot, StatementStructConstruct,
58    VarRemapping, VarUsage, Variable,
59};
60
61type LookupCache = (CacheLookups, Vec<(DefsFunctionWithBodyIdCached, MultiLoweringCached)>);
62
63/// Load the cached lowering of a crate if it has a cache file configuration.
64pub fn load_cached_crate_functions<'db>(
65    db: &'db dyn Database,
66    crate_id: CrateId<'db>,
67) -> Option<OrderedHashMap<defs::ids::FunctionWithBodyId<'db>, MultiLowering<'db>>> {
68    let blob_id = db.crate_config(crate_id)?.cache_file?;
69    let Some(content) = db.blob_content(blob_id) else {
70        return Default::default();
71    };
72
73    let semantic_loading_data = db.cached_crate_semantic_data(crate_id)?.loading_data;
74
75    let def_size = usize::from_be_bytes(content[..8].try_into().unwrap()); // def_size is the first 8 bytes of the blob.
76    let semantic_start = 8 + def_size;
77    let semantic_size =
78        usize::from_be_bytes(content[semantic_start..semantic_start + 8].try_into().unwrap()); // semantic_size is the next 8 bytes.
79
80    let lowering_start = semantic_start + semantic_size + 8;
81    let lowering_size =
82        usize::from_be_bytes(content[lowering_start..lowering_start + 8].try_into().unwrap()); // lowering_size is the next 8 bytes.
83
84    let content = &content[lowering_start + 8..lowering_start + 8 + lowering_size];
85
86    let ((lookups, lowerings), _): (LookupCache, _) =
87        bincode::serde::decode_from_slice(content, bincode::config::standard()).unwrap_or_else(
88            |e| {
89                panic!(
90                    "failed to deserialize lookup cache for crate `{}`: {e}",
91                    crate_id.long(db).name().long(db),
92                )
93            },
94        );
95
96    // TODO(tomer): Fail on version, cfg, and dependencies mismatch.
97
98    let mut ctx = CacheLoadingContext::new(db, lookups, semantic_loading_data);
99
100    Some(
101        lowerings
102            .into_iter()
103            .map(|(function_id, lowering)| {
104                let function_id =
105                    function_id.embed(&ctx.semantic_loading_data.defs_loading_data, ctx.db);
106
107                let lowering = lowering.embed(&mut ctx);
108                (function_id, lowering)
109            })
110            .collect::<OrderedHashMap<_, _>>(),
111    )
112}
113
114#[derive(Debug, Error)]
115pub enum CrateCacheError {
116    #[error("Failed compilation of crate.")]
117    CompilationFailed,
118    #[error("Cache encoding failed: {0}")]
119    EncodingError(#[from] EncodeError),
120}
121
122impl From<DiagnosticAdded> for CrateCacheError {
123    fn from(_e: DiagnosticAdded) -> Self {
124        Self::CompilationFailed
125    }
126}
127
128/// Cache the lowering of each function in the crate into a blob.
129pub fn generate_crate_cache<'db>(
130    db: &'db dyn Database,
131    crate_id: CrateId<'db>,
132) -> Result<Vec<u8>, CrateCacheError> {
133    let modules = db.crate_modules(crate_id);
134    let mut function_ids = Vec::new();
135    for module_id in modules.iter() {
136        for module_id in chain!([module_id], module_macro_modules(db, true, *module_id)) {
137            for free_func in db.module_free_functions_ids(*module_id)?.iter() {
138                function_ids.push(FunctionWithBodyId::Free(*free_func));
139            }
140            for impl_id in db.module_impls_ids(*module_id)?.iter() {
141                for impl_func in db.impl_functions(*impl_id)?.values() {
142                    function_ids.push(FunctionWithBodyId::Impl(*impl_func));
143                }
144            }
145            for trait_id in db.module_traits_ids(*module_id)?.iter() {
146                for trait_func in db.trait_functions(*trait_id)?.values() {
147                    function_ids.push(FunctionWithBodyId::Trait(*trait_func));
148                }
149            }
150        }
151    }
152
153    let mut ctx = CacheSavingContext::new(db, crate_id);
154
155    let def_cache = generate_crate_def_cache(db, crate_id, &mut ctx.semantic_ctx.defs_ctx)?;
156    let semantic_cache = generate_crate_semantic_cache(crate_id, &mut ctx.semantic_ctx)?;
157
158    let cached = function_ids
159        .iter()
160        .filter_map(|id| {
161            db.function_body(*id).ok()?;
162            let multi = match lower_semantic_function(db, *id) {
163                Ok(multi) => multi,
164                Err(err) => return Some(Err(err)),
165            };
166
167            Some(Ok((
168                DefsFunctionWithBodyIdCached::new(*id, &mut ctx.semantic_ctx),
169                MultiLoweringCached::new(multi, &mut ctx),
170            )))
171        })
172        .collect::<Maybe<Vec<_>>>()?;
173
174    let mut artifact = Vec::<u8>::new();
175
176    let def = bincode::serde::encode_to_vec(
177        &(CachedCrateMetadata::new(crate_id, db), def_cache, &ctx.semantic_ctx.defs_ctx.lookups),
178        bincode::config::standard(),
179    )?;
180    artifact.extend(def.len().to_be_bytes());
181    artifact.extend(def);
182
183    let semantic = bincode::serde::encode_to_vec(
184        &(semantic_cache, &ctx.semantic_ctx.lookups),
185        bincode::config::standard(),
186    )?;
187    artifact.extend(semantic.len().to_be_bytes());
188    artifact.extend(semantic);
189
190    let lowered =
191        bincode::serde::encode_to_vec(&(&ctx.lookups, cached), bincode::config::standard())?;
192    artifact.extend(lowered.len().to_be_bytes());
193    artifact.extend(lowered);
194
195    Ok(artifact)
196}
197
198/// Context for loading cache into the database.
199struct CacheLoadingContext<'db> {
200    /// The variable ids of the flat lowered that is currently being loaded.
201    lowered_variables_id: Vec<VariableId>,
202    db: &'db dyn Database,
203
204    /// data for loading the entire cache into the database.
205    data: CacheLoadingData<'db>,
206
207    semantic_loading_data: Arc<SemanticCacheLoadingData<'db>>,
208}
209
210impl<'db> CacheLoadingContext<'db> {
211    fn new(
212        db: &'db dyn Database,
213        lookups: CacheLookups,
214        semantic_loading_data: Arc<SemanticCacheLoadingData<'db>>,
215    ) -> Self {
216        Self {
217            lowered_variables_id: Vec::new(),
218            db,
219            data: CacheLoadingData {
220                function_ids: OrderedHashMap::default(),
221                location_ids: OrderedHashMap::default(),
222                lookups,
223            },
224            semantic_loading_data,
225        }
226    }
227}
228
229impl<'db> Deref for CacheLoadingContext<'db> {
230    type Target = CacheLoadingData<'db>;
231
232    fn deref(&self) -> &Self::Target {
233        &self.data
234    }
235}
236impl<'db> DerefMut for CacheLoadingContext<'db> {
237    fn deref_mut(&mut self) -> &mut Self::Target {
238        &mut self.data
239    }
240}
241
242/// Data for loading cache into the database.
243struct CacheLoadingData<'db> {
244    function_ids: OrderedHashMap<FunctionIdCached, FunctionId<'db>>,
245    location_ids: OrderedHashMap<LocationIdCached, LocationId<'db>>,
246    lookups: CacheLookups,
247}
248impl Deref for CacheLoadingData<'_> {
249    type Target = CacheLookups;
250
251    fn deref(&self) -> &Self::Target {
252        &self.lookups
253    }
254}
255impl DerefMut for CacheLoadingData<'_> {
256    fn deref_mut(&mut self) -> &mut Self::Target {
257        &mut self.lookups
258    }
259}
260
261/// Context for saving cache from the database.
262struct CacheSavingContext<'db> {
263    db: &'db dyn Database,
264    data: CacheSavingData<'db>,
265    semantic_ctx: SemanticCacheSavingContext<'db>,
266}
267impl<'db> Deref for CacheSavingContext<'db> {
268    type Target = CacheSavingData<'db>;
269
270    fn deref(&self) -> &Self::Target {
271        &self.data
272    }
273}
274impl DerefMut for CacheSavingContext<'_> {
275    fn deref_mut(&mut self) -> &mut Self::Target {
276        &mut self.data
277    }
278}
279impl<'db> CacheSavingContext<'db> {
280    fn new(db: &'db dyn Database, self_crate_id: CrateId<'db>) -> Self {
281        Self {
282            db,
283            data: CacheSavingData::default(),
284            semantic_ctx: SemanticCacheSavingContext {
285                db,
286                data: SemanticCacheSavingData::default(),
287                defs_ctx: DefCacheSavingContext::new(db, self_crate_id),
288            },
289        }
290    }
291}
292
293/// Data for saving cache from the database.
294#[derive(Default)]
295struct CacheSavingData<'db> {
296    function_ids: OrderedHashMap<FunctionId<'db>, FunctionIdCached>,
297    location_ids: OrderedHashMap<LocationId<'db>, LocationIdCached>,
298    lookups: CacheLookups,
299}
300impl<'db> Deref for CacheSavingData<'db> {
301    type Target = CacheLookups;
302
303    fn deref(&self) -> &Self::Target {
304        &self.lookups
305    }
306}
307impl<'db> DerefMut for CacheSavingData<'db> {
308    fn deref_mut(&mut self) -> &mut Self::Target {
309        &mut self.lookups
310    }
311}
312
313/// Saved interned items for the cache.
314#[derive(Serialize, Deserialize, Default)]
315struct CacheLookups {
316    function_ids_lookup: Vec<FunctionCached>,
317    location_ids_lookup: Vec<LocationCached>,
318}
319
320/// Cached version of [defs::ids::FunctionWithBodyId]
321/// used as a key in the cache for the [MultiLowering] struct.
322#[derive(Serialize, Deserialize, Hash, Eq, PartialEq)]
323enum DefsFunctionWithBodyIdCached {
324    Free(LanguageElementCached),
325    Impl(LanguageElementCached),
326    Trait(LanguageElementCached),
327}
328impl DefsFunctionWithBodyIdCached {
329    fn new<'db>(
330        id: defs::ids::FunctionWithBodyId<'db>,
331        ctx: &mut SemanticCacheSavingContext<'db>,
332    ) -> Self {
333        match id {
334            defs::ids::FunctionWithBodyId::Free(id) => DefsFunctionWithBodyIdCached::Free(
335                LanguageElementCached::new(id, &mut ctx.defs_ctx),
336            ),
337            defs::ids::FunctionWithBodyId::Impl(id) => DefsFunctionWithBodyIdCached::Impl(
338                LanguageElementCached::new(id, &mut ctx.defs_ctx),
339            ),
340            defs::ids::FunctionWithBodyId::Trait(id) => DefsFunctionWithBodyIdCached::Trait(
341                LanguageElementCached::new(id, &mut ctx.defs_ctx),
342            ),
343        }
344    }
345
346    fn embed<'db>(
347        self,
348        defs_loading_data: &Arc<DefCacheLoadingData<'db>>,
349        db: &'db dyn Database,
350    ) -> defs::ids::FunctionWithBodyId<'db> {
351        match self {
352            DefsFunctionWithBodyIdCached::Free(id) => {
353                let (module_id, function_stable_ptr) = id.get_embedded(defs_loading_data);
354                defs::ids::FunctionWithBodyId::Free(
355                    FreeFunctionLongId(module_id, FunctionWithBodyPtr(function_stable_ptr))
356                        .intern(db),
357                )
358            }
359            DefsFunctionWithBodyIdCached::Impl(id) => {
360                let (module_id, function_stable_ptr) = id.get_embedded(defs_loading_data);
361                defs::ids::FunctionWithBodyId::Impl(
362                    ImplFunctionLongId(module_id, FunctionWithBodyPtr(function_stable_ptr))
363                        .intern(db),
364                )
365            }
366            DefsFunctionWithBodyIdCached::Trait(id) => {
367                let (module_id, function_stable_ptr) = id.get_embedded(defs_loading_data);
368                defs::ids::FunctionWithBodyId::Trait(
369                    TraitFunctionLongId(module_id, TraitItemFunctionPtr(function_stable_ptr))
370                        .intern(db),
371                )
372            }
373        }
374    }
375}
376
377/// Cached version of [MultiLowering].
378#[derive(Serialize, Deserialize)]
379struct MultiLoweringCached {
380    main_lowering: LoweredCached,
381    generated_lowerings: Vec<(GeneratedFunctionKeyCached, LoweredCached)>,
382}
383impl MultiLoweringCached {
384    fn new<'db>(lowering: MultiLowering<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
385        Self {
386            main_lowering: LoweredCached::new(lowering.main_lowering, ctx),
387            generated_lowerings: lowering
388                .generated_lowerings
389                .into_iter()
390                .map(|(key, lowered)| {
391                    (GeneratedFunctionKeyCached::new(key, ctx), LoweredCached::new(lowered, ctx))
392                })
393                .collect(),
394        }
395    }
396    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MultiLowering<'db> {
397        MultiLowering {
398            main_lowering: self.main_lowering.embed(ctx),
399            generated_lowerings: self
400                .generated_lowerings
401                .into_iter()
402                .map(|(key, lowered)| (key.embed(ctx), lowered.embed(ctx)))
403                .collect(),
404        }
405    }
406}
407
408#[derive(Serialize, Deserialize)]
409struct LoweredCached {
410    /// Function signature.
411    signature: LoweredSignatureCached,
412    /// Arena of allocated lowered variables.
413    variables: Vec<VariableCached>,
414    /// Arena of allocated lowered blocks.
415    blocks: Vec<BlockCached>,
416    /// function parameters, including implicits.
417    parameters: Vec<usize>,
418}
419impl LoweredCached {
420    fn new<'db>(lowered: Lowered<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
421        Self {
422            signature: LoweredSignatureCached::new(lowered.signature, ctx),
423            variables: lowered
424                .variables
425                .into_iter()
426                .map(|var| VariableCached::new(var.1, ctx))
427                .collect(),
428            blocks: lowered
429                .blocks
430                .iter()
431                .map(|block: (BlockId, &Block<'_>)| BlockCached::new(block.1.clone(), ctx))
432                .collect(),
433            parameters: lowered.parameters.iter().map(|var| var.index()).collect(),
434        }
435    }
436    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Lowered<'db> {
437        ctx.lowered_variables_id.clear();
438        let mut variables = Arena::new();
439        for var in self.variables {
440            let id = variables.alloc(var.embed(ctx));
441            ctx.lowered_variables_id.push(id);
442        }
443
444        let mut blocks = BlocksBuilder::new();
445        for block in self.blocks {
446            blocks.alloc(block.embed(ctx));
447        }
448        Lowered {
449            diagnostics: Default::default(),
450            signature: self.signature.embed(ctx),
451            variables,
452            blocks: blocks.build().unwrap(),
453            parameters: self
454                .parameters
455                .into_iter()
456                .map(|var_id| ctx.lowered_variables_id[var_id])
457                .collect(),
458        }
459    }
460}
461
462#[derive(Serialize, Deserialize)]
463struct LoweredSignatureCached {
464    /// Function parameters.
465    params: Vec<LoweredParamCached>,
466    /// Extra return values.
467    extra_rets: Vec<ExprVarMemberPathCached>,
468    /// Return type.
469    return_type: TypeIdCached,
470    /// Implicit parameters.
471    implicits: Vec<TypeIdCached>,
472    /// Whether the function is panicable.
473    panicable: bool,
474    location: LocationIdCached,
475}
476impl LoweredSignatureCached {
477    fn new<'db>(signature: Signature<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
478        Self {
479            params: signature
480                .params
481                .iter()
482                .map(|param| LoweredParamCached::new(param, ctx))
483                .collect(),
484            extra_rets: signature
485                .extra_rets
486                .into_iter()
487                .map(|var| ExprVarMemberPathCached::new(var, &mut ctx.semantic_ctx))
488                .collect(),
489
490            return_type: TypeIdCached::new(signature.return_type, &mut ctx.semantic_ctx),
491            implicits: signature
492                .implicits
493                .into_iter()
494                .map(|ty| TypeIdCached::new(ty, &mut ctx.semantic_ctx))
495                .collect(),
496            panicable: signature.panicable,
497            location: LocationIdCached::new(signature.location, ctx),
498        }
499    }
500    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Signature<'db> {
501        Signature {
502            params: self.params.into_iter().map(|var| var.embed(ctx)).collect(),
503            extra_rets: self
504                .extra_rets
505                .into_iter()
506                .map(|var| var.get_embedded(&ctx.semantic_loading_data, ctx.db))
507                .collect(),
508            return_type: self.return_type.get_embedded(&ctx.semantic_loading_data),
509            implicits: self
510                .implicits
511                .into_iter()
512                .map(|ty| ty.get_embedded(&ctx.semantic_loading_data))
513                .collect(),
514            panicable: self.panicable,
515            location: self.location.embed(ctx),
516        }
517    }
518}
519
520#[derive(Serialize, Deserialize)]
521struct LoweredParamCached {
522    ty: TypeIdCached,
523    stable_ptr: SyntaxStablePtrIdCached,
524}
525impl LoweredParamCached {
526    fn new<'db>(param: &LoweredParam<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
527        Self {
528            ty: TypeIdCached::new(param.ty, &mut ctx.semantic_ctx),
529            stable_ptr: SyntaxStablePtrIdCached::new(
530                param.stable_ptr.untyped(),
531                &mut ctx.semantic_ctx.defs_ctx,
532            ),
533        }
534    }
535    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> LoweredParam<'db> {
536        LoweredParam {
537            ty: self.ty.get_embedded(&ctx.semantic_loading_data),
538            stable_ptr: ExprPtr(
539                self.stable_ptr.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
540            ),
541        }
542    }
543}
544#[derive(Serialize, Deserialize)]
545struct VariableCached {
546    droppable: Option<ImplIdCached>,
547    /// Can the type be (trivially) copied.
548    copyable: Option<ImplIdCached>,
549    /// A Destruct impl for the type, if found.
550    destruct_impl: Option<ImplIdCached>,
551    /// A PanicDestruct impl for the type, if found.
552    panic_destruct_impl: Option<ImplIdCached>,
553    /// Semantic type of the variable.
554    ty: TypeIdCached,
555    location: LocationIdCached,
556}
557impl VariableCached {
558    fn new<'db>(variable: Variable<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
559        let TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl } = variable.info;
560        Self {
561            droppable: droppable
562                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
563                .ok(),
564            copyable: copyable
565                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
566                .ok(),
567            destruct_impl: destruct_impl
568                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
569                .ok(),
570            panic_destruct_impl: panic_destruct_impl
571                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
572                .ok(),
573            ty: TypeIdCached::new(variable.ty, &mut ctx.semantic_ctx),
574            location: LocationIdCached::new(variable.location, ctx),
575        }
576    }
577    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Variable<'db> {
578        Variable {
579            ty: self.ty.get_embedded(&ctx.semantic_loading_data),
580            location: self.location.embed(ctx),
581            info: TypeInfo {
582                droppable: self
583                    .droppable
584                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
585                    .ok_or(InferenceError::Reported(skip_diagnostic())),
586                copyable: self
587                    .copyable
588                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
589                    .ok_or(InferenceError::Reported(skip_diagnostic())),
590                destruct_impl: self
591                    .destruct_impl
592                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
593                    .ok_or(InferenceError::Reported(skip_diagnostic())),
594                panic_destruct_impl: self
595                    .panic_destruct_impl
596                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
597                    .ok_or(InferenceError::Reported(skip_diagnostic())),
598            },
599        }
600    }
601}
602
603#[derive(Serialize, Deserialize)]
604struct VarUsageCached {
605    /// Variable id.
606    var_id: usize,
607    /// Location of the usage.
608    location: LocationIdCached,
609}
610
611impl VarUsageCached {
612    fn new<'db>(var_usage: VarUsage<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
613        Self {
614            var_id: var_usage.var_id.index(),
615            location: LocationIdCached::new(var_usage.location, ctx),
616        }
617    }
618    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> VarUsage<'db> {
619        VarUsage {
620            var_id: ctx.lowered_variables_id[self.var_id],
621            location: self.location.embed(ctx),
622        }
623    }
624}
625
626#[derive(Serialize, Deserialize)]
627struct BlockCached {
628    /// Statements in the block.
629    statements: Vec<StatementCached>,
630    /// Block end.
631    end: BlockEndCached,
632}
633impl BlockCached {
634    fn new<'db>(block: Block<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
635        Self {
636            statements: block
637                .statements
638                .into_iter()
639                .map(|stmt| StatementCached::new(stmt, ctx))
640                .collect(),
641            end: BlockEndCached::new(block.end, ctx),
642        }
643    }
644    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Block<'db> {
645        Block {
646            statements: self.statements.into_iter().map(|stmt| stmt.embed(ctx)).collect(),
647            end: self.end.embed(ctx),
648        }
649    }
650}
651#[derive(Serialize, Deserialize)]
652enum BlockEndCached {
653    /// The block was created but still needs to be populated. Block must not be in this state in
654    /// the end of the lowering phase.
655    NotSet,
656    /// This block ends with a `return` statement, exiting the function.
657    Return(Vec<VarUsageCached>, LocationIdCached),
658    /// This block ends with a panic.
659    Panic(VarUsageCached),
660    /// This block ends with a jump to a different block.
661    Goto(usize, VarRemappingCached),
662    Match {
663        info: MatchInfoCached,
664    },
665}
666impl BlockEndCached {
667    fn new<'db>(block_end: BlockEnd<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
668        match block_end {
669            BlockEnd::Return(returns, location) => BlockEndCached::Return(
670                returns.iter().map(|var| VarUsageCached::new(*var, ctx)).collect(),
671                LocationIdCached::new(location, ctx),
672            ),
673            BlockEnd::Panic(data) => BlockEndCached::Panic(VarUsageCached::new(data, ctx)),
674            BlockEnd::Goto(block_id, remapping) => {
675                BlockEndCached::Goto(block_id.0, VarRemappingCached::new(remapping, ctx))
676            }
677            BlockEnd::NotSet => BlockEndCached::NotSet,
678            BlockEnd::Match { info } => {
679                BlockEndCached::Match { info: MatchInfoCached::new(info, ctx) }
680            }
681        }
682    }
683    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> BlockEnd<'db> {
684        match self {
685            BlockEndCached::Return(returns, location) => BlockEnd::Return(
686                returns.into_iter().map(|var_usage| var_usage.embed(ctx)).collect(),
687                location.embed(ctx),
688            ),
689            BlockEndCached::Panic(var_id) => BlockEnd::Panic(var_id.embed(ctx)),
690            BlockEndCached::Goto(block_id, remapping) => {
691                BlockEnd::Goto(BlockId(block_id), remapping.embed(ctx))
692            }
693            BlockEndCached::NotSet => BlockEnd::NotSet,
694            BlockEndCached::Match { info } => BlockEnd::Match { info: info.embed(ctx) },
695        }
696    }
697}
698
699#[derive(Serialize, Deserialize)]
700struct VarRemappingCached {
701    /// Map from new_var to old_var (since new_var cannot appear twice, but old_var can).
702    remapping: OrderedHashMap<usize, VarUsageCached>,
703}
704impl VarRemappingCached {
705    fn new<'db>(var_remapping: VarRemapping<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
706        Self {
707            remapping: var_remapping
708                .iter()
709                .map(|(dst, src)| (dst.index(), VarUsageCached::new(*src, ctx)))
710                .collect(),
711        }
712    }
713    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> VarRemapping<'db> {
714        let mut remapping = OrderedHashMap::default();
715        for (dst, src) in self.remapping {
716            remapping.insert(ctx.lowered_variables_id[dst], src.embed(ctx));
717        }
718        VarRemapping { remapping }
719    }
720}
721
722#[derive(Serialize, Deserialize)]
723enum MatchInfoCached {
724    Enum(MatchEnumInfoCached),
725    Extern(MatchExternInfoCached),
726    Value(MatchEnumValueCached),
727}
728impl MatchInfoCached {
729    fn new<'db>(match_info: MatchInfo<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
730        match match_info {
731            MatchInfo::Enum(info) => MatchInfoCached::Enum(MatchEnumInfoCached::new(info, ctx)),
732            MatchInfo::Extern(info) => {
733                MatchInfoCached::Extern(MatchExternInfoCached::new(info, ctx))
734            }
735            MatchInfo::Value(info) => MatchInfoCached::Value(MatchEnumValueCached::new(info, ctx)),
736        }
737    }
738    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchInfo<'db> {
739        match self {
740            MatchInfoCached::Enum(info) => MatchInfo::Enum(info.embed(ctx)),
741            MatchInfoCached::Extern(info) => MatchInfo::Extern(info.embed(ctx)),
742            MatchInfoCached::Value(info) => MatchInfo::Value(info.embed(ctx)),
743        }
744    }
745}
746
747#[derive(Serialize, Deserialize)]
748struct MatchEnumInfoCached {
749    concrete_enum_id: ConcreteEnumCached,
750    /// A living variable in current scope to match on.
751    input: VarUsageCached,
752    /// Match arms. All blocks should have the same rets.
753    /// Order must be identical to the order in the definition of the enum.
754    arms: Vec<MatchArmCached>,
755    location: LocationIdCached,
756}
757impl MatchEnumInfoCached {
758    fn new<'db>(match_enum_info: MatchEnumInfo<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
759        Self {
760            concrete_enum_id: ConcreteEnumCached::new(
761                match_enum_info.concrete_enum_id,
762                &mut ctx.semantic_ctx,
763            ),
764            input: VarUsageCached::new(match_enum_info.input, ctx),
765            arms: match_enum_info
766                .arms
767                .into_iter()
768                .map(|arm| MatchArmCached::new(arm, ctx))
769                .collect(),
770            location: LocationIdCached::new(match_enum_info.location, ctx),
771        }
772    }
773    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchEnumInfo<'db> {
774        MatchEnumInfo {
775            concrete_enum_id: self
776                .concrete_enum_id
777                .get_embedded(&ctx.semantic_loading_data, ctx.db),
778            input: self.input.embed(ctx),
779            arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(),
780            location: self.location.embed(ctx),
781        }
782    }
783}
784
785#[derive(Serialize, Deserialize)]
786struct MatchExternInfoCached {
787    /// A concrete external function to call.
788    function: FunctionIdCached,
789    /// Living variables in current scope to move to the function, as arguments.
790    inputs: Vec<VarUsageCached>,
791    /// Match arms. All blocks should have the same rets.
792    /// Order must be identical to the order in the definition of the enum.
793    arms: Vec<MatchArmCached>,
794    location: LocationIdCached,
795}
796
797impl MatchExternInfoCached {
798    fn new<'db>(
799        match_extern_info: MatchExternInfo<'db>,
800        ctx: &mut CacheSavingContext<'db>,
801    ) -> Self {
802        Self {
803            function: FunctionIdCached::new(match_extern_info.function, ctx),
804            inputs: match_extern_info
805                .inputs
806                .iter()
807                .map(|var| VarUsageCached::new(*var, ctx))
808                .collect(),
809            arms: match_extern_info
810                .arms
811                .into_iter()
812                .map(|arm| MatchArmCached::new(arm, ctx))
813                .collect(),
814            location: LocationIdCached::new(match_extern_info.location, ctx),
815        }
816    }
817    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchExternInfo<'db> {
818        MatchExternInfo {
819            function: self.function.embed(ctx),
820            inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(),
821            arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(),
822            location: self.location.embed(ctx),
823        }
824    }
825}
826
827#[derive(Serialize, Deserialize)]
828struct MatchEnumValueCached {
829    num_of_arms: usize,
830
831    /// A living variable in current scope to match on.
832    input: VarUsageCached,
833    /// Match arms. All blocks should have the same rets.
834    arms: Vec<MatchArmCached>,
835    location: LocationIdCached,
836}
837
838impl MatchEnumValueCached {
839    fn new<'db>(match_enum_value: MatchEnumValue<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
840        Self {
841            num_of_arms: match_enum_value.num_of_arms,
842            input: VarUsageCached::new(match_enum_value.input, ctx),
843            arms: match_enum_value
844                .arms
845                .into_iter()
846                .map(|arm| MatchArmCached::new(arm, ctx))
847                .collect(),
848            location: LocationIdCached::new(match_enum_value.location, ctx),
849        }
850    }
851    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchEnumValue<'db> {
852        MatchEnumValue {
853            num_of_arms: self.num_of_arms,
854            input: self.input.embed(ctx),
855            arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(),
856            location: self.location.embed(ctx),
857        }
858    }
859}
860/// An arm of a match statement.
861#[derive(Serialize, Deserialize)]
862struct MatchArmCached {
863    /// The selector of the arm.
864    arm_selector: MatchArmSelectorCached,
865
866    /// The block_id where the relevant arm is implemented.
867    block_id: usize,
868
869    /// The list of variable ids introduced in this arm.
870    var_ids: Vec<usize>,
871}
872
873impl MatchArmCached {
874    fn new<'db>(match_arm: MatchArm<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
875        Self {
876            arm_selector: MatchArmSelectorCached::new(
877                match_arm.arm_selector,
878                &mut ctx.semantic_ctx,
879            ),
880            block_id: match_arm.block_id.0,
881            var_ids: match_arm.var_ids.iter().map(|var| var.index()).collect(),
882        }
883    }
884    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchArm<'db> {
885        MatchArm {
886            arm_selector: self.arm_selector.get_embedded(&ctx.semantic_loading_data, ctx.db),
887            block_id: BlockId(self.block_id),
888            var_ids: self
889                .var_ids
890                .into_iter()
891                .map(|var_id| ctx.lowered_variables_id[var_id])
892                .collect(),
893        }
894    }
895}
896
897#[derive(Serialize, Deserialize)]
898enum StatementCached {
899    // Values.
900    Const(StatementConstCached),
901
902    // Flow control.
903    Call(StatementCallCached),
904
905    // Structs (including tuples).
906    StructConstruct(StatementStructConstructCached),
907    StructDestructure(StatementStructDestructureCached),
908
909    // Enums.
910    EnumConstruct(StatementEnumConstructCached),
911
912    Snapshot(StatementSnapshotCached),
913    Desnap(StatementDesnapCached),
914}
915
916impl StatementCached {
917    fn new<'db>(stmt: Statement<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
918        match stmt {
919            Statement::Const(stmt) => StatementCached::Const(StatementConstCached::new(stmt, ctx)),
920            Statement::Call(stmt) => StatementCached::Call(StatementCallCached::new(stmt, ctx)),
921            Statement::StructConstruct(stmt) => {
922                StatementCached::StructConstruct(StatementStructConstructCached::new(stmt, ctx))
923            }
924            Statement::StructDestructure(stmt) => {
925                StatementCached::StructDestructure(StatementStructDestructureCached::new(stmt, ctx))
926            }
927            Statement::EnumConstruct(stmt) => {
928                StatementCached::EnumConstruct(StatementEnumConstructCached::new(stmt, ctx))
929            }
930            Statement::Snapshot(stmt) => {
931                StatementCached::Snapshot(StatementSnapshotCached::new(stmt, ctx))
932            }
933            Statement::Desnap(stmt) => {
934                StatementCached::Desnap(StatementDesnapCached::new(stmt, ctx))
935            }
936        }
937    }
938    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Statement<'db> {
939        match self {
940            StatementCached::Const(stmt) => Statement::Const(stmt.embed(ctx)),
941            StatementCached::Call(stmt) => Statement::Call(stmt.embed(ctx)),
942            StatementCached::StructConstruct(stmt) => Statement::StructConstruct(stmt.embed(ctx)),
943            StatementCached::StructDestructure(stmt) => {
944                Statement::StructDestructure(stmt.embed(ctx))
945            }
946            StatementCached::EnumConstruct(stmt) => Statement::EnumConstruct(stmt.embed(ctx)),
947            StatementCached::Snapshot(stmt) => Statement::Snapshot(stmt.embed(ctx)),
948            StatementCached::Desnap(stmt) => Statement::Desnap(stmt.embed(ctx)),
949        }
950    }
951}
952
953#[derive(Serialize, Deserialize)]
954struct StatementConstCached {
955    /// The value of the const.
956    value: ConstValueIdCached,
957    /// The variable to bind the value to.
958    output: usize,
959    /// Is the const boxed.
960    boxed: bool,
961}
962impl StatementConstCached {
963    fn new<'db>(stmt: StatementConst<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
964        Self {
965            value: ConstValueIdCached::new(stmt.value, &mut ctx.semantic_ctx),
966            output: stmt.output.index(),
967            boxed: stmt.boxed,
968        }
969    }
970    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementConst<'db> {
971        StatementConst::new(
972            self.value.get_embedded(&ctx.semantic_loading_data),
973            ctx.lowered_variables_id[self.output],
974            self.boxed,
975        )
976    }
977}
978
979#[derive(Serialize, Deserialize)]
980struct StatementCallCached {
981    function: FunctionIdCached,
982    inputs: Vec<VarUsageCached>,
983    with_coupon: bool,
984    outputs: Vec<usize>,
985    location: LocationIdCached,
986    is_specialization_base_call: bool,
987}
988impl StatementCallCached {
989    fn new<'db>(stmt: StatementCall<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
990        Self {
991            function: FunctionIdCached::new(stmt.function, ctx),
992            inputs: stmt.inputs.iter().map(|var| VarUsageCached::new(*var, ctx)).collect(),
993            with_coupon: stmt.with_coupon,
994            outputs: stmt.outputs.iter().map(|var| var.index()).collect(),
995            location: LocationIdCached::new(stmt.location, ctx),
996            is_specialization_base_call: stmt.is_specialization_base_call,
997        }
998    }
999    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementCall<'db> {
1000        StatementCall {
1001            function: self.function.embed(ctx),
1002            inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(),
1003            with_coupon: self.with_coupon,
1004            outputs: self
1005                .outputs
1006                .into_iter()
1007                .map(|var_id| ctx.lowered_variables_id[var_id])
1008                .collect(),
1009            location: self.location.embed(ctx),
1010            is_specialization_base_call: self.is_specialization_base_call,
1011        }
1012    }
1013}
1014
1015#[derive(Serialize, Deserialize, Clone)]
1016enum FunctionCached {
1017    /// An original function from the user code.
1018    Semantic(SemanticFunctionIdCached),
1019    /// A function generated by the compiler.
1020    Generated(GeneratedFunctionCached),
1021}
1022impl FunctionCached {
1023    fn new<'db>(function: FunctionLongId<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1024        match function {
1025            FunctionLongId::Semantic(id) => {
1026                FunctionCached::Semantic(SemanticFunctionIdCached::new(id, &mut ctx.semantic_ctx))
1027            }
1028            FunctionLongId::Generated(id) => {
1029                FunctionCached::Generated(GeneratedFunctionCached::new(id, ctx))
1030            }
1031            FunctionLongId::Specialized(_) => {
1032                unreachable!("Specialization of functions only occurs post concretization.")
1033            }
1034        }
1035    }
1036    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> FunctionId<'db> {
1037        match self {
1038            FunctionCached::Semantic(id) => {
1039                FunctionLongId::Semantic(id.get_embedded(&ctx.semantic_loading_data))
1040            }
1041            FunctionCached::Generated(id) => FunctionLongId::Generated(id.embed(ctx)),
1042        }
1043        .intern(ctx.db)
1044    }
1045}
1046
1047#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)]
1048struct FunctionIdCached(usize);
1049impl FunctionIdCached {
1050    fn new<'db>(function_id: FunctionId<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1051        if let Some(id) = ctx.function_ids.get(&function_id) {
1052            return *id;
1053        }
1054        let function = FunctionCached::new(function_id.long(ctx.db).clone(), ctx);
1055        let id = FunctionIdCached(ctx.function_ids_lookup.len());
1056        ctx.function_ids_lookup.push(function);
1057        ctx.function_ids.insert(function_id, id);
1058        id
1059    }
1060    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> FunctionId<'db> {
1061        if let Some(function_id) = ctx.function_ids.get(&self) {
1062            return *function_id;
1063        }
1064
1065        let function = ctx.function_ids_lookup[self.0].clone();
1066        let function_id = function.embed(ctx);
1067        ctx.function_ids.insert(self, function_id);
1068        function_id
1069    }
1070}
1071
1072#[derive(Serialize, Deserialize, Clone)]
1073struct GeneratedFunctionCached {
1074    parent: SemanticConcreteFunctionWithBodyCached,
1075    key: GeneratedFunctionKeyCached,
1076}
1077impl GeneratedFunctionCached {
1078    fn new<'db>(function: GeneratedFunction<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1079        Self {
1080            parent: SemanticConcreteFunctionWithBodyCached::new(
1081                function.parent,
1082                &mut ctx.semantic_ctx,
1083            ),
1084            key: GeneratedFunctionKeyCached::new(function.key, ctx),
1085        }
1086    }
1087    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> GeneratedFunction<'db> {
1088        GeneratedFunction {
1089            parent: self.parent.get_embedded(&ctx.semantic_loading_data, ctx.db),
1090            key: self.key.embed(ctx),
1091        }
1092    }
1093}
1094
1095#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq)]
1096enum GeneratedFunctionKeyCached {
1097    Loop(SyntaxStablePtrIdCached),
1098    TraitFunc(LanguageElementCached, SyntaxStablePtrIdCached),
1099}
1100
1101impl GeneratedFunctionKeyCached {
1102    fn new<'db>(key: GeneratedFunctionKey<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1103        match key {
1104            GeneratedFunctionKey::Loop(id) => GeneratedFunctionKeyCached::Loop(
1105                SyntaxStablePtrIdCached::new(id.untyped(), &mut ctx.semantic_ctx.defs_ctx),
1106            ),
1107            GeneratedFunctionKey::TraitFunc(id, stable_location) => {
1108                GeneratedFunctionKeyCached::TraitFunc(
1109                    LanguageElementCached::new(id, &mut ctx.semantic_ctx.defs_ctx),
1110                    SyntaxStablePtrIdCached::new(
1111                        stable_location.stable_ptr(),
1112                        &mut ctx.semantic_ctx.defs_ctx,
1113                    ),
1114                )
1115            }
1116        }
1117    }
1118    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> GeneratedFunctionKey<'db> {
1119        match self {
1120            GeneratedFunctionKeyCached::Loop(id) => GeneratedFunctionKey::Loop(ExprPtr(
1121                id.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1122            )),
1123            GeneratedFunctionKeyCached::TraitFunc(id, stable_location) => {
1124                let (module_id, stable_ptr) =
1125                    id.get_embedded(&ctx.semantic_loading_data.defs_loading_data);
1126                GeneratedFunctionKey::TraitFunc(
1127                    TraitFunctionLongId(module_id, TraitItemFunctionPtr(stable_ptr)).intern(ctx.db),
1128                    StableLocation::new(
1129                        stable_location.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1130                    ),
1131                )
1132            }
1133        }
1134    }
1135}
1136
1137#[derive(Serialize, Deserialize)]
1138struct StatementStructConstructCached {
1139    inputs: Vec<VarUsageCached>,
1140    /// The variable to bind the value to.
1141    output: usize,
1142}
1143impl StatementStructConstructCached {
1144    fn new<'db>(stmt: StatementStructConstruct<'db>, _ctx: &mut CacheSavingContext<'db>) -> Self {
1145        Self {
1146            inputs: stmt.inputs.iter().map(|var| VarUsageCached::new(*var, _ctx)).collect(),
1147            output: stmt.output.index(),
1148        }
1149    }
1150    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementStructConstruct<'db> {
1151        StatementStructConstruct {
1152            inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(),
1153            output: ctx.lowered_variables_id[self.output],
1154        }
1155    }
1156}
1157#[derive(Serialize, Deserialize)]
1158struct StatementStructDestructureCached {
1159    /// A living variable in current scope to destructure.
1160    input: VarUsageCached,
1161    /// The variables to bind values to.
1162    outputs: Vec<usize>,
1163}
1164impl StatementStructDestructureCached {
1165    fn new<'db>(stmt: StatementStructDestructure<'db>, _ctx: &mut CacheSavingContext<'db>) -> Self {
1166        Self {
1167            input: VarUsageCached::new(stmt.input, _ctx),
1168            outputs: stmt.outputs.iter().map(|var| var.index()).collect(),
1169        }
1170    }
1171    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementStructDestructure<'db> {
1172        StatementStructDestructure {
1173            input: self.input.embed(ctx),
1174            outputs: self
1175                .outputs
1176                .into_iter()
1177                .map(|var_id| ctx.lowered_variables_id[var_id])
1178                .collect(),
1179        }
1180    }
1181}
1182
1183#[derive(Serialize, Deserialize)]
1184struct StatementEnumConstructCached {
1185    variant: ConcreteVariantCached,
1186    /// A living variable in current scope to wrap with the variant.
1187    input: VarUsageCached,
1188    /// The variable to bind the value to.
1189    output: usize,
1190}
1191impl StatementEnumConstructCached {
1192    fn new<'db>(stmt: StatementEnumConstruct<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1193        Self {
1194            variant: ConcreteVariantCached::new(stmt.variant, &mut ctx.semantic_ctx),
1195            input: VarUsageCached::new(stmt.input, ctx),
1196            output: stmt.output.index(),
1197        }
1198    }
1199    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementEnumConstruct<'db> {
1200        StatementEnumConstruct {
1201            variant: self.variant.get_embedded(&ctx.semantic_loading_data, ctx.db),
1202            input: self.input.embed(ctx),
1203            output: ctx.lowered_variables_id[self.output],
1204        }
1205    }
1206}
1207
1208#[derive(Serialize, Deserialize)]
1209struct StatementSnapshotCached {
1210    input: VarUsageCached,
1211    outputs: [usize; 2],
1212}
1213impl StatementSnapshotCached {
1214    fn new<'db>(stmt: StatementSnapshot<'db>, _ctx: &mut CacheSavingContext<'db>) -> Self {
1215        Self {
1216            input: VarUsageCached::new(stmt.input, _ctx),
1217            outputs: stmt.outputs.map(|var| var.index()),
1218        }
1219    }
1220    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementSnapshot<'db> {
1221        StatementSnapshot {
1222            input: self.input.embed(ctx),
1223            outputs: [
1224                ctx.lowered_variables_id[self.outputs[0]],
1225                ctx.lowered_variables_id[self.outputs[1]],
1226            ],
1227        }
1228    }
1229}
1230
1231#[derive(Serialize, Deserialize)]
1232struct StatementDesnapCached {
1233    input: VarUsageCached,
1234    /// The variable to bind the value to.
1235    output: usize,
1236}
1237impl StatementDesnapCached {
1238    fn new<'db>(stmt: StatementDesnap<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1239        Self { input: VarUsageCached::new(stmt.input, ctx), output: stmt.output.index() }
1240    }
1241    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementDesnap<'db> {
1242        StatementDesnap {
1243            input: self.input.embed(ctx),
1244            output: ctx.lowered_variables_id[self.output],
1245        }
1246    }
1247}
1248
1249#[derive(Serialize, Deserialize, Clone)]
1250struct LocationCached {
1251    /// The stable location of the object.
1252    stable_location: SyntaxStablePtrIdCached,
1253    /// Function call locations where this value was inlined from.
1254    inline_locations: Vec<SyntaxStablePtrIdCached>,
1255}
1256impl LocationCached {
1257    fn new<'db>(location: Location<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1258        Self {
1259            stable_location: SyntaxStablePtrIdCached::new(
1260                location.stable_location.stable_ptr(),
1261                &mut ctx.semantic_ctx.defs_ctx,
1262            ),
1263            inline_locations: location
1264                .inline_locations
1265                .iter()
1266                .map(|loc| {
1267                    SyntaxStablePtrIdCached::new(loc.stable_ptr(), &mut ctx.semantic_ctx.defs_ctx)
1268                })
1269                .collect(),
1270        }
1271    }
1272    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Location<'db> {
1273        Location {
1274            stable_location: StableLocation::new(
1275                self.stable_location.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1276            ),
1277            inline_locations: self
1278                .inline_locations
1279                .into_iter()
1280                .map(|loc| {
1281                    StableLocation::new(
1282                        loc.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1283                    )
1284                })
1285                .collect(),
1286            notes: Default::default(),
1287        }
1288    }
1289}
1290
1291#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)]
1292struct LocationIdCached(usize);
1293
1294impl LocationIdCached {
1295    fn new<'db>(location_id: LocationId<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1296        if let Some(id) = ctx.location_ids.get(&location_id) {
1297            return *id;
1298        }
1299        let location = LocationCached::new(location_id.long(ctx.db).clone(), ctx);
1300        let id = LocationIdCached(ctx.location_ids_lookup.len());
1301        ctx.location_ids_lookup.push(location);
1302        ctx.location_ids.insert(location_id, id);
1303        id
1304    }
1305    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> LocationId<'db> {
1306        if let Some(location_id) = ctx.location_ids.get(&self) {
1307            return *location_id;
1308        }
1309        let location = ctx.location_ids_lookup[self.0].clone();
1310        let location = location.embed(ctx).intern(ctx.db);
1311        ctx.location_ids.insert(self, location);
1312        location
1313    }
1314}