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