diff --git a/Cargo.lock b/Cargo.lock index 7b3f34b6d64..4d77f20b0a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "2.0.0-rc.3" @@ -530,7 +539,7 @@ name = "cairo-execute" version = "2.9.2" dependencies = [ "anyhow", - "bincode", + "bincode 2.0.0-rc.3", "cairo-lang-compiler", "cairo-lang-executable", "cairo-lang-runner", @@ -743,6 +752,7 @@ dependencies = [ name = "cairo-lang-lowering" version = "2.9.2" dependencies = [ + "bincode 1.3.3", "cairo-lang-debug", "cairo-lang-defs", "cairo-lang-diagnostics", @@ -764,6 +774,8 @@ dependencies = [ "num-traits", "pretty_assertions", "rust-analyzer-salsa", + "serde", + "smol_str", "test-log", ] @@ -1124,6 +1136,7 @@ dependencies = [ "num-traits", "pretty_assertions", "rust-analyzer-salsa", + "serde", "smol_str", "test-log", "unescaper", @@ -1252,7 +1265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fa8b4b56ee66cebcade4d85128e55b2bfdf046502187aeaa8c2768a427684dc" dependencies = [ "anyhow", - "bincode", + "bincode 2.0.0-rc.3", "bitvec", "clap", "generic-array", diff --git a/Cargo.toml b/Cargo.toml index cb2fd983a94..d80761ed6f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ ark-secp256r1 = "0.5.0" ark-std = "0.5.0" assert_matches = "1.5" bimap = "0.6.3" +bincode = "1.3.3" cairo-vm = { version = "1.0.2", features = ["mod_builtin"] } clap = { version = "4.5.27", features = ["derive"] } colored = "3.0.0" diff --git a/crates/cairo-lang-compiler/src/project.rs b/crates/cairo-lang-compiler/src/project.rs index 63948333497..eeeefe02548 100644 --- a/crates/cairo-lang-compiler/src/project.rs +++ b/crates/cairo-lang-compiler/src/project.rs @@ -85,7 +85,7 @@ pub fn update_crate_root( let (crate_id, crate_settings) = get_crate_id_and_settings(db, crate_identifier, config); db.set_crate_config( crate_id, - Some(CrateConfiguration { root, settings: crate_settings.clone() }), + Some(CrateConfiguration { root, settings: crate_settings.clone(), cache_file: None }), ); } diff --git a/crates/cairo-lang-defs/src/diagnostic_utils.rs b/crates/cairo-lang-defs/src/diagnostic_utils.rs index a4a1c0b73ad..fe3bb048348 100644 --- a/crates/cairo-lang-defs/src/diagnostic_utils.rs +++ b/crates/cairo-lang-defs/src/diagnostic_utils.rs @@ -30,6 +30,11 @@ impl StableLocation { self.0.lookup(db.upcast()) } + /// Returns the [SyntaxStablePtrId] of the [StableLocation]. + pub fn stable_ptr(&self) -> SyntaxStablePtrId { + self.0 + } + /// Returns the [DiagnosticLocation] that corresponds to the [StableLocation]. pub fn diagnostic_location(&self, db: &dyn DefsGroup) -> DiagnosticLocation { let syntax_node = self.syntax_node(db); diff --git a/crates/cairo-lang-filesystem/src/db.rs b/crates/cairo-lang-filesystem/src/db.rs index 72d894eac40..7cf5be60130 100644 --- a/crates/cairo-lang-filesystem/src/db.rs +++ b/crates/cairo-lang-filesystem/src/db.rs @@ -13,8 +13,8 @@ use smol_str::{SmolStr, ToSmolStr}; use crate::cfg::CfgSet; use crate::flag::Flag; use crate::ids::{ - CodeMapping, CodeOrigin, CrateId, CrateLongId, Directory, FileId, FileLongId, FlagId, - FlagLongId, VirtualFile, + BlobId, BlobLongId, CodeMapping, CodeOrigin, CrateId, CrateLongId, Directory, FileId, + FileLongId, FlagId, FlagLongId, VirtualFile, }; use crate::span::{FileSummary, TextOffset, TextSpan, TextWidth}; @@ -50,11 +50,12 @@ pub struct CrateConfiguration { /// The root directory of the crate. pub root: Directory, pub settings: CrateSettings, + pub cache_file: Option, } impl CrateConfiguration { /// Returns a new configuration. pub fn default_for_root(root: Directory) -> Self { - Self { root, settings: CrateSettings::default() } + Self { root, settings: CrateSettings::default(), cache_file: None } } } @@ -182,6 +183,8 @@ pub trait FilesGroup: ExternalFiles { #[salsa::interned] fn intern_file(&self, file: FileLongId) -> FileId; #[salsa::interned] + fn intern_blob(&self, blob: BlobLongId) -> BlobId; + #[salsa::interned] fn intern_flag(&self, flag: FlagLongId) -> FlagId; /// Main input of the project. Lists all the crates configurations. @@ -215,6 +218,8 @@ pub trait FilesGroup: ExternalFiles { fn file_content(&self, file_id: FileId) -> Option>; fn file_summary(&self, file_id: FileId) -> Option>; + /// Query for the blob content. + fn blob_content(&self, blob_id: BlobId) -> Option>; /// Query to get a compilation flag by its ID. fn get_flag(&self, id: FlagId) -> Option>; } @@ -244,6 +249,7 @@ pub fn init_dev_corelib(db: &mut (dyn FilesGroup + 'static), core_lib_dir: PathB coupons: true, }, }, + cache_file: None, }), ); } @@ -302,13 +308,17 @@ fn crates(db: &dyn FilesGroup) -> Vec { fn crate_config(db: &dyn FilesGroup, crt: CrateId) -> Option { match crt.lookup_intern(db) { CrateLongId::Real { .. } => db.crate_configs().get(&crt).cloned(), - CrateLongId::Virtual { name: _, file_id, settings } => Some(CrateConfiguration { - root: Directory::Virtual { - files: BTreeMap::from([("lib.cairo".into(), file_id)]), - dirs: Default::default(), - }, - settings: toml::from_str(&settings).expect("Failed to parse virtual crate settings."), - }), + CrateLongId::Virtual { name: _, file_id, settings, cache_file } => { + Some(CrateConfiguration { + root: Directory::Virtual { + files: BTreeMap::from([("lib.cairo".into(), file_id)]), + dirs: Default::default(), + }, + settings: toml::from_str(&settings) + .expect("Failed to parse virtual crate settings."), + cache_file, + }) + } } } @@ -348,6 +358,22 @@ fn get_flag(db: &dyn FilesGroup, id: FlagId) -> Option> { db.flags().get(&id).cloned() } +fn blob_content(db: &dyn FilesGroup, blob: BlobId) -> Option> { + match blob.lookup_intern(db) { + BlobLongId::OnDisk(path) => { + // This does not result in performance cost due to OS caching and the fact that salsa + // will re-execute only this single query if the file content did not change. + db.salsa_runtime().report_synthetic_read(Durability::LOW); + + match fs::read(path) { + Ok(content) => Some(content.into()), + Err(_) => None, + } + } + BlobLongId::Virtual(content) => Some(content), + } +} + /// Returns the location of the originating user code. pub fn get_originating_location( db: &dyn FilesGroup, diff --git a/crates/cairo-lang-filesystem/src/ids.rs b/crates/cairo-lang-filesystem/src/ids.rs index 381cc41f0ed..dcfb4bfd342 100644 --- a/crates/cairo-lang-filesystem/src/ids.rs +++ b/crates/cairo-lang-filesystem/src/ids.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use cairo_lang_utils::{Intern, LookupIntern, define_short_id}; use path_clean::PathClean; +use serde::{Deserialize, Serialize}; use smol_str::SmolStr; use crate::db::{CORELIB_CRATE_NAME, FilesGroup}; @@ -17,7 +18,7 @@ pub enum CrateLongId { /// A crate that appears in crate_roots(), and on the filesystem. Real { name: SmolStr, discriminator: Option }, /// A virtual crate, not a part of the crate_roots(). Used mainly for tests. - Virtual { name: SmolStr, file_id: FileId, settings: String }, + Virtual { name: SmolStr, file_id: FileId, settings: String, cache_file: Option }, } impl CrateLongId { pub fn name(&self) -> SmolStr { @@ -78,14 +79,14 @@ pub enum FileLongId { External(salsa::InternId), } /// Whether the file holds syntax for a module or for an expression. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum FileKind { Module, Expr, } /// A mapping for a code rewrite. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct CodeMapping { pub span: TextSpan, pub origin: CodeOrigin, @@ -108,7 +109,7 @@ impl CodeMapping { } /// The origin of a code mapping. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum CodeOrigin { /// The origin is a copied node staring at the given offset. Start(TextOffset), @@ -214,3 +215,18 @@ impl Directory { } } } + +/// A FileId for data that is not necessarily a valid UTF-8 string. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum BlobLongId { + OnDisk(PathBuf), + Virtual(Arc<[u8]>), +} + +define_short_id!(BlobId, BlobLongId, FilesGroup, lookup_intern_blob, intern_blob); + +impl BlobId { + pub fn new(db: &dyn FilesGroup, path: PathBuf) -> BlobId { + BlobLongId::OnDisk(path.clean()).intern(db) + } +} diff --git a/crates/cairo-lang-lowering/Cargo.toml b/crates/cairo-lang-lowering/Cargo.toml index a1a5f3c9105..278f75a850d 100644 --- a/crates/cairo-lang-lowering/Cargo.toml +++ b/crates/cairo-lang-lowering/Cargo.toml @@ -7,6 +7,7 @@ license-file.workspace = true description = "Cairo lowering phase." [dependencies] +bincode.workspace = true cairo-lang-debug = { path = "../cairo-lang-debug", version = "~2.9.2" } cairo-lang-defs = { path = "../cairo-lang-defs", version = "~2.9.2" } cairo-lang-diagnostics = { path = "../cairo-lang-diagnostics", version = "~2.9.2" } @@ -23,6 +24,8 @@ num-bigint = { workspace = true, default-features = true } num-integer = { workspace = true, default-features = true } num-traits = { workspace = true, default-features = true } salsa.workspace = true +serde = { workspace = true, default-features = true } +smol_str.workspace = true [dev-dependencies] cairo-lang-plugins = { path = "../cairo-lang-plugins" } diff --git a/crates/cairo-lang-lowering/src/cache/mod.rs b/crates/cairo-lang-lowering/src/cache/mod.rs new file mode 100644 index 00000000000..86262c980e4 --- /dev/null +++ b/crates/cairo-lang-lowering/src/cache/mod.rs @@ -0,0 +1,2741 @@ +#[cfg(test)] +#[path = "test.rs"] +mod test; + +use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; +use std::sync::Arc; + +use cairo_lang_debug::DebugWithDb; +use cairo_lang_defs::diagnostic_utils::StableLocation; +use cairo_lang_defs::ids::{ + EnumLongId, ExternFunctionLongId, ExternTypeLongId, FileIndex, FreeFunctionLongId, + FunctionWithBodyId, GenericParamId, GenericParamLongId, ImplDefId, ImplDefLongId, + ImplFunctionLongId, LanguageElementId, LocalVarId, LocalVarLongId, MemberLongId, ModuleFileId, + ModuleId, ParamLongId, PluginGeneratedFileId, PluginGeneratedFileLongId, StatementConstLongId, + StatementItemId, StatementUseLongId, StructLongId, SubmoduleId, SubmoduleLongId, + TraitConstantId, TraitConstantLongId, TraitFunctionLongId, TraitTypeId, TraitTypeLongId, + VariantLongId, +}; +use cairo_lang_diagnostics::{Maybe, skip_diagnostic}; +use cairo_lang_filesystem::ids::{ + CodeMapping, CrateId, CrateLongId, FileId, FileKind, FileLongId, VirtualFile, +}; +use cairo_lang_filesystem::span::TextWidth; +use cairo_lang_semantic::db::SemanticGroup; +use cairo_lang_semantic::expr::inference::InferenceError; +use cairo_lang_semantic::items::constant::{ConstValue, ImplConstantId}; +use cairo_lang_semantic::items::functions::{ + ConcreteFunctionWithBody, GenericFunctionId, GenericFunctionWithBodyId, ImplFunctionBodyId, + ImplGenericFunctionId, ImplGenericFunctionWithBodyId, +}; +use cairo_lang_semantic::items::imp::{ImplId, ImplLongId}; +use cairo_lang_semantic::types::{ + ConcreteEnumLongId, ConcreteExternTypeLongId, ConcreteStructLongId, ImplTypeId, +}; +use cairo_lang_semantic::{ + ConcreteFunction, ConcreteImplLongId, MatchArmSelector, TypeId, TypeLongId, ValueSelectorArm, +}; +use cairo_lang_syntax::node::TypedStablePtr; +use cairo_lang_syntax::node::ast::{ + ExprPtr, FunctionWithBodyPtr, GenericParamPtr, ItemConstantPtr, ItemEnumPtr, + ItemExternFunctionPtr, ItemExternTypePtr, ItemImplPtr, ItemModulePtr, ItemStructPtr, MemberPtr, + ParamPtr, TerminalIdentifierPtr, TraitItemConstantPtr, TraitItemFunctionPtr, TraitItemTypePtr, + UsePathLeafPtr, VariantPtr, +}; +use cairo_lang_syntax::node::green::{GreenNode, GreenNodeDetails}; +use cairo_lang_syntax::node::ids::{GreenId, SyntaxStablePtrId}; +use cairo_lang_syntax::node::kind::SyntaxKind; +use cairo_lang_syntax::node::stable_ptr::SyntaxStablePtr; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use cairo_lang_utils::{Intern, LookupIntern}; +use id_arena::Arena; +use num_bigint::BigInt; +use salsa::InternKey; +use serde::{Deserialize, Serialize}; +use smol_str::SmolStr; +use {cairo_lang_defs as defs, cairo_lang_semantic as semantic}; + +use crate::blocks::FlatBlocksBuilder; +use crate::db::LoweringGroup; +use crate::ids::{ + FunctionId, FunctionLongId, GeneratedFunction, GeneratedFunctionKey, LocationId, Signature, +}; +use crate::lower::MultiLowering; +use crate::objects::{ + BlockId, MatchExternInfo, Statement, StatementCall, StatementConst, StatementStructDestructure, + VariableId, +}; +use crate::{ + FlatBlock, FlatBlockEnd, FlatLowered, Location, MatchArm, MatchEnumInfo, MatchEnumValue, + MatchInfo, StatementDesnap, StatementEnumConstruct, StatementSnapshot, + StatementStructConstruct, VarRemapping, VarUsage, Variable, +}; + +/// Load the cached lowering of a crate if it has a cache file configuration. +pub fn load_cached_crate_functions( + db: &dyn LoweringGroup, + crate_id: CrateId, +) -> Option>> { + let blob_id = db.crate_config(crate_id)?.cache_file?; + let Some(content) = db.blob_content(blob_id) else { + return Default::default(); + }; + let (lookups, semantic_lookups, lowerings): ( + CacheLookups, + SemanticCacheLookups, + Vec<(DefsFunctionWithBodyIdCached, MultiLoweringCached)>, + ) = bincode::deserialize(&content).unwrap_or_default(); + // TODO(tomer): Fail on version, cfg, and dependencies mismatch. + let mut ctx = CacheLoadingContext::new(db, lookups, semantic_lookups, crate_id); + + Some( + lowerings + .into_iter() + .map(|(function_id, lowering)| { + let function_id = function_id.embed(&mut ctx.semantic_ctx); + + let lowering = lowering.embed(&mut ctx); + (function_id, lowering) + }) + .collect::>() + .into(), + ) +} + +/// Cache the lowering of each function in the crate into a blob. +pub fn generate_crate_cache( + db: &dyn LoweringGroup, + crate_id: cairo_lang_filesystem::ids::CrateId, +) -> Maybe> { + let modules = db.crate_modules(crate_id); + let mut function_ids = Vec::new(); + for module_id in modules.iter() { + for free_func in db.module_free_functions_ids(*module_id)?.iter() { + function_ids.push(FunctionWithBodyId::Free(*free_func)); + } + for impl_id in db.module_impls_ids(*module_id)?.iter() { + for impl_func in db.impl_functions(*impl_id)?.values() { + function_ids.push(FunctionWithBodyId::Impl(*impl_func)); + } + } + } + + let mut ctx = CacheSavingContext::new(db, crate_id); + let cached = function_ids + .iter() + .map(|id| { + let multi = db.priv_function_with_body_multi_lowering(*id)?; + Ok(( + DefsFunctionWithBodyIdCached::new(*id, &mut ctx.semantic_ctx), + MultiLoweringCached::new((*multi).clone(), &mut ctx), + )) + }) + .collect::>>()?; + + let artifact = if let Ok(lowered) = + bincode::serialize(&(&ctx.lookups, &ctx.semantic_ctx.lookups, cached)) + { + lowered + } else { + "".into() + }; + Ok(Arc::from(artifact.as_slice())) +} + +/// Context for loading cache into the database. +struct CacheLoadingContext<'db> { + /// The variable ids of the flat lowered that is currently being loaded. + flat_lowered_variables_id: Vec, + db: &'db dyn LoweringGroup, + + /// data for loading the entire cache into the database. + data: CacheLoadingData, + + semantic_ctx: SemanticCacheLoadingContext<'db>, +} + +impl<'db> CacheLoadingContext<'db> { + fn new( + db: &'db dyn LoweringGroup, + lookups: CacheLookups, + semantic_lookups: SemanticCacheLookups, + self_crate_id: CrateId, + ) -> Self { + Self { + flat_lowered_variables_id: Vec::new(), + db, + data: CacheLoadingData { + function_ids: OrderedHashMap::default(), + location_ids: OrderedHashMap::default(), + lookups, + }, + semantic_ctx: SemanticCacheLoadingContext::<'db> { + db: db.upcast(), + data: SemanticCacheLoadingData::new(semantic_lookups, self_crate_id), + }, + } + } +} + +impl Deref for CacheLoadingContext<'_> { + type Target = CacheLoadingData; + + fn deref(&self) -> &Self::Target { + &self.data + } +} +impl DerefMut for CacheLoadingContext<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + +/// Data for loading cache into the database. +struct CacheLoadingData { + function_ids: OrderedHashMap, + location_ids: OrderedHashMap, + lookups: CacheLookups, +} +impl Deref for CacheLoadingData { + type Target = CacheLookups; + + fn deref(&self) -> &Self::Target { + &self.lookups + } +} +impl DerefMut for CacheLoadingData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.lookups + } +} + +/// Context for saving cache from the database. +struct CacheSavingContext<'db> { + db: &'db dyn LoweringGroup, + data: CacheSavingData, + semantic_ctx: SemanticCacheSavingContext<'db>, +} +impl Deref for CacheSavingContext<'_> { + type Target = CacheSavingData; + + fn deref(&self) -> &Self::Target { + &self.data + } +} +impl DerefMut for CacheSavingContext<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} +impl<'db> CacheSavingContext<'db> { + fn new(db: &'db dyn LoweringGroup, self_crate_id: CrateId) -> Self { + Self { + db, + data: CacheSavingData::default(), + semantic_ctx: SemanticCacheSavingContext { + db: db.upcast(), + data: SemanticCacheSavingData::default(), + self_crate_id, + }, + } + } +} + +/// Data for saving cache from the database. +#[derive(Default)] +struct CacheSavingData { + function_ids: OrderedHashMap, + location_ids: OrderedHashMap, + lookups: CacheLookups, +} +impl Deref for CacheSavingData { + type Target = CacheLookups; + + fn deref(&self) -> &Self::Target { + &self.lookups + } +} +impl DerefMut for CacheSavingData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.lookups + } +} + +/// Saved interned items for the cache. +#[derive(Serialize, Deserialize, Default)] +struct CacheLookups { + function_ids_lookup: Vec, + location_ids_lookup: Vec, +} + +/// Context for loading cache into the database. +struct SemanticCacheLoadingContext<'db> { + db: &'db dyn SemanticGroup, + data: SemanticCacheLoadingData, +} + +impl Deref for SemanticCacheLoadingContext<'_> { + type Target = SemanticCacheLoadingData; + + fn deref(&self) -> &Self::Target { + &self.data + } +} +impl DerefMut for SemanticCacheLoadingContext<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + +/// Data for loading cache into the database. +struct SemanticCacheLoadingData { + function_ids: OrderedHashMap, + type_ids: OrderedHashMap, + impl_ids: OrderedHashMap, + green_ids: OrderedHashMap, + syntax_stable_ptr_ids: OrderedHashMap, + crate_ids: OrderedHashMap, + submodule_ids: OrderedHashMap, + file_ids: OrderedHashMap, + self_crate_id: CrateId, + lookups: SemanticCacheLookups, +} + +impl SemanticCacheLoadingData { + fn new(lookups: SemanticCacheLookups, self_crate_id: CrateId) -> Self { + Self { + function_ids: OrderedHashMap::default(), + type_ids: OrderedHashMap::default(), + impl_ids: OrderedHashMap::default(), + green_ids: OrderedHashMap::default(), + syntax_stable_ptr_ids: OrderedHashMap::default(), + crate_ids: OrderedHashMap::default(), + submodule_ids: OrderedHashMap::default(), + file_ids: OrderedHashMap::default(), + self_crate_id, + lookups, + } + } +} + +impl Deref for SemanticCacheLoadingData { + type Target = SemanticCacheLookups; + + fn deref(&self) -> &Self::Target { + &self.lookups + } +} +impl DerefMut for SemanticCacheLoadingData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.lookups + } +} + +/// Context for saving cache from the database. +struct SemanticCacheSavingContext<'db> { + db: &'db dyn SemanticGroup, + data: SemanticCacheSavingData, + self_crate_id: CrateId, +} +impl Deref for SemanticCacheSavingContext<'_> { + type Target = SemanticCacheSavingData; + + fn deref(&self) -> &Self::Target { + &self.data + } +} +impl DerefMut for SemanticCacheSavingContext<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + +/// Data for saving cache from the database. +#[derive(Default)] +struct SemanticCacheSavingData { + function_ids: OrderedHashMap, + + type_ids: OrderedHashMap, + + impl_ids: OrderedHashMap, + + green_ids: OrderedHashMap, + crate_ids: OrderedHashMap, + submodule_ids: OrderedHashMap, + + syntax_stable_ptr_ids: OrderedHashMap, + file_ids: OrderedHashMap, + + lookups: SemanticCacheLookups, +} + +impl Deref for SemanticCacheSavingData { + type Target = SemanticCacheLookups; + + fn deref(&self) -> &Self::Target { + &self.lookups + } +} +impl DerefMut for SemanticCacheSavingData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.lookups + } +} + +/// Saved interned items for the cache. +#[derive(Serialize, Deserialize, Default)] +struct SemanticCacheLookups { + function_ids_lookup: Vec, + type_ids_lookup: Vec, + impl_ids_lookup: Vec, + green_ids_lookup: Vec, + crate_ids_lookup: Vec, + syntax_stable_ptr_ids_lookup: Vec, + submodule_ids_lookup: Vec, + file_ids_lookup: Vec, +} + +/// Cached version of [defs::ids::FunctionWithBodyId] +/// used as a key in the cache for the [MultiLowering] struct. +#[derive(Serialize, Deserialize, Hash, Eq, PartialEq)] +enum DefsFunctionWithBodyIdCached { + Free(LanguageElementCached), + Impl(LanguageElementCached), + Trait(LanguageElementCached), +} +impl DefsFunctionWithBodyIdCached { + fn new(id: defs::ids::FunctionWithBodyId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match id { + defs::ids::FunctionWithBodyId::Free(id) => { + DefsFunctionWithBodyIdCached::Free(LanguageElementCached::new(id, ctx)) + } + defs::ids::FunctionWithBodyId::Impl(id) => { + DefsFunctionWithBodyIdCached::Impl(LanguageElementCached::new(id, ctx)) + } + defs::ids::FunctionWithBodyId::Trait(id) => { + DefsFunctionWithBodyIdCached::Trait(LanguageElementCached::new(id, ctx)) + } + } + } + + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> defs::ids::FunctionWithBodyId { + match self { + DefsFunctionWithBodyIdCached::Free(id) => { + let (moddule_file_id, function_stable_ptr) = id.embed(ctx); + defs::ids::FunctionWithBodyId::Free( + FreeFunctionLongId(moddule_file_id, FunctionWithBodyPtr(function_stable_ptr)) + .intern(ctx.db), + ) + } + DefsFunctionWithBodyIdCached::Impl(id) => { + let (moddule_file_id, function_stable_ptr) = id.embed(ctx); + defs::ids::FunctionWithBodyId::Impl( + ImplFunctionLongId(moddule_file_id, FunctionWithBodyPtr(function_stable_ptr)) + .intern(ctx.db), + ) + } + DefsFunctionWithBodyIdCached::Trait(id) => { + let (moddule_file_id, function_stable_ptr) = id.embed(ctx); + defs::ids::FunctionWithBodyId::Trait( + TraitFunctionLongId(moddule_file_id, TraitItemFunctionPtr(function_stable_ptr)) + .intern(ctx.db), + ) + } + } + } +} + +/// Cached version of [MultiLowering]. +#[derive(Serialize, Deserialize)] +struct MultiLoweringCached { + main_lowering: FlatLoweredCached, + generated_lowerings: Vec<(GeneratedFunctionKeyCached, FlatLoweredCached)>, +} +impl MultiLoweringCached { + fn new(lowering: MultiLowering, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + main_lowering: FlatLoweredCached::new(lowering.main_lowering, ctx), + generated_lowerings: lowering + .generated_lowerings + .into_iter() + .map(|(key, flat_lowered)| { + ( + GeneratedFunctionKeyCached::new(key, ctx), + FlatLoweredCached::new(flat_lowered, ctx), + ) + }) + .collect(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> MultiLowering { + MultiLowering { + main_lowering: self.main_lowering.embed(ctx), + generated_lowerings: self + .generated_lowerings + .into_iter() + .map(|(key, flat_lowered)| (key.embed(ctx), flat_lowered.embed(ctx))) + .collect(), + } + } +} + +#[derive(Serialize, Deserialize)] +struct FlatLoweredCached { + /// Function signature. + signature: SignatureCached, + /// Arena of allocated lowered variables. + variables: Vec, + /// Arena of allocated lowered blocks. + blocks: Vec, + /// function parameters, including implicits. + parameters: Vec, +} +impl FlatLoweredCached { + fn new(flat_lowered: FlatLowered, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + signature: SignatureCached::new(flat_lowered.signature, ctx), + variables: flat_lowered + .variables + .into_iter() + .map(|var| VariableCached::new(var.1, ctx)) + .collect(), + blocks: flat_lowered + .blocks + .into_iter() + .map(|block: (BlockId, &FlatBlock)| FlatBlockCached::new(block.1.clone(), ctx)) + .collect(), + parameters: flat_lowered.parameters.iter().map(|var| var.index()).collect(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> FlatLowered { + ctx.flat_lowered_variables_id.clear(); + let mut variables = Arena::new(); + for var in self.variables { + let id = variables.alloc(var.embed(ctx)); + ctx.flat_lowered_variables_id.push(id); + } + + let mut blocks = FlatBlocksBuilder::new(); + for block in self.blocks { + blocks.alloc(block.embed(ctx)); + } + FlatLowered { + diagnostics: Default::default(), + signature: self.signature.embed(ctx), + variables, + blocks: blocks.build().unwrap(), + parameters: self + .parameters + .into_iter() + .map(|var_id| ctx.flat_lowered_variables_id[var_id]) + .collect(), + } + } +} + +#[derive(Serialize, Deserialize)] +struct SignatureCached { + /// Function parameters. + params: Vec, + /// Extra return values. + extra_rets: Vec, + /// Return type. + return_type: TypeIdCached, + /// Implicit parameters. + implicits: Vec, + /// Whether the function is panicable. + panicable: bool, + location: LocationIdCached, +} +impl SignatureCached { + fn new(signature: Signature, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + params: signature + .params + .into_iter() + .map(|var| ExprVarMemberPathCached::new(var, &mut ctx.semantic_ctx)) + .collect(), + extra_rets: signature + .extra_rets + .into_iter() + .map(|var| ExprVarMemberPathCached::new(var, &mut ctx.semantic_ctx)) + .collect(), + + return_type: TypeIdCached::new(signature.return_type, &mut ctx.semantic_ctx), + implicits: signature + .implicits + .into_iter() + .map(|ty| TypeIdCached::new(ty, &mut ctx.semantic_ctx)) + .collect(), + panicable: signature.panicable, + location: LocationIdCached::new(signature.location, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> Signature { + Signature { + params: self.params.into_iter().map(|var| var.embed(&mut ctx.semantic_ctx)).collect(), + extra_rets: self + .extra_rets + .into_iter() + .map(|var| var.embed(&mut ctx.semantic_ctx)) + .collect(), + return_type: self.return_type.embed(&mut ctx.semantic_ctx), + implicits: self + .implicits + .into_iter() + .map(|ty| ty.embed(&mut ctx.semantic_ctx)) + .collect(), + panicable: self.panicable, + location: self.location.embed(ctx), + } + } +} + +#[derive(Serialize, Deserialize)] +enum ExprVarMemberPathCached { + Var(ExprVarCached), + Member { + parent: Box, + member_id: LanguageElementCached, + concrete_struct_id: ConcreteStructCached, + stable_ptr: SyntaxStablePtrIdCached, + ty: TypeIdCached, + }, +} +impl ExprVarMemberPathCached { + fn new( + expr_var_member_path: semantic::ExprVarMemberPath, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + match expr_var_member_path { + semantic::ExprVarMemberPath::Var(var) => { + ExprVarMemberPathCached::Var(ExprVarCached::new(var, ctx)) + } + semantic::ExprVarMemberPath::Member { + parent, + member_id, + concrete_struct_id, + stable_ptr, + ty, + } => ExprVarMemberPathCached::Member { + parent: Box::new(ExprVarMemberPathCached::new(*parent, ctx)), + member_id: LanguageElementCached::new(member_id, ctx), + concrete_struct_id: ConcreteStructCached::new(concrete_struct_id, ctx), + stable_ptr: SyntaxStablePtrIdCached::new(stable_ptr.untyped(), ctx), + ty: TypeIdCached::new(ty, ctx), + }, + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ExprVarMemberPath { + match self { + ExprVarMemberPathCached::Var(var) => semantic::ExprVarMemberPath::Var(var.embed(ctx)), + ExprVarMemberPathCached::Member { + parent, + member_id, + concrete_struct_id, + stable_ptr, + ty, + } => { + let parent = Box::new(parent.embed(ctx)); + let (moddule_file_id, member_stable_ptr) = member_id.embed(ctx); + let member_id = + MemberLongId(moddule_file_id, MemberPtr(member_stable_ptr)).intern(ctx.db); + semantic::ExprVarMemberPath::Member { + parent, + member_id, + concrete_struct_id: concrete_struct_id.embed(ctx), + stable_ptr: ExprPtr(stable_ptr.embed(ctx)), + ty: ty.embed(ctx), + } + } + } + } +} + +#[derive(Serialize, Deserialize)] +struct ExprVarCached { + var: SemanticVarIdCached, + /// Variable type. + ty: TypeIdCached, + stable_ptr: SyntaxStablePtrIdCached, +} +impl ExprVarCached { + fn new(expr_var: semantic::ExprVar, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { + var: SemanticVarIdCached::new(expr_var.var, ctx), + ty: TypeIdCached::new(expr_var.ty, ctx), + stable_ptr: SyntaxStablePtrIdCached::new(expr_var.stable_ptr.untyped(), ctx), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ExprVar { + semantic::ExprVar { + var: self.var.embed(ctx), + ty: self.ty.embed(ctx), + stable_ptr: ExprPtr(self.stable_ptr.embed(ctx)), + } + } +} + +#[derive(Serialize, Deserialize)] +enum SemanticVarIdCached { + Param(SemanticParamIdCached), + Local(SemanticLocalVarIdCached), + Item(SemanticStatementItemIdCached), +} +impl SemanticVarIdCached { + fn new(var_id: semantic::VarId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match var_id { + semantic::VarId::Param(id) => { + SemanticVarIdCached::Param(SemanticParamIdCached::new(id, ctx)) + } + semantic::VarId::Local(id) => { + SemanticVarIdCached::Local(SemanticLocalVarIdCached::new(id, ctx)) + } + semantic::VarId::Item(id) => { + SemanticVarIdCached::Item(SemanticStatementItemIdCached::new(id, ctx)) + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::VarId { + match self { + SemanticVarIdCached::Param(id) => semantic::VarId::Param(id.embed(ctx)), + SemanticVarIdCached::Local(id) => semantic::VarId::Local(id.embed(ctx)), + SemanticVarIdCached::Item(id) => semantic::VarId::Item(id.embed(ctx)), + } + } +} + +#[derive(Serialize, Deserialize)] +struct SemanticParamIdCached { + language_element: LanguageElementCached, +} +impl SemanticParamIdCached { + fn new(param_id: semantic::ParamId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { language_element: LanguageElementCached::new(param_id, ctx) } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ParamId { + let (module_id, stable_ptr) = self.language_element.embed(ctx); + ParamLongId(module_id, ParamPtr(stable_ptr)).intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize)] +struct SemanticLocalVarIdCached { + language_element: LanguageElementCached, +} +impl SemanticLocalVarIdCached { + fn new(local_var_id: LocalVarId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { language_element: LanguageElementCached::new(local_var_id, ctx) } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> LocalVarId { + let (module_id, stable_ptr) = self.language_element.embed(ctx); + LocalVarLongId(module_id, TerminalIdentifierPtr(stable_ptr)).intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize)] +enum SemanticStatementItemIdCached { + Constant(LanguageElementCached), + Use(LanguageElementCached), +} + +impl SemanticStatementItemIdCached { + fn new(item_id: StatementItemId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match item_id { + StatementItemId::Constant(id) => { + SemanticStatementItemIdCached::Constant(LanguageElementCached::new(id, ctx)) + } + StatementItemId::Use(id) => { + SemanticStatementItemIdCached::Use(LanguageElementCached::new(id, ctx)) + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> StatementItemId { + match self { + SemanticStatementItemIdCached::Constant(id) => { + let (module_id, stable_ptr) = id.embed(ctx); + StatementItemId::Constant( + StatementConstLongId(module_id, ItemConstantPtr(stable_ptr)).intern(ctx.db), + ) + } + SemanticStatementItemIdCached::Use(id) => { + let (module_id, stable_ptr) = id.embed(ctx); + StatementItemId::Use( + StatementUseLongId(module_id, UsePathLeafPtr(stable_ptr)).intern(ctx.db), + ) + } + } + } +} + +#[derive(Serialize, Deserialize)] +struct VariableCached { + droppable: Option, + /// Can the type be (trivially) copied. + copyable: Option, + /// A Destruct impl for the type, if found. + destruct_impl: Option, + /// A PanicDestruct impl for the type, if found. + panic_destruct_impl: Option, + /// Semantic type of the variable. + ty: TypeIdCached, + location: LocationIdCached, +} +impl VariableCached { + fn new(variable: Variable, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + droppable: variable + .droppable + .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx)) + .ok(), + copyable: variable + .copyable + .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx)) + .ok(), + destruct_impl: variable + .destruct_impl + .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx)) + .ok(), + panic_destruct_impl: variable + .panic_destruct_impl + .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx)) + .ok(), + ty: TypeIdCached::new(variable.ty, &mut ctx.semantic_ctx), + location: LocationIdCached::new(variable.location, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> Variable { + Variable { + droppable: self + .droppable + .map(|impl_id| impl_id.embed(&mut ctx.semantic_ctx)) + .ok_or(InferenceError::Reported(skip_diagnostic())), + copyable: self + .copyable + .map(|impl_id| impl_id.embed(&mut ctx.semantic_ctx)) + .ok_or(InferenceError::Reported(skip_diagnostic())), + destruct_impl: self + .destruct_impl + .map(|impl_id| impl_id.embed(&mut ctx.semantic_ctx)) + .ok_or(InferenceError::Reported(skip_diagnostic())), + panic_destruct_impl: self + .panic_destruct_impl + .map(|impl_id| impl_id.embed(&mut ctx.semantic_ctx)) + .ok_or(InferenceError::Reported(skip_diagnostic())), + ty: self.ty.embed(&mut ctx.semantic_ctx), + location: self.location.embed(ctx), + } + } +} + +#[derive(Serialize, Deserialize)] +struct VarUsageCached { + /// Variable id. + var_id: usize, + /// Location of the usage. + location: LocationIdCached, +} + +impl VarUsageCached { + fn new(var_usage: VarUsage, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + var_id: var_usage.var_id.index(), + location: LocationIdCached::new(var_usage.location, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> VarUsage { + VarUsage { + var_id: ctx.flat_lowered_variables_id[self.var_id], + location: self.location.embed(ctx), + } + } +} + +#[derive(Serialize, Deserialize)] +struct FlatBlockCached { + /// Statements in the block. + statements: Vec, + /// Block end. + end: FlatBlockEndCached, +} +impl FlatBlockCached { + fn new(flat_block: FlatBlock, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + statements: flat_block + .statements + .into_iter() + .map(|stmt| StatementCached::new(stmt, ctx)) + .collect(), + end: FlatBlockEndCached::new(flat_block.end, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> FlatBlock { + FlatBlock { + statements: self.statements.into_iter().map(|stmt| stmt.embed(ctx)).collect(), + end: self.end.embed(ctx), + } + } +} +#[derive(Serialize, Deserialize)] +enum FlatBlockEndCached { + /// The block was created but still needs to be populated. Block must not be in this state in + /// the end of the lowering phase. + NotSet, + /// This block ends with a `return` statement, exiting the function. + Return(Vec, LocationIdCached), + /// This block ends with a panic. + Panic(VarUsageCached), + /// This block ends with a jump to a different block. + Goto(usize, VarRemappingCached), + Match { + info: MatchInfoCached, + }, +} +impl FlatBlockEndCached { + fn new(flat_block_end: FlatBlockEnd, ctx: &mut CacheSavingContext<'_>) -> Self { + match flat_block_end { + FlatBlockEnd::Return(returns, location) => FlatBlockEndCached::Return( + returns.iter().map(|var| VarUsageCached::new(*var, ctx)).collect(), + LocationIdCached::new(location, ctx), + ), + FlatBlockEnd::Panic(data) => FlatBlockEndCached::Panic(VarUsageCached::new(data, ctx)), + FlatBlockEnd::Goto(block_id, remapping) => { + FlatBlockEndCached::Goto(block_id.0, VarRemappingCached::new(remapping, ctx)) + } + FlatBlockEnd::NotSet => FlatBlockEndCached::NotSet, + FlatBlockEnd::Match { info } => { + FlatBlockEndCached::Match { info: MatchInfoCached::new(info, ctx) } + } + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> FlatBlockEnd { + match self { + FlatBlockEndCached::Return(returns, location) => FlatBlockEnd::Return( + returns.into_iter().map(|var_usage| var_usage.embed(ctx)).collect(), + location.embed(ctx), + ), + FlatBlockEndCached::Panic(var_id) => FlatBlockEnd::Panic(var_id.embed(ctx)), + FlatBlockEndCached::Goto(block_id, remapping) => { + FlatBlockEnd::Goto(BlockId(block_id), remapping.embed(ctx)) + } + FlatBlockEndCached::NotSet => FlatBlockEnd::NotSet, + FlatBlockEndCached::Match { info } => FlatBlockEnd::Match { info: info.embed(ctx) }, + } + } +} + +#[derive(Serialize, Deserialize)] +struct VarRemappingCached { + /// Map from new_var to old_var (since new_var cannot appear twice, but old_var can). + remapping: OrderedHashMap, +} +impl VarRemappingCached { + fn new(var_remapping: VarRemapping, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + remapping: var_remapping + .iter() + .map(|(dst, src)| (dst.index(), VarUsageCached::new(*src, ctx))) + .collect(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> VarRemapping { + let mut remapping = OrderedHashMap::default(); + for (dst, src) in self.remapping { + remapping.insert(ctx.flat_lowered_variables_id[dst], src.embed(ctx)); + } + VarRemapping { remapping } + } +} + +#[derive(Serialize, Deserialize)] +enum MatchInfoCached { + Enum(MatchEnumInfoCached), + Extern(MatchExternInfoCached), + Value(MatchEnumValueCached), +} +impl MatchInfoCached { + fn new(match_info: MatchInfo, ctx: &mut CacheSavingContext<'_>) -> Self { + match match_info { + MatchInfo::Enum(info) => MatchInfoCached::Enum(MatchEnumInfoCached::new(info, ctx)), + MatchInfo::Extern(info) => { + MatchInfoCached::Extern(MatchExternInfoCached::new(info, ctx)) + } + MatchInfo::Value(info) => MatchInfoCached::Value(MatchEnumValueCached::new(info, ctx)), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> MatchInfo { + match self { + MatchInfoCached::Enum(info) => MatchInfo::Enum(info.embed(ctx)), + MatchInfoCached::Extern(info) => MatchInfo::Extern(info.embed(ctx)), + MatchInfoCached::Value(info) => MatchInfo::Value(info.embed(ctx)), + } + } +} + +#[derive(Serialize, Deserialize)] +struct MatchEnumInfoCached { + concrete_enum_id: ConcreteEnumCached, + /// A living variable in current scope to match on. + input: VarUsageCached, + /// Match arms. All blocks should have the same rets. + /// Order must be identical to the order in the definition of the enum. + arms: Vec, + location: LocationIdCached, +} +impl MatchEnumInfoCached { + fn new(match_enum_info: MatchEnumInfo, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + concrete_enum_id: ConcreteEnumCached::new( + match_enum_info.concrete_enum_id, + &mut ctx.semantic_ctx, + ), + input: VarUsageCached::new(match_enum_info.input, ctx), + arms: match_enum_info + .arms + .into_iter() + .map(|arm| MatchArmCached::new(arm, ctx)) + .collect(), + location: LocationIdCached::new(match_enum_info.location, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> MatchEnumInfo { + MatchEnumInfo { + concrete_enum_id: self.concrete_enum_id.embed(&mut ctx.semantic_ctx), + input: self.input.embed(ctx), + arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(), + location: self.location.embed(ctx), + } + } +} + +#[derive(Serialize, Deserialize)] +struct MatchExternInfoCached { + /// A concrete external function to call. + function: FunctionIdCached, + /// Living variables in current scope to move to the function, as arguments. + inputs: Vec, + /// Match arms. All blocks should have the same rets. + /// Order must be identical to the order in the definition of the enum. + arms: Vec, + location: LocationIdCached, +} + +impl MatchExternInfoCached { + fn new(match_extern_info: MatchExternInfo, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + function: FunctionIdCached::new(match_extern_info.function, ctx), + inputs: match_extern_info + .inputs + .iter() + .map(|var| VarUsageCached::new(*var, ctx)) + .collect(), + arms: match_extern_info + .arms + .into_iter() + .map(|arm| MatchArmCached::new(arm, ctx)) + .collect(), + location: LocationIdCached::new(match_extern_info.location, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> MatchExternInfo { + MatchExternInfo { + function: self.function.embed(ctx), + inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(), + arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(), + location: self.location.embed(ctx), + } + } +} + +#[derive(Serialize, Deserialize)] +struct MatchEnumValueCached { + num_of_arms: usize, + + /// A living variable in current scope to match on. + input: VarUsageCached, + /// Match arms. All blocks should have the same rets. + arms: Vec, + location: LocationIdCached, +} + +impl MatchEnumValueCached { + fn new(match_enum_value: MatchEnumValue, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + num_of_arms: match_enum_value.num_of_arms, + input: VarUsageCached::new(match_enum_value.input, ctx), + arms: match_enum_value + .arms + .into_iter() + .map(|arm| MatchArmCached::new(arm, ctx)) + .collect(), + location: LocationIdCached::new(match_enum_value.location, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> MatchEnumValue { + MatchEnumValue { + num_of_arms: self.num_of_arms, + input: self.input.embed(ctx), + arms: self.arms.into_iter().map(|arm| arm.embed(ctx)).collect(), + location: self.location.embed(ctx), + } + } +} +/// An arm of a match statement. +#[derive(Serialize, Deserialize)] +struct MatchArmCached { + /// The selector of the arm. + arm_selector: MatchArmSelectorCached, + + /// The block_id where the relevant arm is implemented. + block_id: usize, + + /// The list of variable ids introduced in this arm. + var_ids: Vec, +} + +impl MatchArmCached { + fn new(match_arm: MatchArm, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + arm_selector: MatchArmSelectorCached::new( + match_arm.arm_selector, + &mut ctx.semantic_ctx, + ), + block_id: match_arm.block_id.0, + var_ids: match_arm.var_ids.iter().map(|var| var.index()).collect(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> MatchArm { + MatchArm { + arm_selector: self.arm_selector.embed(ctx), + block_id: BlockId(self.block_id), + var_ids: self + .var_ids + .into_iter() + .map(|var_id| ctx.flat_lowered_variables_id[var_id]) + .collect(), + } + } +} + +#[derive(Serialize, Deserialize)] +enum MatchArmSelectorCached { + VariantId(ConcreteVariantCached), + Value(usize), +} + +impl MatchArmSelectorCached { + fn new(match_arm_selector: MatchArmSelector, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match match_arm_selector { + MatchArmSelector::VariantId(variant_id) => { + MatchArmSelectorCached::VariantId(ConcreteVariantCached::new(variant_id, ctx)) + } + MatchArmSelector::Value(value) => MatchArmSelectorCached::Value(value.value), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> MatchArmSelector { + match self { + MatchArmSelectorCached::VariantId(variant_id) => { + MatchArmSelector::VariantId(variant_id.embed(&mut ctx.semantic_ctx)) + } + MatchArmSelectorCached::Value(value) => { + MatchArmSelector::Value(ValueSelectorArm { value }) + } + } + } +} + +#[derive(Serialize, Deserialize)] +enum StatementCached { + // Values. + Const(StatementConstCached), + + // Flow control. + Call(StatementCallCached), + + // Structs (including tuples). + StructConstruct(StatementStructConstructCached), + StructDestructure(StatementStructDestructureCached), + + // Enums. + EnumConstruct(StatementEnumConstructCached), + + Snapshot(StatementSnapshotCached), + Desnap(StatementDesnapCached), +} + +impl StatementCached { + fn new(stmt: Statement, ctx: &mut CacheSavingContext<'_>) -> Self { + match stmt { + Statement::Const(stmt) => StatementCached::Const(StatementConstCached::new(stmt, ctx)), + Statement::Call(stmt) => StatementCached::Call(StatementCallCached::new(stmt, ctx)), + Statement::StructConstruct(stmt) => { + StatementCached::StructConstruct(StatementStructConstructCached::new(stmt, ctx)) + } + Statement::StructDestructure(stmt) => { + StatementCached::StructDestructure(StatementStructDestructureCached::new(stmt, ctx)) + } + Statement::EnumConstruct(stmt) => { + StatementCached::EnumConstruct(StatementEnumConstructCached::new(stmt, ctx)) + } + Statement::Snapshot(stmt) => { + StatementCached::Snapshot(StatementSnapshotCached::new(stmt, ctx)) + } + Statement::Desnap(stmt) => { + StatementCached::Desnap(StatementDesnapCached::new(stmt, ctx)) + } + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> Statement { + match self { + StatementCached::Const(stmt) => Statement::Const(stmt.embed(ctx)), + StatementCached::Call(stmt) => Statement::Call(stmt.embed(ctx)), + StatementCached::StructConstruct(stmt) => Statement::StructConstruct(stmt.embed(ctx)), + StatementCached::StructDestructure(stmt) => { + Statement::StructDestructure(stmt.embed(ctx)) + } + StatementCached::EnumConstruct(stmt) => Statement::EnumConstruct(stmt.embed(ctx)), + StatementCached::Snapshot(stmt) => Statement::Snapshot(stmt.embed(ctx)), + StatementCached::Desnap(stmt) => Statement::Desnap(stmt.embed(ctx)), + } + } +} + +#[derive(Serialize, Deserialize)] +struct StatementConstCached { + /// The value of the const. + value: ConstValueCached, + /// The variable to bind the value to. + output: usize, +} +impl StatementConstCached { + fn new(stmt: StatementConst, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + value: ConstValueCached::new(stmt.value, &mut ctx.semantic_ctx), + output: stmt.output.index(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> StatementConst { + StatementConst { + value: self.value.embed(&mut ctx.semantic_ctx), + output: ctx.flat_lowered_variables_id[self.output], + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum ConstValueCached { + Int(BigInt, TypeIdCached), + Struct(Vec, TypeIdCached), + Enum(ConcreteVariantCached, Box), + NonZero(Box), + Boxed(Box), + Generic(GenericParamCached), + ImplConstant(ImplConstantCached), +} +impl ConstValueCached { + fn new(const_value_id: ConstValue, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match const_value_id { + ConstValue::Int(value, ty) => ConstValueCached::Int(value, TypeIdCached::new(ty, ctx)), + ConstValue::Struct(values, ty) => ConstValueCached::Struct( + values.into_iter().map(|v| ConstValueCached::new(v, ctx)).collect(), + TypeIdCached::new(ty, ctx), + ), + ConstValue::Enum(variant, value) => ConstValueCached::Enum( + ConcreteVariantCached::new(variant, ctx), + Box::new(ConstValueCached::new(*value, ctx)), + ), + ConstValue::NonZero(value) => { + ConstValueCached::NonZero(Box::new(ConstValueCached::new(*value, ctx))) + } + ConstValue::Boxed(value) => { + ConstValueCached::Boxed(Box::new(ConstValueCached::new(*value, ctx))) + } + ConstValue::Generic(generic_param) => { + ConstValueCached::Generic(GenericParamCached::new(generic_param, ctx)) + } + ConstValue::ImplConstant(impl_constant_id) => { + ConstValueCached::ImplConstant(ImplConstantCached::new(impl_constant_id, ctx)) + } + _ => { + println!("{:?}", const_value_id.debug(ctx.db.elongate())); + todo!() + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ConstValue { + match self { + ConstValueCached::Int(value, ty) => ConstValue::Int(value, ty.embed(ctx)), + ConstValueCached::Struct(values, ty) => ConstValue::Struct( + values.into_iter().map(|v| v.embed(ctx)).collect(), + ty.embed(ctx), + ), + ConstValueCached::Enum(variant, value) => { + ConstValue::Enum(variant.embed(ctx), Box::new(value.embed(ctx))) + } + ConstValueCached::NonZero(value) => ConstValue::NonZero(Box::new(value.embed(ctx))), + ConstValueCached::Boxed(value) => ConstValue::Boxed(Box::new(value.embed(ctx))), + ConstValueCached::Generic(generic_param) => { + ConstValue::Generic(generic_param.embed(ctx)) + } + ConstValueCached::ImplConstant(impl_constant_id) => { + ConstValue::ImplConstant(impl_constant_id.embed(ctx)) + } + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ImplConstantCached { + impl_id: ImplIdCached, + trait_constant: TraitConstantCached, +} +impl ImplConstantCached { + fn new(impl_constant_id: ImplConstantId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { + impl_id: ImplIdCached::new(impl_constant_id.impl_id(), ctx), + trait_constant: TraitConstantCached::new(impl_constant_id.trait_constant_id(), ctx), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ImplConstantId { + ImplConstantId::new(self.impl_id.embed(ctx), self.trait_constant.embed(ctx), ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct TraitConstantCached { + language_element: LanguageElementCached, +} +impl TraitConstantCached { + fn new(trait_constant_id: TraitConstantId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { language_element: LanguageElementCached::new(trait_constant_id, ctx) } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> TraitConstantId { + let (module_id, stable_ptr) = self.language_element.embed(ctx); + TraitConstantLongId(module_id, TraitItemConstantPtr(stable_ptr)).intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize)] +struct ConstStatementCached { + /// Value of the constant. + value: i32, +} + +#[derive(Serialize, Deserialize)] +struct StatementCallCached { + /// A function to "call". + function: FunctionIdCached, + /// Living variables in current scope to move to the function, as arguments. + inputs: Vec, + /// Is the last input a coupon for the function call. See + /// [semantic::ExprFunctionCall::coupon_arg] for more information. + with_coupon: bool, + /// New variables to be introduced into the current scope from the function outputs. + outputs: Vec, + location: LocationIdCached, +} +impl StatementCallCached { + fn new(stmt: StatementCall, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + function: FunctionIdCached::new(stmt.function, ctx), + inputs: stmt.inputs.iter().map(|var| VarUsageCached::new(*var, ctx)).collect(), + with_coupon: stmt.with_coupon, + outputs: stmt.outputs.iter().map(|var| var.index()).collect(), + location: LocationIdCached::new(stmt.location, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> StatementCall { + StatementCall { + function: self.function.embed(ctx), + inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(), + with_coupon: self.with_coupon, + outputs: self + .outputs + .into_iter() + .map(|var_id| ctx.flat_lowered_variables_id[var_id]) + .collect(), + location: self.location.embed(ctx), + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum FunctionCached { + /// An original function from the user code. + Semantic(SemanticFunctionIdCached), + /// A function generated by the compiler. + Generated(GeneratedFunctionCached), +} +impl FunctionCached { + fn new(function: FunctionLongId, ctx: &mut CacheSavingContext<'_>) -> Self { + match function { + FunctionLongId::Semantic(id) => { + FunctionCached::Semantic(SemanticFunctionIdCached::new(id, &mut ctx.semantic_ctx)) + } + FunctionLongId::Generated(id) => { + FunctionCached::Generated(GeneratedFunctionCached::new(id, ctx)) + } + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> FunctionId { + match self { + FunctionCached::Semantic(id) => { + FunctionLongId::Semantic(id.embed(&mut ctx.semantic_ctx)) + } + FunctionCached::Generated(id) => FunctionLongId::Generated(id.embed(ctx)), + } + .intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +struct FunctionIdCached(usize); +impl FunctionIdCached { + fn new(function_id: FunctionId, ctx: &mut CacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.function_ids.get(&function_id) { + return *id; + } + let function = FunctionCached::new(function_id.lookup_intern(ctx.db), ctx); + let id = FunctionIdCached(ctx.function_ids_lookup.len()); + ctx.function_ids_lookup.push(function); + ctx.function_ids.insert(function_id, id); + id + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> FunctionId { + if let Some(function_id) = ctx.function_ids.get(&self) { + return *function_id; + } + + let function = ctx.function_ids_lookup[self.0].clone(); + let function_id = function.embed(ctx); + ctx.function_ids.insert(self, function_id); + function_id + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct SemanticFunctionCached { + generic_function: GenericFunctionCached, + + generic_args: Vec, +} +impl SemanticFunctionCached { + fn new( + function_id: semantic::FunctionLongId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + let function = function_id.function; + Self { + generic_function: GenericFunctionCached::new(function.generic_function, ctx), + generic_args: function + .generic_args + .into_iter() + .map(|arg| GenericArgumentCached::new(arg, ctx)) + .collect(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::FunctionLongId { + semantic::FunctionLongId { + function: ConcreteFunction { + generic_function: self.generic_function.embed(ctx), + generic_args: self.generic_args.into_iter().map(|arg| arg.embed(ctx)).collect(), + }, + } + } +} +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +struct SemanticFunctionIdCached(usize); +impl SemanticFunctionIdCached { + fn new(function_id: semantic::FunctionId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.function_ids.get(&function_id) { + return *id; + } + let function = SemanticFunctionCached::new(function_id.lookup_intern(ctx.db), ctx); + let id = SemanticFunctionIdCached(ctx.function_ids_lookup.len()); + ctx.function_ids_lookup.push(function); + ctx.function_ids.insert(function_id, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::FunctionId { + if let Some(function_id) = ctx.function_ids.get(&self) { + return *function_id; + } + + let function = ctx.function_ids_lookup[self.0].clone(); + let function_id = function.embed(ctx).intern(ctx.db); + ctx.function_ids.insert(self, function_id); + function_id + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum GenericFunctionCached { + /// A generic free function. + Free(LanguageElementCached), + /// A generic extern function. + Extern(LanguageElementCached), + /// A generic function of an impl. + Impl(ImplIdCached, LanguageElementCached), +} +impl GenericFunctionCached { + fn new(generic_function: GenericFunctionId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match generic_function { + GenericFunctionId::Free(id) => { + GenericFunctionCached::Free(LanguageElementCached::new(id, ctx)) + } + GenericFunctionId::Extern(id) => { + GenericFunctionCached::Extern(LanguageElementCached::new(id, ctx)) + } + GenericFunctionId::Impl(id) => GenericFunctionCached::Impl( + ImplIdCached::new(id.impl_id, ctx), + LanguageElementCached::new(id.function, ctx), + ), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> GenericFunctionId { + match self { + GenericFunctionCached::Free(id) => { + let (module_id, stable_ptr) = id.embed(ctx); + let id = + FreeFunctionLongId(module_id, FunctionWithBodyPtr(stable_ptr)).intern(ctx.db); + GenericFunctionId::Free(id) + } + GenericFunctionCached::Extern(id) => { + let (module_id, stable_ptr) = id.embed(ctx); + let id = ExternFunctionLongId(module_id, ItemExternFunctionPtr(stable_ptr)) + .intern(ctx.db); + GenericFunctionId::Extern(id) + } + GenericFunctionCached::Impl(id, name) => { + let impl_id = id.embed(ctx); + let (module_file_id, stable_ptr) = name.embed(ctx); + let trait_function_id = + TraitFunctionLongId(module_file_id, TraitItemFunctionPtr(stable_ptr)) + .intern(ctx.db); + + GenericFunctionId::Impl(ImplGenericFunctionId { + impl_id, + function: trait_function_id, + }) + } + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct GeneratedFunctionCached { + parent: SemanticConcreteFunctionWithBodyCached, + key: GeneratedFunctionKeyCached, +} +impl GeneratedFunctionCached { + fn new(function: GeneratedFunction, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + parent: SemanticConcreteFunctionWithBodyCached::new( + function.parent, + &mut ctx.semantic_ctx, + ), + key: GeneratedFunctionKeyCached::new(function.key, ctx), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> GeneratedFunction { + GeneratedFunction { + parent: self.parent.embed(&mut ctx.semantic_ctx), + key: self.key.embed(ctx), + } + } +} +#[derive(Serialize, Deserialize, Clone)] +struct SemanticConcreteFunctionWithBodyCached { + generic_function: GenericFunctionWithBodyCached, + generic_args: Vec, +} +impl SemanticConcreteFunctionWithBodyCached { + fn new( + function_id: semantic::ConcreteFunctionWithBodyId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + Self { + generic_function: GenericFunctionWithBodyCached::new( + function_id.generic_function(ctx.db), + ctx, + ), + generic_args: function_id + .lookup_intern(ctx.db) + .generic_args + .into_iter() + .map(|arg| GenericArgumentCached::new(arg, ctx)) + .collect(), + } + } + fn embed( + self, + ctx: &mut SemanticCacheLoadingContext<'_>, + ) -> semantic::ConcreteFunctionWithBodyId { + let generic_function = self.generic_function.embed(ctx); + let generic_args = self.generic_args.into_iter().map(|arg| arg.embed(ctx)).collect(); + ConcreteFunctionWithBody { generic_function, generic_args }.intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum GenericFunctionWithBodyCached { + Free(LanguageElementCached), + Impl(ConcreteImplCached, ImplFunctionBodyCached), +} + +impl GenericFunctionWithBodyCached { + fn new( + generic_function: GenericFunctionWithBodyId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + match generic_function { + GenericFunctionWithBodyId::Free(id) => { + GenericFunctionWithBodyCached::Free(LanguageElementCached::new(id, ctx)) + } + GenericFunctionWithBodyId::Impl(id) => GenericFunctionWithBodyCached::Impl( + ConcreteImplCached::new(id.concrete_impl_id, ctx), + ImplFunctionBodyCached::new(id.function_body, ctx), + ), + GenericFunctionWithBodyId::Trait(_id) => { + unreachable!("Trait functions are not supported in serialization") + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> GenericFunctionWithBodyId { + match self { + GenericFunctionWithBodyCached::Free(id) => { + let (module_id, stable_ptr) = id.embed(ctx); + let id = + FreeFunctionLongId(module_id, FunctionWithBodyPtr(stable_ptr)).intern(ctx.db); + GenericFunctionWithBodyId::Free(id) + } + GenericFunctionWithBodyCached::Impl(id, function_body) => { + // todo handle trait functions + GenericFunctionWithBodyId::Impl(ImplGenericFunctionWithBodyId { + concrete_impl_id: id.embed(ctx), + function_body: function_body.embed(ctx), + }) + } + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum ImplFunctionBodyCached { + /// A function that was implemented in the impl. + Impl(LanguageElementCached), + /// The default implementation of a trait function in the trait. + Trait(LanguageElementCached), +} +impl ImplFunctionBodyCached { + fn new(function_body: ImplFunctionBodyId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match function_body { + ImplFunctionBodyId::Impl(id) => { + ImplFunctionBodyCached::Impl(LanguageElementCached::new(id, ctx)) + } + ImplFunctionBodyId::Trait(id) => { + ImplFunctionBodyCached::Trait(LanguageElementCached::new(id, ctx)) + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ImplFunctionBodyId { + match self { + ImplFunctionBodyCached::Impl(id) => { + let (module_file_id, stable_ptr) = id.embed(ctx); + ImplFunctionBodyId::Impl( + ImplFunctionLongId(module_file_id, FunctionWithBodyPtr(stable_ptr)) + .intern(ctx.db), + ) + } + ImplFunctionBodyCached::Trait(id) => { + let (module_file_id, stable_ptr) = id.embed(ctx); + ImplFunctionBodyId::Trait( + TraitFunctionLongId(module_file_id, TraitItemFunctionPtr(stable_ptr)) + .intern(ctx.db), + ) + } + } + } +} + +#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq)] +enum GeneratedFunctionKeyCached { + Loop(SyntaxStablePtrIdCached), + TraitFunc(LanguageElementCached, SyntaxStablePtrIdCached), +} + +impl GeneratedFunctionKeyCached { + fn new(key: GeneratedFunctionKey, ctx: &mut CacheSavingContext<'_>) -> Self { + match key { + GeneratedFunctionKey::Loop(id) => GeneratedFunctionKeyCached::Loop( + SyntaxStablePtrIdCached::new(id.untyped(), &mut ctx.semantic_ctx), + ), + GeneratedFunctionKey::TraitFunc(id, stable_location) => { + GeneratedFunctionKeyCached::TraitFunc( + LanguageElementCached::new(id, &mut ctx.semantic_ctx), + SyntaxStablePtrIdCached::new( + stable_location.stable_ptr(), + &mut ctx.semantic_ctx, + ), + ) + } + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> GeneratedFunctionKey { + match self { + GeneratedFunctionKeyCached::Loop(id) => { + GeneratedFunctionKey::Loop(ExprPtr(id.embed(&mut ctx.semantic_ctx))) + } + GeneratedFunctionKeyCached::TraitFunc(id, stable_location) => { + let (module_file_id, stable_ptr) = id.embed(&mut ctx.semantic_ctx); + GeneratedFunctionKey::TraitFunc( + TraitFunctionLongId(module_file_id, TraitItemFunctionPtr(stable_ptr)) + .intern(ctx.db), + StableLocation::new(stable_location.embed(&mut ctx.semantic_ctx)), + ) + } + } + } +} + +#[derive(Serialize, Deserialize)] +struct StatementStructConstructCached { + inputs: Vec, + /// The variable to bind the value to. + output: usize, +} +impl StatementStructConstructCached { + fn new(stmt: StatementStructConstruct, _ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + inputs: stmt.inputs.iter().map(|var| VarUsageCached::new(*var, _ctx)).collect(), + output: stmt.output.index(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> StatementStructConstruct { + StatementStructConstruct { + inputs: self.inputs.into_iter().map(|var_id| var_id.embed(ctx)).collect(), + output: ctx.flat_lowered_variables_id[self.output], + } + } +} +#[derive(Serialize, Deserialize)] +struct StatementStructDestructureCached { + /// A living variable in current scope to destructure. + input: VarUsageCached, + /// The variables to bind values to. + outputs: Vec, +} +impl StatementStructDestructureCached { + fn new(stmt: StatementStructDestructure, _ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + input: VarUsageCached::new(stmt.input, _ctx), + outputs: stmt.outputs.iter().map(|var| var.index()).collect(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> StatementStructDestructure { + StatementStructDestructure { + input: self.input.embed(ctx), + outputs: self + .outputs + .into_iter() + .map(|var_id| ctx.flat_lowered_variables_id[var_id]) + .collect(), + } + } +} + +#[derive(Serialize, Deserialize)] +struct StatementEnumConstructCached { + variant: ConcreteVariantCached, + /// A living variable in current scope to wrap with the variant. + input: VarUsageCached, + /// The variable to bind the value to. + output: usize, +} +impl StatementEnumConstructCached { + fn new(stmt: StatementEnumConstruct, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + variant: ConcreteVariantCached::new(stmt.variant, &mut ctx.semantic_ctx), + input: VarUsageCached::new(stmt.input, ctx), + output: stmt.output.index(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> StatementEnumConstruct { + StatementEnumConstruct { + variant: self.variant.embed(&mut ctx.semantic_ctx), + input: self.input.embed(ctx), + output: ctx.flat_lowered_variables_id[self.output], + } + } +} + +#[derive(Serialize, Deserialize)] +struct StatementSnapshotCached { + input: VarUsageCached, + outputs: [usize; 2], +} +impl StatementSnapshotCached { + fn new(stmt: StatementSnapshot, _ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + input: VarUsageCached::new(stmt.input, _ctx), + outputs: stmt.outputs.map(|var| var.index()), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> StatementSnapshot { + StatementSnapshot { + input: self.input.embed(ctx), + outputs: [ + ctx.flat_lowered_variables_id[self.outputs[0]], + ctx.flat_lowered_variables_id[self.outputs[1]], + ], + } + } +} + +#[derive(Serialize, Deserialize)] +struct StatementDesnapCached { + input: VarUsageCached, + /// The variable to bind the value to. + output: usize, +} +impl StatementDesnapCached { + fn new(stmt: StatementDesnap, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { input: VarUsageCached::new(stmt.input, ctx), output: stmt.output.index() } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> StatementDesnap { + StatementDesnap { + input: self.input.embed(ctx), + output: ctx.flat_lowered_variables_id[self.output], + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum GenericArgumentCached { + Type(TypeIdCached), + Value(ConstValueCached), + Impl(ImplIdCached), + NegImpl, +} + +impl GenericArgumentCached { + fn new( + generic_argument_id: semantic::GenericArgumentId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + match generic_argument_id { + semantic::GenericArgumentId::Type(type_id) => { + GenericArgumentCached::Type(TypeIdCached::new(type_id, ctx)) + } + semantic::GenericArgumentId::Constant(const_value_id) => { + GenericArgumentCached::Value(ConstValueCached::new( + const_value_id.lookup_intern(ctx.db), // todo intern + ctx, + )) + } + semantic::GenericArgumentId::Impl(impl_id) => { + GenericArgumentCached::Impl(ImplIdCached::new(impl_id, ctx)) + } + semantic::GenericArgumentId::NegImpl => GenericArgumentCached::NegImpl, + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::GenericArgumentId { + match self { + GenericArgumentCached::Type(ty) => semantic::GenericArgumentId::Type(ty.embed(ctx)), + GenericArgumentCached::Value(value) => { + semantic::GenericArgumentId::Constant(value.embed(ctx).intern(ctx.db)) + } + GenericArgumentCached::Impl(imp) => semantic::GenericArgumentId::Impl(imp.embed(ctx)), + GenericArgumentCached::NegImpl => semantic::GenericArgumentId::NegImpl, + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum TypeCached { + Concrete(ConcreteTypeCached), + /// Some expressions might have invalid types during processing, either due to errors or + /// during inference. + Tuple(Vec), + Snapshot(Box), + GenericParameter(GenericParamCached), + ImplType(ImplTypeCached), + FixedSizeArray(TypeIdCached, ConstValueCached), +} + +impl TypeCached { + fn new(type_id: TypeLongId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match type_id { + semantic::TypeLongId::Concrete(concrete_type_id) => { + TypeCached::Concrete(ConcreteTypeCached::new(concrete_type_id, ctx)) + } + semantic::TypeLongId::Tuple(vec) => { + TypeCached::Tuple(vec.into_iter().map(|ty| TypeIdCached::new(ty, ctx)).collect()) + } + semantic::TypeLongId::Snapshot(type_id) => { + TypeCached::Snapshot(Box::new(TypeIdCached::new(type_id, ctx))) + } + semantic::TypeLongId::GenericParameter(generic_param_id) => { + TypeCached::GenericParameter(GenericParamCached::new(generic_param_id, ctx)) + } + semantic::TypeLongId::ImplType(impl_type_id) => { + TypeCached::ImplType(ImplTypeCached::new(impl_type_id, ctx)) + } + semantic::TypeLongId::FixedSizeArray { type_id, size } => TypeCached::FixedSizeArray( + TypeIdCached::new(type_id, ctx), + ConstValueCached::new(size.lookup_intern(ctx.db), ctx), + ), + _ => { + println!("failed to cache type {:?}", type_id.debug(ctx.db.elongate())); + todo!() + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> TypeLongId { + match self { + TypeCached::Concrete(concrete_type) => TypeLongId::Concrete(concrete_type.embed(ctx)), + TypeCached::Tuple(vec) => { + TypeLongId::Tuple(vec.into_iter().map(|ty| ty.embed(ctx)).collect()) + } + TypeCached::Snapshot(type_id) => TypeLongId::Snapshot(type_id.embed(ctx)), + TypeCached::GenericParameter(generic_param) => { + TypeLongId::GenericParameter(generic_param.embed(ctx)) + } + TypeCached::ImplType(impl_type) => TypeLongId::ImplType(impl_type.embed(ctx)), + TypeCached::FixedSizeArray(type_id, size) => TypeLongId::FixedSizeArray { + type_id: type_id.embed(ctx), + size: size.embed(ctx).intern(ctx.db), + }, + } + } +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +struct TypeIdCached(usize); + +impl TypeIdCached { + fn new(ty: TypeId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.type_ids.get(&ty) { + return *id; + } + let ty_long = TypeCached::new(ty.lookup_intern(ctx.db), ctx); + let id = TypeIdCached(ctx.type_ids_lookup.len()); + ctx.type_ids_lookup.push(ty_long); + ctx.type_ids.insert(ty, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> TypeId { + if let Some(type_id) = ctx.type_ids.get(&self) { + return *type_id; + } + + let ty = ctx.type_ids_lookup[self.0].clone(); + let ty = ty.embed(ctx).intern(ctx.db); + ctx.type_ids.insert(self, ty); + ty + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum ConcreteTypeCached { + Struct(ConcreteStructCached), + Enum(ConcreteEnumCached), + Extern(ConcreteExternTypeCached), +} + +impl ConcreteTypeCached { + fn new( + concrete_type_id: semantic::ConcreteTypeId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + match concrete_type_id { + semantic::ConcreteTypeId::Struct(id) => { + ConcreteTypeCached::Struct(ConcreteStructCached::new(id, ctx)) + } + semantic::ConcreteTypeId::Enum(id) => { + ConcreteTypeCached::Enum(ConcreteEnumCached::new(id, ctx)) + } + semantic::ConcreteTypeId::Extern(id) => { + ConcreteTypeCached::Extern(ConcreteExternTypeCached::new(id, ctx)) + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ConcreteTypeId { + match self { + ConcreteTypeCached::Struct(s) => semantic::ConcreteTypeId::Struct(s.embed(ctx)), + ConcreteTypeCached::Enum(e) => semantic::ConcreteTypeId::Enum(e.embed(ctx)), + ConcreteTypeCached::Extern(e) => semantic::ConcreteTypeId::Extern(e.embed(ctx)), + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ImplTypeCached { + impl_id: ImplIdCached, + trait_type: TraitTypeCached, +} +impl ImplTypeCached { + fn new(impl_type_id: ImplTypeId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { + impl_id: ImplIdCached::new(impl_type_id.impl_id(), ctx), + trait_type: TraitTypeCached::new(impl_type_id.ty(), ctx), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ImplTypeId { + let impl_id = self.impl_id.embed(ctx); + let ty = self.trait_type.embed(ctx); + ImplTypeId::new(impl_id, ty, ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct TraitTypeCached { + language_element: LanguageElementCached, +} +impl TraitTypeCached { + fn new(trait_type_id: TraitTypeId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { language_element: LanguageElementCached::new(trait_type_id, ctx) } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> TraitTypeId { + let (module_file_id, stable_ptr) = self.language_element.embed(ctx); + TraitTypeLongId(module_file_id, TraitItemTypePtr(stable_ptr)).intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum ImplCached { + Concrete(ConcreteImplCached), + GenericParameter(GenericParamCached), +} +impl ImplCached { + fn new(impl_id: ImplLongId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match impl_id { + ImplLongId::Concrete(concrete_impl) => { + ImplCached::Concrete(ConcreteImplCached::new(concrete_impl, ctx)) + } + ImplLongId::GenericParameter(generic_param_id) => { + ImplCached::GenericParameter(GenericParamCached::new(generic_param_id, ctx)) + } + _ => todo!(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ImplLongId { + match self { + ImplCached::Concrete(concrete_impl) => ImplLongId::Concrete(concrete_impl.embed(ctx)), + ImplCached::GenericParameter(generic_param) => { + ImplLongId::GenericParameter(generic_param.embed(ctx)) + } + } + } +} +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +struct ImplIdCached(usize); + +impl ImplIdCached { + fn new(impl_id: ImplId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.impl_ids.get(&impl_id) { + return *id; + } + let imp = ImplCached::new(impl_id.lookup_intern(ctx.db), ctx); + let id = ImplIdCached(ctx.impl_ids_lookup.len()); + ctx.impl_ids_lookup.push(imp); + ctx.impl_ids.insert(impl_id, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ImplId { + if let Some(impl_id) = ctx.impl_ids.get(&self) { + return *impl_id; + } + + let imp = ctx.impl_ids_lookup[self.0].clone(); + let imp = imp.embed(ctx).intern(ctx.db); + ctx.impl_ids.insert(self, imp); + imp + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ConcreteImplCached { + impl_def_id: ImplDefIdCached, + generic_args: Vec, +} +impl ConcreteImplCached { + fn new( + concrete_impl: semantic::ConcreteImplId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + let long_id = concrete_impl.lookup_intern(ctx.db); + Self { + impl_def_id: ImplDefIdCached::new(long_id.impl_def_id, ctx), + generic_args: long_id + .generic_args + .into_iter() + .map(|arg| GenericArgumentCached::new(arg, ctx)) + .collect(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ConcreteImplId { + let impl_def_id = self.impl_def_id.embed(ctx); + let long_id = ConcreteImplLongId { + impl_def_id, + generic_args: self.generic_args.into_iter().map(|arg| arg.embed(ctx)).collect(), + }; + long_id.intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ImplDefIdCached { + language_element: LanguageElementCached, +} +impl ImplDefIdCached { + fn new(impl_def_id: ImplDefId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { language_element: LanguageElementCached::new(impl_def_id, ctx) } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ImplDefId { + let (module_file_id, stable_ptr) = self.language_element.embed(ctx); + ImplDefLongId(module_file_id, ItemImplPtr(stable_ptr)).intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct GenericParamCached { + language_element: LanguageElementCached, +} +impl GenericParamCached { + fn new(generic_param_id: GenericParamId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { language_element: LanguageElementCached::new(generic_param_id, ctx) } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> GenericParamId { + let (module_file_id, stable_ptr) = self.language_element.embed(ctx); + GenericParamLongId(module_file_id, GenericParamPtr(stable_ptr)).intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ConcreteVariantCached { + concrete_enum_id: ConcreteEnumCached, + id: LanguageElementCached, + ty: TypeIdCached, + /// The index of the variant from within the variant list. + idx: usize, +} +impl ConcreteVariantCached { + fn new( + concrete_variant: semantic::ConcreteVariant, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + Self { + concrete_enum_id: ConcreteEnumCached::new(concrete_variant.concrete_enum_id, ctx), + id: LanguageElementCached::new(concrete_variant.id, ctx), + ty: TypeIdCached::new(concrete_variant.ty, ctx), + idx: concrete_variant.idx, + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ConcreteVariant { + let concrete_enum_id = self.concrete_enum_id.embed(ctx); + let ty = self.ty.embed(ctx); + let (module_file_id, stable_ptr) = self.id.embed(ctx); + + let id = VariantLongId(module_file_id, VariantPtr(stable_ptr)).intern(ctx.db); + semantic::ConcreteVariant { concrete_enum_id, id, ty, idx: self.idx } + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ConcreteEnumCached { + enum_id: LanguageElementCached, + generic_args: Vec, +} + +impl ConcreteEnumCached { + fn new( + concrete_enum: semantic::ConcreteEnumId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + let long_id = concrete_enum.lookup_intern(ctx.db); + Self { + enum_id: LanguageElementCached::new(long_id.enum_id, ctx), + generic_args: long_id + .generic_args + .into_iter() + .map(|arg| GenericArgumentCached::new(arg, ctx)) + .collect(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ConcreteEnumId { + let (module_file_id, stable_ptr) = self.enum_id.embed(ctx); + + let long_id = ConcreteEnumLongId { + enum_id: EnumLongId(module_file_id, ItemEnumPtr(stable_ptr)).intern(ctx.db), + generic_args: self.generic_args.into_iter().map(|arg| arg.embed(ctx)).collect(), + }; + long_id.intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ConcreteStructCached { + struct_id: LanguageElementCached, + generic_args: Vec, +} +impl ConcreteStructCached { + fn new( + concrete_struct: semantic::ConcreteStructId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + let long_id = concrete_struct.lookup_intern(ctx.db); + Self { + struct_id: LanguageElementCached::new(long_id.struct_id, ctx), + generic_args: long_id + .generic_args + .into_iter() + .map(|arg| GenericArgumentCached::new(arg, ctx)) + .collect(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ConcreteStructId { + let (module_file_id, stable_ptr) = self.struct_id.embed(ctx); + + let long_id = ConcreteStructLongId { + struct_id: StructLongId(module_file_id, ItemStructPtr(stable_ptr)).intern(ctx.db), + generic_args: self.generic_args.into_iter().map(|arg| arg.embed(ctx)).collect(), + }; + long_id.intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct ConcreteExternTypeCached { + language_element: LanguageElementCached, + generic_args: Vec, +} +impl ConcreteExternTypeCached { + fn new( + concrete_extern_type: semantic::ConcreteExternTypeId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + let long_id = concrete_extern_type.lookup_intern(ctx.db); + Self { + language_element: LanguageElementCached::new(long_id.extern_type_id, ctx), + generic_args: long_id + .generic_args + .into_iter() + .map(|arg| GenericArgumentCached::new(arg, ctx)) + .collect(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> semantic::ConcreteExternTypeId { + let (module_file_id, stable_ptr) = self.language_element.embed(ctx); + + let long_id = ConcreteExternTypeLongId { + extern_type_id: ExternTypeLongId(module_file_id, ItemExternTypePtr(stable_ptr)) + .intern(ctx.db), + generic_args: self.generic_args.into_iter().map(|arg| arg.embed(ctx)).collect(), + }; + long_id.intern(ctx.db) + } +} + +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq)] +struct ModuleFileCached { + module: ModuleIdCached, + file_index: usize, +} +impl ModuleFileCached { + fn new(module_file_id: ModuleFileId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { module: ModuleIdCached::new(module_file_id.0, ctx), file_index: module_file_id.1.0 } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ModuleFileId { + ModuleFileId(self.module.embed(ctx), FileIndex(self.file_index)) + } +} + +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq)] +enum ModuleIdCached { + CrateRoot(CrateIdCached), + Submodule(SubmoduleIdCached), +} +impl ModuleIdCached { + fn new(module_id: ModuleId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match module_id { + ModuleId::CrateRoot(crate_id) => { + ModuleIdCached::CrateRoot(CrateIdCached::new(crate_id, ctx)) + } + ModuleId::Submodule(submodule_id) => { + ModuleIdCached::Submodule(SubmoduleIdCached::new(submodule_id, ctx)) + } + } + } + fn embed(&self, ctx: &mut SemanticCacheLoadingContext<'_>) -> ModuleId { + match self { + ModuleIdCached::CrateRoot(crate_id) => ModuleId::CrateRoot(crate_id.embed(ctx)), + ModuleIdCached::Submodule(submodule_id) => ModuleId::Submodule(submodule_id.embed(ctx)), + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum CrateCached { + Real { name: SmolStr, discriminator: Option }, + Virtual { name: SmolStr, file_id: FileIdCached, settings: String }, +} +impl CrateCached { + fn new(crate_id: CrateLongId, _ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match crate_id { + CrateLongId::Real { name, discriminator } => CrateCached::Real { name, discriminator }, + CrateLongId::Virtual { name, file_id, settings, cache_file: _ } => { + CrateCached::Virtual { name, file_id: FileIdCached::new(file_id, _ctx), settings } + } + } + } + fn embed(self, _ctx: &mut SemanticCacheLoadingContext<'_>) -> CrateLongId { + match self { + CrateCached::Real { name, discriminator } => CrateLongId::Real { name, discriminator }, + CrateCached::Virtual { name, file_id, settings } => { + CrateLongId::Virtual { + name, + file_id: file_id.embed(_ctx), + settings, + cache_file: None, // todo if two virtual crates are supported + } + } + } + } +} +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +enum CrateIdCached { + SelfCrate, + Other(usize), +} +impl CrateIdCached { + fn new(crate_id: CrateId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + if crate_id == ctx.self_crate_id { + return CrateIdCached::SelfCrate; + } + if let Some(id) = ctx.crate_ids.get(&crate_id) { + return *id; + } + let crate_long_id = CrateCached::new(crate_id.lookup_intern(ctx.db), ctx); + let id = CrateIdCached::Other(ctx.crate_ids_lookup.len()); + ctx.crate_ids_lookup.push(crate_long_id); + ctx.crate_ids.insert(crate_id, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> CrateId { + let CrateIdCached::Other(id) = self else { + return ctx.self_crate_id; + }; + + if let Some(crate_id) = ctx.crate_ids.get(&self) { + return *crate_id; + } + let crate_long_id = ctx.crate_ids_lookup[id].clone(); + let crate_id = crate_long_id.embed(ctx).intern(ctx.db); + ctx.crate_ids.insert(self, crate_id); + crate_id + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct SubmoduleCached { + language_element: LanguageElementCached, +} +impl SubmoduleCached { + fn new(submodule_id: SubmoduleLongId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { language_element: LanguageElementCached::new(submodule_id.intern(ctx.db), ctx) } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> SubmoduleLongId { + let (module_file_id, stable_ptr) = self.language_element.embed(ctx); + + SubmoduleLongId(module_file_id, ItemModulePtr(stable_ptr)) + } +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +struct SubmoduleIdCached(usize); + +impl SubmoduleIdCached { + fn new(submodule_id: SubmoduleId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.submodule_ids.get(&submodule_id) { + return *id; + } + let submodule = SubmoduleCached::new(submodule_id.lookup_intern(ctx.db), ctx); + let id = SubmoduleIdCached(ctx.submodule_ids_lookup.len()); + ctx.submodule_ids_lookup.push(submodule); + ctx.submodule_ids.insert(submodule_id, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> SubmoduleId { + if let Some(submodule_id) = ctx.submodule_ids.get(&self) { + return *submodule_id; + } + let submodule = ctx.submodule_ids_lookup[self.0].clone(); + let submodule = submodule.embed(ctx).intern(ctx.db); + ctx.submodule_ids.insert(self, submodule); + submodule + } +} + +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq)] +struct LanguageElementCached { + module_file_id: ModuleFileCached, + stable_ptr: SyntaxStablePtrIdCached, +} +impl LanguageElementCached { + fn new( + language_element: T, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + Self { + module_file_id: ModuleFileCached::new( + language_element.module_file_id(ctx.db.upcast()), + ctx, + ), + stable_ptr: SyntaxStablePtrIdCached::new( + language_element.untyped_stable_ptr(ctx.db.upcast()), + ctx, + ), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> (ModuleFileId, SyntaxStablePtrId) { + let module_file_id = self.module_file_id.embed(ctx); + let stable_ptr = self.stable_ptr.embed(ctx); + (module_file_id, stable_ptr) + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct LocationCached { + /// The stable location of the object. + stable_location: SyntaxStablePtrIdCached, + /// Function call locations where this value was inlined from. + inline_locations: Vec, +} +impl LocationCached { + fn new(location: Location, ctx: &mut CacheSavingContext<'_>) -> Self { + Self { + stable_location: SyntaxStablePtrIdCached::new( + location.stable_location.stable_ptr(), + &mut ctx.semantic_ctx, + ), + inline_locations: location + .inline_locations + .iter() + .map(|loc| SyntaxStablePtrIdCached::new(loc.stable_ptr(), &mut ctx.semantic_ctx)) + .collect(), + } + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> Location { + Location { + stable_location: StableLocation::new(self.stable_location.embed(&mut ctx.semantic_ctx)), + inline_locations: self + .inline_locations + .into_iter() + .map(|loc| StableLocation::new(loc.embed(&mut ctx.semantic_ctx))) + .collect(), + notes: Default::default(), + } + } +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +struct LocationIdCached(usize); + +impl LocationIdCached { + fn new(location_id: LocationId, ctx: &mut CacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.location_ids.get(&location_id) { + return *id; + } + let location = LocationCached::new(location_id.lookup_intern(ctx.db), ctx); + let id = LocationIdCached(ctx.location_ids_lookup.len()); + ctx.location_ids_lookup.push(location); + ctx.location_ids.insert(location_id, id); + id + } + fn embed(self, ctx: &mut CacheLoadingContext<'_>) -> LocationId { + if let Some(location_id) = ctx.location_ids.get(&self) { + return *location_id; + } + let location = ctx.location_ids_lookup[self.0].clone(); + let location = location.embed(ctx).intern(ctx.db); + ctx.location_ids.insert(self, location); + location + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum SyntaxStablePtrCached { + /// The root node of the tree. + Root(FileIdCached, GreenIdCached), + /// A child node. + Child { + /// The parent of the node. + parent: SyntaxStablePtrIdCached, + /// The SyntaxKind of the node. + kind: SyntaxKind, + /// A list of field values for this node, to index by. + /// Which fields are used is determined by each SyntaxKind. + /// For example, a function item might use the name of the function. + key_fields: Vec, + /// Chronological index among all nodes with the same (parent, kind, key_fields). + index: usize, + }, +} + +impl SyntaxStablePtrCached { + fn new(syntax_stable_ptr: SyntaxStablePtr, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match syntax_stable_ptr { + SyntaxStablePtr::Root(root, green_id) => SyntaxStablePtrCached::Root( + FileIdCached::new(root, ctx), + GreenIdCached::new(green_id, ctx), + ), + SyntaxStablePtr::Child { parent, kind, key_fields, index } => { + SyntaxStablePtrCached::Child { + parent: SyntaxStablePtrIdCached::new(parent, ctx), + kind, + key_fields: key_fields + .into_iter() + .map(|field| GreenIdCached::new(field, ctx)) + .collect(), + index, + } + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> SyntaxStablePtr { + match self { + SyntaxStablePtrCached::Root(file, green_id) => { + SyntaxStablePtr::Root(file.embed(ctx), green_id.embed(ctx)) + } + SyntaxStablePtrCached::Child { parent, kind, key_fields, index } => { + SyntaxStablePtr::Child { + parent: parent.embed(ctx), + kind, + key_fields: key_fields.into_iter().map(|field| field.embed(ctx)).collect(), + index, + } + } + } + } +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)] +struct SyntaxStablePtrIdCached(usize); +impl SyntaxStablePtrIdCached { + fn new( + syntax_stable_ptr_id: SyntaxStablePtrId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + if let Some(id) = ctx.syntax_stable_ptr_ids.get(&syntax_stable_ptr_id) { + return *id; + } + let stable_ptr = + SyntaxStablePtrCached::new(syntax_stable_ptr_id.lookup_intern(ctx.db), ctx); + let id = SyntaxStablePtrIdCached(ctx.syntax_stable_ptr_ids_lookup.len()); + ctx.syntax_stable_ptr_ids_lookup.push(stable_ptr); + ctx.syntax_stable_ptr_ids.insert(syntax_stable_ptr_id, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> SyntaxStablePtrId { + if let Some(syntax_stable_ptr_id) = ctx.syntax_stable_ptr_ids.get(&self) { + return *syntax_stable_ptr_id; + } + let stable_ptr = ctx.syntax_stable_ptr_ids_lookup[self.0].clone(); + let stable_ptr = stable_ptr.embed(ctx); + let stable_ptr_id = stable_ptr.intern(ctx.db); + ctx.syntax_stable_ptr_ids.insert(self, stable_ptr_id); + stable_ptr_id + } +} + +#[derive(Serialize, Deserialize, Clone)] +enum GreenNodeDetailsCached { + Token(SmolStr), + Node { children: Vec, width: TextWidth }, +} + +impl GreenNodeDetailsCached { + fn new( + green_node_details: &GreenNodeDetails, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> GreenNodeDetailsCached { + match green_node_details { + GreenNodeDetails::Token(token) => GreenNodeDetailsCached::Token(token.clone()), + GreenNodeDetails::Node { children, width } => GreenNodeDetailsCached::Node { + children: children.iter().map(|child| GreenIdCached::new(*child, ctx)).collect(), + width: *width, + }, + } + } + fn embed(&self, ctx: &mut SemanticCacheLoadingContext<'_>) -> GreenNodeDetails { + match self { + GreenNodeDetailsCached::Token(token) => GreenNodeDetails::Token(token.clone()), + GreenNodeDetailsCached::Node { children, width } => GreenNodeDetails::Node { + children: children.iter().map(|child| child.embed(ctx)).collect(), + width: *width, + }, + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct GreenNodeCached { + kind: SyntaxKind, + details: GreenNodeDetailsCached, +} +impl GreenNodeCached { + fn new(green_node: &GreenNode, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { + kind: green_node.kind, + details: GreenNodeDetailsCached::new(&green_node.details, ctx), + } + } + fn embed(&self, ctx: &mut SemanticCacheLoadingContext<'_>) -> GreenNode { + GreenNode { kind: self.kind, details: self.details.embed(ctx) } + } +} + +#[derive(Serialize, Deserialize, Clone, Copy, Eq, Hash, PartialEq)] +struct GreenIdCached(usize); + +impl GreenIdCached { + fn new(green_id: GreenId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.green_ids.get(&green_id) { + return *id; + } + let green_node = GreenNodeCached::new(green_id.lookup_intern(ctx.db).as_ref(), ctx); + let id = GreenIdCached(ctx.green_ids_lookup.len()); + ctx.green_ids_lookup.push(green_node); + ctx.green_ids.insert(green_id, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> GreenId { + if let Some(green_id) = ctx.green_ids.get(&self) { + return *green_id; + } + let green_node = ctx.green_ids_lookup[self.0].clone(); + let green_node = Arc::new(green_node.embed(ctx)); + let green_id = green_node.intern(ctx.db); + ctx.green_ids.insert(self, green_id); + green_id + } +} +#[derive(Serialize, Deserialize, Clone)] +enum FileCached { + OnDisk(PathBuf), + Virtual(VirtualFileCached), + External(PluginGeneratedFileCached), +} + +impl FileCached { + fn new(file: &FileLongId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + match file { + FileLongId::OnDisk(path) => FileCached::OnDisk(path.clone()), + FileLongId::Virtual(virtual_file) => { + FileCached::Virtual(VirtualFileCached::new(virtual_file, ctx)) + } + FileLongId::External(external_file) => { + FileCached::External(PluginGeneratedFileCached::new( + PluginGeneratedFileId::from_intern_id(*external_file), + ctx, + )) + } + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> FileLongId { + match self { + FileCached::OnDisk(path) => FileLongId::OnDisk(path.clone()), + FileCached::Virtual(virtual_file) => FileLongId::Virtual(virtual_file.embed(ctx)), + FileCached::External(external_file) => { + FileLongId::External(external_file.embed(ctx).as_intern_id()) + } + } + } +} + +#[derive(Serialize, Deserialize, Clone, Copy, Eq, Hash, PartialEq)] +struct FileIdCached(usize); +impl FileIdCached { + fn new(file_id: FileId, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + if let Some(id) = ctx.file_ids.get(&file_id) { + return *id; + } + let file = FileCached::new(&file_id.lookup_intern(ctx.db), ctx); + let id = FileIdCached(ctx.file_ids_lookup.len()); + ctx.file_ids_lookup.push(file); + ctx.file_ids.insert(file_id, id); + id + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> FileId { + if let Some(file_id) = ctx.file_ids.get(&self) { + return *file_id; + } + let file = ctx.file_ids_lookup[self.0].clone(); + let file = file.embed(ctx); + let file_id = file.intern(ctx.db); + ctx.file_ids.insert(self, file_id); + file_id + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct VirtualFileCached { + parent: Option, + name: SmolStr, + content: String, + code_mappings: Vec, + kind: FileKind, +} + +impl VirtualFileCached { + fn new(virtual_file: &VirtualFile, ctx: &mut SemanticCacheSavingContext<'_>) -> Self { + Self { + parent: virtual_file.parent.map(|parent| FileIdCached::new(parent, ctx)), + name: virtual_file.name.clone(), + content: String::from(&*(virtual_file.content)), + code_mappings: virtual_file.code_mappings.to_vec(), + kind: virtual_file.kind.clone(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> VirtualFile { + VirtualFile { + parent: self.parent.map(|parent| parent.embed(ctx)), + name: self.name, + content: self.content.into(), + code_mappings: self.code_mappings.into(), + kind: self.kind, + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct PluginGeneratedFileCached { + /// The module that the file was generated from. + module_id: ModuleIdCached, + /// The stable pointer the file was generated from being ran on. + stable_ptr: SyntaxStablePtrIdCached, + /// The name of the generated file to differentiate between different generated files. + name: SmolStr, +} + +impl PluginGeneratedFileCached { + fn new( + plugin_generated_file: PluginGeneratedFileId, + ctx: &mut SemanticCacheSavingContext<'_>, + ) -> Self { + let long_id = plugin_generated_file.lookup_intern(ctx.db); + Self { + module_id: ModuleIdCached::new(long_id.module_id, ctx), + stable_ptr: SyntaxStablePtrIdCached::new(long_id.stable_ptr, ctx), + name: long_id.name.clone(), + } + } + fn embed(self, ctx: &mut SemanticCacheLoadingContext<'_>) -> PluginGeneratedFileId { + let module_id = self.module_id.embed(ctx); + let stable_ptr = self.stable_ptr.embed(ctx); + let long_id = PluginGeneratedFileLongId { module_id, stable_ptr, name: self.name }; + long_id.intern(ctx.db) + } +} diff --git a/crates/cairo-lang-lowering/src/cache/test.rs b/crates/cairo-lang-lowering/src/cache/test.rs new file mode 100644 index 00000000000..7a674007f84 --- /dev/null +++ b/crates/cairo-lang-lowering/src/cache/test.rs @@ -0,0 +1,85 @@ +use cairo_lang_debug::DebugWithDb; +use cairo_lang_filesystem::ids::BlobLongId; +use cairo_lang_semantic::test_utils::{setup_test_function, setup_test_function_ex}; +use cairo_lang_test_utils::parse_test_file::TestRunnerResult; +use cairo_lang_test_utils::verify_diagnostics_expectation; +use cairo_lang_utils::Intern; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; + +use super::generate_crate_cache; +use crate::FlatLowered; +use crate::db::LoweringGroup; +use crate::fmt::LoweredFormatter; +use crate::ids::ConcreteFunctionWithBodyId; +use crate::test_utils::LoweringDatabaseForTesting; + +cairo_lang_test_utils::test_file_test!( + cache, + "src/cache/test_data", + { + cache :"cache", + }, + test_cache_check +); + +fn formatted_lowered(db: &dyn LoweringGroup, lowered: &FlatLowered) -> String { + let lowered_formatter = LoweredFormatter::new(db, &lowered.variables); + format!("{:?}", lowered.debug(&lowered_formatter)) +} + +fn test_cache_check( + inputs: &OrderedHashMap, + args: &OrderedHashMap, +) -> TestRunnerResult { + let db = &mut LoweringDatabaseForTesting::default(); + + let (test_function, _semantic_diagnostics) = setup_test_function( + db, + inputs["function"].as_str(), + inputs["function_name"].as_str(), + inputs["module_code"].as_str(), + ) + .split(); + + let artifact = generate_crate_cache(db, test_function.module_id.owning_crate(db)).unwrap(); + let new_db = LoweringDatabaseForTesting::new(); + let cached_file = BlobLongId::Virtual(artifact).intern(&new_db); + let (test_function, semantic_diagnostics) = setup_test_function_ex( + &new_db, + inputs["function"].as_str(), + inputs["function_name"].as_str(), + inputs["module_code"].as_str(), + None, + Some(cached_file), + ) + .split(); + + let function_id: ConcreteFunctionWithBodyId = + ConcreteFunctionWithBodyId::from_semantic(&new_db, test_function.concrete_function_id); + + let lowered = new_db.final_concrete_function_with_body_lowered(function_id); + if let Ok(lowered) = &lowered { + assert!( + lowered.blocks.iter().all(|(_, b)| b.is_set()), + "There should not be any unset flat blocks" + ); + } + let diagnostics = + new_db.module_lowering_diagnostics(test_function.module_id).unwrap_or_default(); + let formatted_lowering_diagnostics = diagnostics.format(&new_db); + let combined_diagnostics = + format!("{}\n{}", semantic_diagnostics, formatted_lowering_diagnostics); + let error = verify_diagnostics_expectation(args, &combined_diagnostics); + let lowering_format = lowered.map(|lowered| formatted_lowered(&new_db, &lowered)).unwrap_or( + "" + .to_string(), + ); + TestRunnerResult { + outputs: OrderedHashMap::from([ + ("semantic_diagnostics".into(), semantic_diagnostics), + ("lowering_diagnostics".into(), formatted_lowering_diagnostics), + ("lowering_flat".into(), lowering_format), + ]), + error, + } +} diff --git a/crates/cairo-lang-lowering/src/cache/test_data/cache b/crates/cairo-lang-lowering/src/cache/test_data/cache new file mode 100644 index 00000000000..6873b243aec --- /dev/null +++ b/crates/cairo-lang-lowering/src/cache/test_data/cache @@ -0,0 +1,41 @@ +//! > Basic cache valid. + +//! > test_runner_name +test_cache_check + +//! > function +fn foo(x: ACopy, y: ADrop) { + if true { + use_a_copy(x); + use_a_drop(y); + } else { + use_a_drop(y); + } + use_a_copy(x); +} + +//! > function_name +foo + +//! > module_code +extern type ACopy; +impl ACopyCopy of Copy; +extern type ADrop; +impl ADropDrop of Drop; + +extern fn use_a_copy(x: ACopy) nopanic; +extern fn use_a_drop(x: ADrop) nopanic; + +//! > semantic_diagnostics + +//! > lowering_diagnostics + +//! > lowering_flat +Parameters: v0: test::ACopy, v1: test::ADrop +blk0 (root): +Statements: + () <- test::use_a_copy(v0) + () <- test::use_a_drop(v1) + () <- test::use_a_copy(v0) +End: + Return() diff --git a/crates/cairo-lang-lowering/src/db.rs b/crates/cairo-lang-lowering/src/db.rs index f9e1899c07d..5b9ea002ad5 100644 --- a/crates/cairo-lang-lowering/src/db.rs +++ b/crates/cairo-lang-lowering/src/db.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use cairo_lang_debug::DebugWithDb; use cairo_lang_defs as defs; use cairo_lang_defs::ids::{LanguageElementId, ModuleId, ModuleItemId, NamedLanguageElementLongId}; use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe}; @@ -7,6 +8,7 @@ use cairo_lang_filesystem::ids::FileId; use cairo_lang_semantic::db::SemanticGroup; use cairo_lang_semantic::items::enm::SemanticEnumEx; use cairo_lang_semantic::{self as semantic, ConcreteTypeId, TypeId, TypeLongId, corelib}; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use cairo_lang_utils::ordered_hash_set::OrderedHashSet; use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; use cairo_lang_utils::unordered_hash_set::UnorderedHashSet; @@ -17,6 +19,7 @@ use num_traits::ToPrimitive; use crate::add_withdraw_gas::add_withdraw_gas; use crate::borrow_check::{PotentialDestructCalls, borrow_check}; +use crate::cache::load_cached_crate_functions; use crate::concretize::concretize_lowered; use crate::destructs::add_destructs; use crate::diagnostic::{LoweringDiagnostic, LoweringDiagnosticKind}; @@ -62,6 +65,13 @@ pub trait LoweringGroup: SemanticGroup + Upcast { function_id: defs::ids::FunctionWithBodyId, ) -> Maybe>; + /// Returns a mapping from function ids to their multi-lowerings for the given loaded from a + /// cache for the given crate. + fn cached_multi_lowerings( + &self, + crate_id: cairo_lang_filesystem::ids::CrateId, + ) -> Option>>; + /// Computes the lowered representation of a function with a body before borrow checking. fn priv_function_with_body_lowering( &self, @@ -382,10 +392,26 @@ fn priv_function_with_body_multi_lowering( db: &dyn LoweringGroup, function_id: defs::ids::FunctionWithBodyId, ) -> Maybe> { + let crate_id = function_id.module_file_id(db.upcast()).0.owning_crate(db.upcast()); + if let Some(map) = db.cached_multi_lowerings(crate_id) { + if let Some(multi_lowering) = map.get(&function_id) { + return Ok(Arc::new(multi_lowering.clone())); + } else { + panic!("function not found in cached lowering {:?}", function_id.debug(db)); + } + }; + let multi_lowering = lower_semantic_function(db.upcast(), function_id)?; Ok(Arc::new(multi_lowering)) } +fn cached_multi_lowerings( + db: &dyn LoweringGroup, + crate_id: cairo_lang_filesystem::ids::CrateId, +) -> Option>> { + load_cached_crate_functions(db, crate_id) +} + // * Borrow checking. fn priv_function_with_body_lowering( db: &dyn LoweringGroup, diff --git a/crates/cairo-lang-lowering/src/lib.rs b/crates/cairo-lang-lowering/src/lib.rs index 9c89d8e44ab..5b9b3ad3824 100644 --- a/crates/cairo-lang-lowering/src/lib.rs +++ b/crates/cairo-lang-lowering/src/lib.rs @@ -3,6 +3,7 @@ //! This crate is responsible for handling the lowering phase. pub mod add_withdraw_gas; pub mod borrow_check; +pub mod cache; pub mod concretize; pub mod db; pub mod destructs; diff --git a/crates/cairo-lang-lowering/src/objects.rs b/crates/cairo-lang-lowering/src/objects.rs index 2bed0961b76..529aadf9af3 100644 --- a/crates/cairo-lang-lowering/src/objects.rs +++ b/crates/cairo-lang-lowering/src/objects.rs @@ -381,7 +381,7 @@ pub struct StatementStructDestructure { #[derive(Clone, Debug, PartialEq, Eq)] pub struct StatementSnapshot { pub input: VarUsage, - outputs: [VariableId; 2], + pub outputs: [VariableId; 2], } impl StatementSnapshot { pub fn new(input: VarUsage, output_original: VariableId, output_snapshot: VariableId) -> Self { diff --git a/crates/cairo-lang-lowering/src/test.rs b/crates/cairo-lang-lowering/src/test.rs index 31e494681df..63d93522f5b 100644 --- a/crates/cairo-lang-lowering/src/test.rs +++ b/crates/cairo-lang-lowering/src/test.rs @@ -7,7 +7,7 @@ use cairo_lang_defs::ids::LanguageElementId; use cairo_lang_diagnostics::{DiagnosticNote, DiagnosticsBuilder}; use cairo_lang_semantic as semantic; use cairo_lang_semantic::db::SemanticGroup; -use cairo_lang_semantic::test_utils::{setup_test_expr, setup_test_function}; +use cairo_lang_semantic::test_utils::{setup_test_expr, setup_test_function, setup_test_module}; use cairo_lang_syntax::node::{Terminal, TypedStablePtr}; use cairo_lang_test_utils::parse_test_file::TestRunnerResult; use cairo_lang_test_utils::verify_diagnostics_expectation; @@ -15,7 +15,6 @@ use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use cairo_lang_utils::{LookupIntern, Upcast, extract_matches}; use itertools::Itertools; use pretty_assertions::assert_eq; -use semantic::test_utils::setup_test_module_ex; use crate::FlatLowered; use crate::db::LoweringGroup; @@ -198,14 +197,13 @@ fn test_sizes() { ("core::cmp::min::::Coupon", 0), ]; - let test_module = setup_test_module_ex( + let test_module = setup_test_module( db, &type_to_size .iter() .enumerate() .map(|(i, (ty_str, _))| format!("type T{i} = {ty_str};\n")) .join(""), - None, ) .unwrap(); let db: &LoweringDatabaseForTesting = db; diff --git a/crates/cairo-lang-semantic/src/test_utils.rs b/crates/cairo-lang-semantic/src/test_utils.rs index fac7efd5d9e..cddb04dd64a 100644 --- a/crates/cairo-lang-semantic/src/test_utils.rs +++ b/crates/cairo-lang-semantic/src/test_utils.rs @@ -8,7 +8,7 @@ use cairo_lang_filesystem::db::{ FilesDatabase, FilesGroup, init_dev_corelib, init_files_group, }; use cairo_lang_filesystem::detect::detect_corelib; -use cairo_lang_filesystem::ids::{CrateId, CrateLongId, FileKind, FileLongId, VirtualFile}; +use cairo_lang_filesystem::ids::{BlobId, CrateId, CrateLongId, FileKind, FileLongId, VirtualFile}; use cairo_lang_parser::db::{ParserDatabase, ParserGroup}; use cairo_lang_syntax::node::db::{SyntaxDatabase, SyntaxGroup}; use cairo_lang_syntax::node::{TypedStablePtr, ast}; @@ -131,6 +131,7 @@ pub fn setup_test_crate_ex( db: &dyn SemanticGroup, content: &str, crate_settings: Option<&str>, + cache_file: Option, ) -> CrateId { let file_id = FileLongId::Virtual(VirtualFile { parent: None, @@ -162,13 +163,14 @@ pub fn setup_test_crate_ex( name: "test".into(), file_id, settings: toml::to_string_pretty(&settings).unwrap(), + cache_file, } .intern(db) } /// See [setup_test_crate_ex]. pub fn setup_test_crate(db: &dyn SemanticGroup, content: &str) -> CrateId { - setup_test_crate_ex(db, content, None) + setup_test_crate_ex(db, content, None, None) } /// Sets up a module with given content, and returns its module id. @@ -176,8 +178,9 @@ pub fn setup_test_module_ex( db: &(dyn SemanticGroup + 'static), content: &str, crate_settings: Option<&str>, + cached_crate: Option, ) -> WithStringDiagnostics { - let crate_id = setup_test_crate_ex(db, content, crate_settings); + let crate_id = setup_test_crate_ex(db, content, crate_settings, cached_crate); let module_id = ModuleId::CrateRoot(crate_id); let file_id = db.module_main_file(module_id).unwrap(); @@ -195,7 +198,7 @@ pub fn setup_test_module( db: &(dyn SemanticGroup + 'static), content: &str, ) -> WithStringDiagnostics { - setup_test_module_ex(db, content, None) + setup_test_module_ex(db, content, None, None) } /// Helper struct for the return value of [setup_test_function]. @@ -216,13 +219,15 @@ pub fn setup_test_function_ex( function_name: &str, module_code: &str, crate_settings: Option<&str>, + cache_crate: Option, ) -> WithStringDiagnostics { let content = if module_code.is_empty() { function_code.to_string() } else { format!("{module_code}\n{function_code}") }; - let (test_module, diagnostics) = setup_test_module_ex(db, &content, crate_settings).split(); + let (test_module, diagnostics) = + setup_test_module_ex(db, &content, crate_settings, cache_crate).split(); let generic_function_id = db .module_item_by_name(test_module.module_id, function_name.into()) .expect("Failed to load module") @@ -253,7 +258,7 @@ pub fn setup_test_function( function_name: &str, module_code: &str, ) -> WithStringDiagnostics { - setup_test_function_ex(db, function_code, function_name, module_code, None) + setup_test_function_ex(db, function_code, function_name, module_code, None, None) } /// Helper struct for the return value of [setup_test_expr] and [setup_test_block]. @@ -277,7 +282,7 @@ pub fn setup_test_expr( ) -> WithStringDiagnostics { let function_code = format!("fn test_func() {{ {function_body} {{\n{expr_code}\n}}; }}"); let (test_function, diagnostics) = - setup_test_function_ex(db, &function_code, "test_func", module_code, crate_settings) + setup_test_function_ex(db, &function_code, "test_func", module_code, crate_settings, None) .split(); let semantic::ExprBlock { statements, .. } = extract_matches!( db.expr_semantic(test_function.function_id, test_function.body), @@ -353,6 +358,7 @@ pub fn test_function_diagnostics( inputs["function_name"].as_str(), inputs["module_code"].as_str(), inputs.get("crate_settings").map(|x| x.as_str()), + None, ) .get_diagnostics(); let error = verify_diagnostics_expectation(args, &diagnostics); diff --git a/crates/cairo-lang-syntax-codegen/src/generator.rs b/crates/cairo-lang-syntax-codegen/src/generator.rs index d1697d482cd..bc9b04cc5c5 100644 --- a/crates/cairo-lang-syntax-codegen/src/generator.rs +++ b/crates/cairo-lang-syntax-codegen/src/generator.rs @@ -63,6 +63,7 @@ fn generate_kinds_code() -> rust::Tokens { let mut tokens = quote! { $("// Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`.\n") use core::fmt; + use serde::{Deserialize, Serialize}; }; // SyntaxKind. @@ -75,7 +76,7 @@ fn generate_kinds_code() -> rust::Tokens { name_tokens(&spec, |k| matches!(k, NodeKind::Terminal { is_keyword, .. } if *is_keyword)); tokens.extend(quote! { - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum SyntaxKind { $(for t in kinds => $t,) } diff --git a/crates/cairo-lang-syntax/Cargo.toml b/crates/cairo-lang-syntax/Cargo.toml index 55788f3a019..6581efbd480 100644 --- a/crates/cairo-lang-syntax/Cargo.toml +++ b/crates/cairo-lang-syntax/Cargo.toml @@ -16,6 +16,7 @@ num-traits = { workspace = true, default-features = true } salsa.workspace = true smol_str.workspace = true unescaper.workspace = true +serde = { workspace = true, default-features = true } [dev-dependencies] env_logger.workspace = true diff --git a/crates/cairo-lang-syntax/src/node/kind.rs b/crates/cairo-lang-syntax/src/node/kind.rs index de170ed12a9..ae16a7b99c6 100644 --- a/crates/cairo-lang-syntax/src/node/kind.rs +++ b/crates/cairo-lang-syntax/src/node/kind.rs @@ -1,6 +1,8 @@ // Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`. use core::fmt; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] + +use serde::{Deserialize, Serialize}; +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum SyntaxKind { Trivia, ExprList,