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