Skip to main content

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