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
422                .blocks
423                .iter()
424                .map(|block: (BlockId, &Block<'_>)| BlockCached::new(block.1.clone(), ctx))
425                .collect(),
426            parameters: lowered.parameters.iter().map(|var| var.index()).collect(),
427        }
428    }
429    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Lowered<'db> {
430        ctx.lowered_variables_id.clear();
431        let mut variables = Arena::new();
432        for var in self.variables {
433            let id = variables.alloc(var.embed(ctx));
434            ctx.lowered_variables_id.push(id);
435        }
436
437        let mut blocks = BlocksBuilder::new();
438        for block in self.blocks {
439            blocks.alloc(block.embed(ctx));
440        }
441        Lowered {
442            diagnostics: Default::default(),
443            signature: self.signature.embed(ctx),
444            variables,
445            blocks: blocks.build().unwrap(),
446            parameters: self
447                .parameters
448                .into_iter()
449                .map(|var_id| ctx.lowered_variables_id[var_id])
450                .collect(),
451        }
452    }
453}
454
455#[derive(Serialize, Deserialize)]
456struct LoweredSignatureCached {
457    /// Function parameters.
458    params: Vec<LoweredParamCached>,
459    /// Extra return values.
460    extra_rets: Vec<ExprVarMemberPathCached>,
461    /// Return type.
462    return_type: TypeIdCached,
463    /// Implicit parameters.
464    implicits: Vec<TypeIdCached>,
465    /// Whether the function is panicable.
466    panicable: bool,
467    location: LocationIdCached,
468}
469impl LoweredSignatureCached {
470    fn new<'db>(signature: Signature<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
471        Self {
472            params: signature
473                .params
474                .iter()
475                .map(|param| LoweredParamCached::new(param, ctx))
476                .collect(),
477            extra_rets: signature
478                .extra_rets
479                .into_iter()
480                .map(|var| ExprVarMemberPathCached::new(var, &mut ctx.semantic_ctx))
481                .collect(),
482
483            return_type: TypeIdCached::new(signature.return_type, &mut ctx.semantic_ctx),
484            implicits: signature
485                .implicits
486                .into_iter()
487                .map(|ty| TypeIdCached::new(ty, &mut ctx.semantic_ctx))
488                .collect(),
489            panicable: signature.panicable,
490            location: LocationIdCached::new(signature.location, ctx),
491        }
492    }
493    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Signature<'db> {
494        Signature {
495            params: self.params.into_iter().map(|var| var.embed(ctx)).collect(),
496            extra_rets: self
497                .extra_rets
498                .into_iter()
499                .map(|var| var.get_embedded(&ctx.semantic_loading_data, ctx.db))
500                .collect(),
501            return_type: self.return_type.get_embedded(&ctx.semantic_loading_data),
502            implicits: self
503                .implicits
504                .into_iter()
505                .map(|ty| ty.get_embedded(&ctx.semantic_loading_data))
506                .collect(),
507            panicable: self.panicable,
508            location: self.location.embed(ctx),
509        }
510    }
511}
512
513#[derive(Serialize, Deserialize)]
514struct LoweredParamCached {
515    ty: TypeIdCached,
516    stable_ptr: SyntaxStablePtrIdCached,
517}
518impl LoweredParamCached {
519    fn new<'db>(param: &LoweredParam<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
520        Self {
521            ty: TypeIdCached::new(param.ty, &mut ctx.semantic_ctx),
522            stable_ptr: SyntaxStablePtrIdCached::new(
523                param.stable_ptr.untyped(),
524                &mut ctx.semantic_ctx.defs_ctx,
525            ),
526        }
527    }
528    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> LoweredParam<'db> {
529        LoweredParam {
530            ty: self.ty.get_embedded(&ctx.semantic_loading_data),
531            stable_ptr: ExprPtr(
532                self.stable_ptr.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
533            ),
534        }
535    }
536}
537#[derive(Serialize, Deserialize)]
538struct VariableCached {
539    droppable: Option<ImplIdCached>,
540    /// Can the type be (trivially) copied.
541    copyable: Option<ImplIdCached>,
542    /// A Destruct impl for the type, if found.
543    destruct_impl: Option<ImplIdCached>,
544    /// A PanicDestruct impl for the type, if found.
545    panic_destruct_impl: Option<ImplIdCached>,
546    /// Semantic type of the variable.
547    ty: TypeIdCached,
548    location: LocationIdCached,
549}
550impl VariableCached {
551    fn new<'db>(variable: Variable<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
552        let TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl } = variable.info;
553        Self {
554            droppable: droppable
555                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
556                .ok(),
557            copyable: copyable
558                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
559                .ok(),
560            destruct_impl: destruct_impl
561                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
562                .ok(),
563            panic_destruct_impl: panic_destruct_impl
564                .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx))
565                .ok(),
566            ty: TypeIdCached::new(variable.ty, &mut ctx.semantic_ctx),
567            location: LocationIdCached::new(variable.location, ctx),
568        }
569    }
570    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Variable<'db> {
571        Variable {
572            ty: self.ty.get_embedded(&ctx.semantic_loading_data),
573            location: self.location.embed(ctx),
574            info: TypeInfo {
575                droppable: self
576                    .droppable
577                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
578                    .ok_or(InferenceError::Reported(skip_diagnostic())),
579                copyable: self
580                    .copyable
581                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
582                    .ok_or(InferenceError::Reported(skip_diagnostic())),
583                destruct_impl: self
584                    .destruct_impl
585                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
586                    .ok_or(InferenceError::Reported(skip_diagnostic())),
587                panic_destruct_impl: self
588                    .panic_destruct_impl
589                    .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data))
590                    .ok_or(InferenceError::Reported(skip_diagnostic())),
591            },
592        }
593    }
594}
595
596#[derive(Serialize, Deserialize)]
597struct VarUsageCached {
598    /// Variable id.
599    var_id: usize,
600    /// Location of the usage.
601    location: LocationIdCached,
602}
603
604impl VarUsageCached {
605    fn new<'db>(var_usage: VarUsage<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
606        Self {
607            var_id: var_usage.var_id.index(),
608            location: LocationIdCached::new(var_usage.location, ctx),
609        }
610    }
611    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> VarUsage<'db> {
612        VarUsage {
613            var_id: ctx.lowered_variables_id[self.var_id],
614            location: self.location.embed(ctx),
615        }
616    }
617}
618
619#[derive(Serialize, Deserialize)]
620struct BlockCached {
621    /// Statements in the block.
622    statements: Vec<StatementCached>,
623    /// Block end.
624    end: BlockEndCached,
625}
626impl BlockCached {
627    fn new<'db>(block: Block<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
628        Self {
629            statements: block
630                .statements
631                .into_iter()
632                .map(|stmt| StatementCached::new(stmt, ctx))
633                .collect(),
634            end: BlockEndCached::new(block.end, ctx),
635        }
636    }
637    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Block<'db> {
638        Block {
639            statements: self.statements.into_iter().map(|stmt| stmt.embed(ctx)).collect(),
640            end: self.end.embed(ctx),
641        }
642    }
643}
644#[derive(Serialize, Deserialize)]
645enum BlockEndCached {
646    /// The block was created but still needs to be populated. Block must not be in this state in
647    /// the end of the lowering phase.
648    NotSet,
649    /// This block ends with a `return` statement, exiting the function.
650    Return(Vec<VarUsageCached>, LocationIdCached),
651    /// This block ends with a panic.
652    Panic(VarUsageCached),
653    /// This block ends with a jump to a different block.
654    Goto(usize, VarRemappingCached),
655    Match {
656        info: MatchInfoCached,
657    },
658}
659impl BlockEndCached {
660    fn new<'db>(block_end: BlockEnd<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
661        match block_end {
662            BlockEnd::Return(returns, location) => BlockEndCached::Return(
663                returns.iter().map(|var| VarUsageCached::new(*var, ctx)).collect(),
664                LocationIdCached::new(location, ctx),
665            ),
666            BlockEnd::Panic(data) => BlockEndCached::Panic(VarUsageCached::new(data, ctx)),
667            BlockEnd::Goto(block_id, remapping) => {
668                BlockEndCached::Goto(block_id.0, VarRemappingCached::new(remapping, ctx))
669            }
670            BlockEnd::NotSet => BlockEndCached::NotSet,
671            BlockEnd::Match { info } => {
672                BlockEndCached::Match { info: MatchInfoCached::new(info, ctx) }
673            }
674        }
675    }
676    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> BlockEnd<'db> {
677        match self {
678            BlockEndCached::Return(returns, location) => BlockEnd::Return(
679                returns.into_iter().map(|var_usage| var_usage.embed(ctx)).collect(),
680                location.embed(ctx),
681            ),
682            BlockEndCached::Panic(var_id) => BlockEnd::Panic(var_id.embed(ctx)),
683            BlockEndCached::Goto(block_id, remapping) => {
684                BlockEnd::Goto(BlockId(block_id), remapping.embed(ctx))
685            }
686            BlockEndCached::NotSet => BlockEnd::NotSet,
687            BlockEndCached::Match { info } => BlockEnd::Match { info: info.embed(ctx) },
688        }
689    }
690}
691
692#[derive(Serialize, Deserialize)]
693struct VarRemappingCached {
694    /// Map from new_var to old_var (since new_var cannot appear twice, but old_var can).
695    remapping: OrderedHashMap<usize, VarUsageCached>,
696}
697impl VarRemappingCached {
698    fn new<'db>(var_remapping: VarRemapping<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
699        Self {
700            remapping: var_remapping
701                .iter()
702                .map(|(dst, src)| (dst.index(), VarUsageCached::new(*src, ctx)))
703                .collect(),
704        }
705    }
706    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> VarRemapping<'db> {
707        let mut remapping = OrderedHashMap::default();
708        for (dst, src) in self.remapping {
709            remapping.insert(ctx.lowered_variables_id[dst], src.embed(ctx));
710        }
711        VarRemapping { remapping }
712    }
713}
714
715#[derive(Serialize, Deserialize)]
716enum MatchInfoCached {
717    Enum(MatchEnumInfoCached),
718    Extern(MatchExternInfoCached),
719    Value(MatchEnumValueCached),
720}
721impl MatchInfoCached {
722    fn new<'db>(match_info: MatchInfo<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
723        match match_info {
724            MatchInfo::Enum(info) => MatchInfoCached::Enum(MatchEnumInfoCached::new(info, ctx)),
725            MatchInfo::Extern(info) => {
726                MatchInfoCached::Extern(MatchExternInfoCached::new(info, ctx))
727            }
728            MatchInfo::Value(info) => MatchInfoCached::Value(MatchEnumValueCached::new(info, ctx)),
729        }
730    }
731    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchInfo<'db> {
732        match self {
733            MatchInfoCached::Enum(info) => MatchInfo::Enum(info.embed(ctx)),
734            MatchInfoCached::Extern(info) => MatchInfo::Extern(info.embed(ctx)),
735            MatchInfoCached::Value(info) => MatchInfo::Value(info.embed(ctx)),
736        }
737    }
738}
739
740#[derive(Serialize, Deserialize)]
741struct MatchEnumInfoCached {
742    concrete_enum_id: ConcreteEnumCached,
743    /// A living variable in current scope to match on.
744    input: VarUsageCached,
745    /// Match arms. All blocks should have the same rets.
746    /// Order must be identical to the order in the definition of the enum.
747    arms: Vec<MatchArmCached>,
748    location: LocationIdCached,
749}
750impl MatchEnumInfoCached {
751    fn new<'db>(match_enum_info: MatchEnumInfo<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
752        Self {
753            concrete_enum_id: ConcreteEnumCached::new(
754                match_enum_info.concrete_enum_id,
755                &mut ctx.semantic_ctx,
756            ),
757            input: VarUsageCached::new(match_enum_info.input, ctx),
758            arms: match_enum_info
759                .arms
760                .into_iter()
761                .map(|arm| MatchArmCached::new(arm, ctx))
762                .collect(),
763            location: LocationIdCached::new(match_enum_info.location, ctx),
764        }
765    }
766    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchEnumInfo<'db> {
767        MatchEnumInfo {
768            concrete_enum_id: self
769                .concrete_enum_id
770                .get_embedded(&ctx.semantic_loading_data, ctx.db),
771            input: self.input.embed(ctx),
772            arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(),
773            location: self.location.embed(ctx),
774        }
775    }
776}
777
778#[derive(Serialize, Deserialize)]
779struct MatchExternInfoCached {
780    /// A concrete external function to call.
781    function: FunctionIdCached,
782    /// Living variables in current scope to move to the function, as arguments.
783    inputs: Vec<VarUsageCached>,
784    /// Match arms. All blocks should have the same rets.
785    /// Order must be identical to the order in the definition of the enum.
786    arms: Vec<MatchArmCached>,
787    location: LocationIdCached,
788}
789
790impl MatchExternInfoCached {
791    fn new<'db>(
792        match_extern_info: MatchExternInfo<'db>,
793        ctx: &mut CacheSavingContext<'db>,
794    ) -> Self {
795        Self {
796            function: FunctionIdCached::new(match_extern_info.function, ctx),
797            inputs: match_extern_info
798                .inputs
799                .iter()
800                .map(|var| VarUsageCached::new(*var, ctx))
801                .collect(),
802            arms: match_extern_info
803                .arms
804                .into_iter()
805                .map(|arm| MatchArmCached::new(arm, ctx))
806                .collect(),
807            location: LocationIdCached::new(match_extern_info.location, ctx),
808        }
809    }
810    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchExternInfo<'db> {
811        MatchExternInfo {
812            function: self.function.embed(ctx),
813            inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(),
814            arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(),
815            location: self.location.embed(ctx),
816        }
817    }
818}
819
820#[derive(Serialize, Deserialize)]
821struct MatchEnumValueCached {
822    num_of_arms: usize,
823
824    /// A living variable in current scope to match on.
825    input: VarUsageCached,
826    /// Match arms. All blocks should have the same rets.
827    arms: Vec<MatchArmCached>,
828    location: LocationIdCached,
829}
830
831impl MatchEnumValueCached {
832    fn new<'db>(match_enum_value: MatchEnumValue<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
833        Self {
834            num_of_arms: match_enum_value.num_of_arms,
835            input: VarUsageCached::new(match_enum_value.input, ctx),
836            arms: match_enum_value
837                .arms
838                .into_iter()
839                .map(|arm| MatchArmCached::new(arm, ctx))
840                .collect(),
841            location: LocationIdCached::new(match_enum_value.location, ctx),
842        }
843    }
844    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchEnumValue<'db> {
845        MatchEnumValue {
846            num_of_arms: self.num_of_arms,
847            input: self.input.embed(ctx),
848            arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(),
849            location: self.location.embed(ctx),
850        }
851    }
852}
853/// An arm of a match statement.
854#[derive(Serialize, Deserialize)]
855struct MatchArmCached {
856    /// The selector of the arm.
857    arm_selector: MatchArmSelectorCached,
858
859    /// The block_id where the relevant arm is implemented.
860    block_id: usize,
861
862    /// The list of variable ids introduced in this arm.
863    var_ids: Vec<usize>,
864}
865
866impl MatchArmCached {
867    fn new<'db>(match_arm: MatchArm<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
868        Self {
869            arm_selector: MatchArmSelectorCached::new(
870                match_arm.arm_selector,
871                &mut ctx.semantic_ctx,
872            ),
873            block_id: match_arm.block_id.0,
874            var_ids: match_arm.var_ids.iter().map(|var| var.index()).collect(),
875        }
876    }
877    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> MatchArm<'db> {
878        MatchArm {
879            arm_selector: self.arm_selector.get_embedded(&ctx.semantic_loading_data, ctx.db),
880            block_id: BlockId(self.block_id),
881            var_ids: self
882                .var_ids
883                .into_iter()
884                .map(|var_id| ctx.lowered_variables_id[var_id])
885                .collect(),
886        }
887    }
888}
889
890#[derive(Serialize, Deserialize)]
891enum StatementCached {
892    // Values.
893    Const(StatementConstCached),
894
895    // Flow control.
896    Call(StatementCallCached),
897
898    // Structs (including tuples).
899    StructConstruct(StatementStructConstructCached),
900    StructDestructure(StatementStructDestructureCached),
901
902    // Enums.
903    EnumConstruct(StatementEnumConstructCached),
904
905    // Boxing.
906    IntoBox(StatementIntoBoxCached),
907    Unbox(StatementUnboxCached),
908
909    Snapshot(StatementSnapshotCached),
910    Desnap(StatementDesnapCached),
911}
912
913impl StatementCached {
914    fn new<'db>(stmt: Statement<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
915        match stmt {
916            Statement::Const(stmt) => StatementCached::Const(StatementConstCached::new(stmt, ctx)),
917            Statement::Call(stmt) => StatementCached::Call(StatementCallCached::new(stmt, ctx)),
918            Statement::StructConstruct(stmt) => {
919                StatementCached::StructConstruct(StatementStructConstructCached::new(stmt, ctx))
920            }
921            Statement::StructDestructure(stmt) => {
922                StatementCached::StructDestructure(StatementStructDestructureCached::new(stmt, ctx))
923            }
924            Statement::EnumConstruct(stmt) => {
925                StatementCached::EnumConstruct(StatementEnumConstructCached::new(stmt, ctx))
926            }
927            Statement::Snapshot(stmt) => {
928                StatementCached::Snapshot(StatementSnapshotCached::new(stmt, ctx))
929            }
930            Statement::Desnap(stmt) => {
931                StatementCached::Desnap(StatementDesnapCached::new(stmt, ctx))
932            }
933            Statement::IntoBox(stmt) => {
934                StatementCached::IntoBox(StatementIntoBoxCached::new(stmt, ctx))
935            }
936            Statement::Unbox(stmt) => StatementCached::Unbox(StatementUnboxCached::new(stmt, ctx)),
937        }
938    }
939    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Statement<'db> {
940        match self {
941            StatementCached::Const(stmt) => Statement::Const(stmt.embed(ctx)),
942            StatementCached::Call(stmt) => Statement::Call(stmt.embed(ctx)),
943            StatementCached::StructConstruct(stmt) => Statement::StructConstruct(stmt.embed(ctx)),
944            StatementCached::StructDestructure(stmt) => {
945                Statement::StructDestructure(stmt.embed(ctx))
946            }
947            StatementCached::EnumConstruct(stmt) => Statement::EnumConstruct(stmt.embed(ctx)),
948            StatementCached::Snapshot(stmt) => Statement::Snapshot(stmt.embed(ctx)),
949            StatementCached::Desnap(stmt) => Statement::Desnap(stmt.embed(ctx)),
950            StatementCached::IntoBox(stmt) => Statement::IntoBox(stmt.embed(ctx)),
951            StatementCached::Unbox(stmt) => Statement::Unbox(stmt.embed(ctx)),
952        }
953    }
954}
955
956#[derive(Serialize, Deserialize)]
957struct StatementConstCached {
958    /// The value of the const.
959    value: ConstValueIdCached,
960    /// The variable to bind the value to.
961    output: usize,
962    /// Is the const boxed.
963    boxed: bool,
964}
965impl StatementConstCached {
966    fn new<'db>(stmt: StatementConst<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
967        Self {
968            value: ConstValueIdCached::new(stmt.value, &mut ctx.semantic_ctx),
969            output: stmt.output.index(),
970            boxed: stmt.boxed,
971        }
972    }
973    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementConst<'db> {
974        StatementConst::new(
975            self.value.get_embedded(&ctx.semantic_loading_data),
976            ctx.lowered_variables_id[self.output],
977            self.boxed,
978        )
979    }
980}
981
982#[derive(Serialize, Deserialize)]
983struct StatementCallCached {
984    function: FunctionIdCached,
985    inputs: Vec<VarUsageCached>,
986    with_coupon: bool,
987    outputs: Vec<usize>,
988    location: LocationIdCached,
989    is_specialization_base_call: bool,
990}
991impl StatementCallCached {
992    fn new<'db>(stmt: StatementCall<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
993        Self {
994            function: FunctionIdCached::new(stmt.function, ctx),
995            inputs: stmt.inputs.iter().map(|var| VarUsageCached::new(*var, ctx)).collect(),
996            with_coupon: stmt.with_coupon,
997            outputs: stmt.outputs.iter().map(|var| var.index()).collect(),
998            location: LocationIdCached::new(stmt.location, ctx),
999            is_specialization_base_call: stmt.is_specialization_base_call,
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            is_specialization_base_call: self.is_specialization_base_call,
1014        }
1015    }
1016}
1017
1018#[derive(Serialize, Deserialize, Clone)]
1019enum FunctionCached {
1020    /// An original function from the user code.
1021    Semantic(SemanticFunctionIdCached),
1022    /// A function generated by the compiler.
1023    Generated(GeneratedFunctionCached),
1024}
1025impl FunctionCached {
1026    fn new<'db>(function: FunctionLongId<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1027        match function {
1028            FunctionLongId::Semantic(id) => {
1029                FunctionCached::Semantic(SemanticFunctionIdCached::new(id, &mut ctx.semantic_ctx))
1030            }
1031            FunctionLongId::Generated(id) => {
1032                FunctionCached::Generated(GeneratedFunctionCached::new(id, ctx))
1033            }
1034            FunctionLongId::Specialized(_) => {
1035                unreachable!("Specialization of functions only occurs post concretization.")
1036            }
1037        }
1038    }
1039    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> FunctionId<'db> {
1040        match self {
1041            FunctionCached::Semantic(id) => {
1042                FunctionLongId::Semantic(id.get_embedded(&ctx.semantic_loading_data))
1043            }
1044            FunctionCached::Generated(id) => FunctionLongId::Generated(id.embed(ctx)),
1045        }
1046        .intern(ctx.db)
1047    }
1048}
1049
1050#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)]
1051struct FunctionIdCached(usize);
1052impl FunctionIdCached {
1053    fn new<'db>(function_id: FunctionId<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1054        if let Some(id) = ctx.function_ids.get(&function_id) {
1055            return *id;
1056        }
1057        let function = FunctionCached::new(function_id.long(ctx.db).clone(), ctx);
1058        let id = FunctionIdCached(ctx.function_ids_lookup.len());
1059        ctx.function_ids_lookup.push(function);
1060        ctx.function_ids.insert(function_id, id);
1061        id
1062    }
1063    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> FunctionId<'db> {
1064        if let Some(function_id) = ctx.function_ids.get(&self) {
1065            return *function_id;
1066        }
1067
1068        let function = ctx.function_ids_lookup[self.0].clone();
1069        let function_id = function.embed(ctx);
1070        ctx.function_ids.insert(self, function_id);
1071        function_id
1072    }
1073}
1074
1075#[derive(Serialize, Deserialize, Clone)]
1076struct GeneratedFunctionCached {
1077    parent: SemanticConcreteFunctionWithBodyCached,
1078    key: GeneratedFunctionKeyCached,
1079}
1080impl GeneratedFunctionCached {
1081    fn new<'db>(function: GeneratedFunction<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1082        Self {
1083            parent: SemanticConcreteFunctionWithBodyCached::new(
1084                function.parent,
1085                &mut ctx.semantic_ctx,
1086            ),
1087            key: GeneratedFunctionKeyCached::new(function.key, ctx),
1088        }
1089    }
1090    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> GeneratedFunction<'db> {
1091        GeneratedFunction {
1092            parent: self.parent.get_embedded(&ctx.semantic_loading_data, ctx.db),
1093            key: self.key.embed(ctx),
1094        }
1095    }
1096}
1097
1098#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq)]
1099enum GeneratedFunctionKeyCached {
1100    Loop(SyntaxStablePtrIdCached),
1101    TraitFunc(LanguageElementCached, SyntaxStablePtrIdCached),
1102}
1103
1104impl GeneratedFunctionKeyCached {
1105    fn new<'db>(key: GeneratedFunctionKey<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1106        match key {
1107            GeneratedFunctionKey::Loop(id) => GeneratedFunctionKeyCached::Loop(
1108                SyntaxStablePtrIdCached::new(id.untyped(), &mut ctx.semantic_ctx.defs_ctx),
1109            ),
1110            GeneratedFunctionKey::TraitFunc(id, stable_location) => {
1111                GeneratedFunctionKeyCached::TraitFunc(
1112                    LanguageElementCached::new(id, &mut ctx.semantic_ctx.defs_ctx),
1113                    SyntaxStablePtrIdCached::new(
1114                        stable_location.stable_ptr(),
1115                        &mut ctx.semantic_ctx.defs_ctx,
1116                    ),
1117                )
1118            }
1119        }
1120    }
1121    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> GeneratedFunctionKey<'db> {
1122        match self {
1123            GeneratedFunctionKeyCached::Loop(id) => GeneratedFunctionKey::Loop(ExprPtr(
1124                id.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1125            )),
1126            GeneratedFunctionKeyCached::TraitFunc(id, stable_location) => {
1127                let (module_id, stable_ptr) =
1128                    id.get_embedded(&ctx.semantic_loading_data.defs_loading_data);
1129                GeneratedFunctionKey::TraitFunc(
1130                    TraitFunctionLongId(module_id, TraitItemFunctionPtr(stable_ptr)).intern(ctx.db),
1131                    StableLocation::new(
1132                        stable_location.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1133                    ),
1134                )
1135            }
1136        }
1137    }
1138}
1139
1140#[derive(Serialize, Deserialize)]
1141struct StatementStructConstructCached {
1142    inputs: Vec<VarUsageCached>,
1143    /// The variable to bind the value to.
1144    output: usize,
1145}
1146impl StatementStructConstructCached {
1147    fn new<'db>(stmt: StatementStructConstruct<'db>, _ctx: &mut CacheSavingContext<'db>) -> Self {
1148        Self {
1149            inputs: stmt.inputs.iter().map(|var| VarUsageCached::new(*var, _ctx)).collect(),
1150            output: stmt.output.index(),
1151        }
1152    }
1153    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementStructConstruct<'db> {
1154        StatementStructConstruct {
1155            inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(),
1156            output: ctx.lowered_variables_id[self.output],
1157        }
1158    }
1159}
1160#[derive(Serialize, Deserialize)]
1161struct StatementStructDestructureCached {
1162    /// A living variable in current scope to destructure.
1163    input: VarUsageCached,
1164    /// The variables to bind values to.
1165    outputs: Vec<usize>,
1166}
1167impl StatementStructDestructureCached {
1168    fn new<'db>(stmt: StatementStructDestructure<'db>, _ctx: &mut CacheSavingContext<'db>) -> Self {
1169        Self {
1170            input: VarUsageCached::new(stmt.input, _ctx),
1171            outputs: stmt.outputs.iter().map(|var| var.index()).collect(),
1172        }
1173    }
1174    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementStructDestructure<'db> {
1175        StatementStructDestructure {
1176            input: self.input.embed(ctx),
1177            outputs: self
1178                .outputs
1179                .into_iter()
1180                .map(|var_id| ctx.lowered_variables_id[var_id])
1181                .collect(),
1182        }
1183    }
1184}
1185
1186#[derive(Serialize, Deserialize)]
1187struct StatementEnumConstructCached {
1188    variant: ConcreteVariantCached,
1189    /// A living variable in current scope to wrap with the variant.
1190    input: VarUsageCached,
1191    /// The variable to bind the value to.
1192    output: usize,
1193}
1194impl StatementEnumConstructCached {
1195    fn new<'db>(stmt: StatementEnumConstruct<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1196        Self {
1197            variant: ConcreteVariantCached::new(stmt.variant, &mut ctx.semantic_ctx),
1198            input: VarUsageCached::new(stmt.input, ctx),
1199            output: stmt.output.index(),
1200        }
1201    }
1202    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementEnumConstruct<'db> {
1203        StatementEnumConstruct {
1204            variant: self.variant.get_embedded(&ctx.semantic_loading_data, ctx.db),
1205            input: self.input.embed(ctx),
1206            output: ctx.lowered_variables_id[self.output],
1207        }
1208    }
1209}
1210
1211#[derive(Serialize, Deserialize)]
1212struct StatementSnapshotCached {
1213    input: VarUsageCached,
1214    outputs: [usize; 2],
1215}
1216impl StatementSnapshotCached {
1217    fn new<'db>(stmt: StatementSnapshot<'db>, _ctx: &mut CacheSavingContext<'db>) -> Self {
1218        Self {
1219            input: VarUsageCached::new(stmt.input, _ctx),
1220            outputs: stmt.outputs.map(|var| var.index()),
1221        }
1222    }
1223    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementSnapshot<'db> {
1224        StatementSnapshot {
1225            input: self.input.embed(ctx),
1226            outputs: [
1227                ctx.lowered_variables_id[self.outputs[0]],
1228                ctx.lowered_variables_id[self.outputs[1]],
1229            ],
1230        }
1231    }
1232}
1233
1234#[derive(Serialize, Deserialize)]
1235struct StatementDesnapCached {
1236    input: VarUsageCached,
1237    /// The variable to bind the value to.
1238    output: usize,
1239}
1240impl StatementDesnapCached {
1241    fn new<'db>(stmt: StatementDesnap<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1242        Self { input: VarUsageCached::new(stmt.input, ctx), output: stmt.output.index() }
1243    }
1244    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementDesnap<'db> {
1245        StatementDesnap {
1246            input: self.input.embed(ctx),
1247            output: ctx.lowered_variables_id[self.output],
1248        }
1249    }
1250}
1251
1252#[derive(Serialize, Deserialize)]
1253struct StatementIntoBoxCached {
1254    input: VarUsageCached,
1255    output: usize,
1256}
1257impl StatementIntoBoxCached {
1258    fn new<'db>(stmt: StatementIntoBox<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1259        Self { input: VarUsageCached::new(stmt.input, ctx), output: stmt.output.index() }
1260    }
1261    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementIntoBox<'db> {
1262        StatementIntoBox {
1263            input: self.input.embed(ctx),
1264            output: ctx.lowered_variables_id[self.output],
1265        }
1266    }
1267}
1268
1269#[derive(Serialize, Deserialize)]
1270struct StatementUnboxCached {
1271    input: VarUsageCached,
1272    output: usize,
1273}
1274impl StatementUnboxCached {
1275    fn new<'db>(stmt: StatementUnbox<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1276        Self { input: VarUsageCached::new(stmt.input, ctx), output: stmt.output.index() }
1277    }
1278    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> StatementUnbox<'db> {
1279        StatementUnbox {
1280            input: self.input.embed(ctx),
1281            output: ctx.lowered_variables_id[self.output],
1282        }
1283    }
1284}
1285
1286#[derive(Serialize, Deserialize, Clone)]
1287struct LocationCached {
1288    /// The stable location of the object.
1289    stable_location: SyntaxStablePtrIdCached,
1290    /// Function call locations where this value was inlined from.
1291    inline_locations: Vec<SyntaxStablePtrIdCached>,
1292}
1293impl LocationCached {
1294    fn new<'db>(location: Location<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1295        Self {
1296            stable_location: SyntaxStablePtrIdCached::new(
1297                location.stable_location.stable_ptr(),
1298                &mut ctx.semantic_ctx.defs_ctx,
1299            ),
1300            inline_locations: location
1301                .inline_locations
1302                .iter()
1303                .map(|loc| {
1304                    SyntaxStablePtrIdCached::new(loc.stable_ptr(), &mut ctx.semantic_ctx.defs_ctx)
1305                })
1306                .collect(),
1307        }
1308    }
1309    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> Location<'db> {
1310        Location {
1311            stable_location: StableLocation::new(
1312                self.stable_location.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1313            ),
1314            inline_locations: self
1315                .inline_locations
1316                .into_iter()
1317                .map(|loc| {
1318                    StableLocation::new(
1319                        loc.get_embedded(&ctx.semantic_loading_data.defs_loading_data),
1320                    )
1321                })
1322                .collect(),
1323            notes: Default::default(),
1324        }
1325    }
1326}
1327
1328#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)]
1329struct LocationIdCached(usize);
1330
1331impl LocationIdCached {
1332    fn new<'db>(location_id: LocationId<'db>, ctx: &mut CacheSavingContext<'db>) -> Self {
1333        if let Some(id) = ctx.location_ids.get(&location_id) {
1334            return *id;
1335        }
1336        let location = LocationCached::new(location_id.long(ctx.db).clone(), ctx);
1337        let id = LocationIdCached(ctx.location_ids_lookup.len());
1338        ctx.location_ids_lookup.push(location);
1339        ctx.location_ids.insert(location_id, id);
1340        id
1341    }
1342    fn embed<'db>(self, ctx: &mut CacheLoadingContext<'db>) -> LocationId<'db> {
1343        if let Some(location_id) = ctx.location_ids.get(&self) {
1344            return *location_id;
1345        }
1346        let location = ctx.location_ids_lookup[self.0].clone();
1347        let location = location.embed(ctx).intern(ctx.db);
1348        ctx.location_ids.insert(self, location);
1349        location
1350    }
1351}