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