diff --git a/crates/cairo-lang-compiler/src/db.rs b/crates/cairo-lang-compiler/src/db.rs index 04e8b4339cf..d9061c458ef 100644 --- a/crates/cairo-lang-compiler/src/db.rs +++ b/crates/cairo-lang-compiler/src/db.rs @@ -1,5 +1,10 @@ use anyhow::{Result, anyhow, bail}; -use cairo_lang_defs::db::{init_defs_group, init_external_files}; +use cairo_lang_defs::db::{ + InlineMacroPluginOverrideStorage, InlineMacroPluginOverrideView, MacroPluginOverrideStorage, + MacroPluginOverrideView, init_defs_group, init_external_files, + new_inline_macro_plugin_override_storage, new_macro_plugin_override_storage, + register_inline_macro_plugin_override_view, register_macro_plugin_override_view, +}; use cairo_lang_diagnostics::Maybe; use cairo_lang_filesystem::cfg::CfgSet; use cairo_lang_filesystem::db::{ @@ -16,7 +21,11 @@ use cairo_lang_lowering::optimizations::config::Optimizations; use cairo_lang_lowering::utils::InliningStrategy; use cairo_lang_project::ProjectConfig; use cairo_lang_runnable_utils::builder::RunnableBuilder; -use cairo_lang_semantic::db::{PluginSuiteInput, init_semantic_group}; +use cairo_lang_semantic::db::{ + AnalyzerPluginOverrideStorage, AnalyzerPluginOverrideView, PluginSuiteInput, + init_semantic_group, new_analyzer_plugin_override_storage, + register_analyzer_plugin_override_view, +}; use cairo_lang_semantic::inline_macros::get_default_plugin_suite; use cairo_lang_semantic::plugin::PluginSuite; use cairo_lang_sierra_generator::db::init_sierra_gen_group; @@ -75,6 +84,9 @@ pub struct RootDatabase { storage: salsa::Storage, file_contents: FileContentStorage, crate_configs: CrateConfigStorage, + macro_plugin_overrides: MacroPluginOverrideStorage, + inline_macro_plugin_overrides: InlineMacroPluginOverrideStorage, + analyzer_plugin_overrides: AnalyzerPluginOverrideStorage, } #[salsa::db] impl salsa::Database for RootDatabase {} @@ -88,6 +100,21 @@ impl CrateConfigView for RootDatabase { Some(&self.crate_configs) } } +impl MacroPluginOverrideView for RootDatabase { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + Some(&self.macro_plugin_overrides) + } +} +impl InlineMacroPluginOverrideView for RootDatabase { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + Some(&self.inline_macro_plugin_overrides) + } +} +impl AnalyzerPluginOverrideView for RootDatabase { + fn analyzer_plugin_override_storage(&self) -> Option<&AnalyzerPluginOverrideStorage> { + Some(&self.analyzer_plugin_overrides) + } +} impl CloneableDatabase for RootDatabase { fn dyn_clone(&self) -> Box { Box::new(self.clone()) @@ -100,10 +127,16 @@ impl RootDatabase { storage: Default::default(), file_contents: Default::default(), crate_configs: Default::default(), + macro_plugin_overrides: new_macro_plugin_override_storage(), + inline_macro_plugin_overrides: new_inline_macro_plugin_override_storage(), + analyzer_plugin_overrides: new_analyzer_plugin_override_storage(), }; register_files_group_view(&res); register_crate_config_view(&res); + register_macro_plugin_override_view(&res); + register_inline_macro_plugin_override_view(&res); + register_analyzer_plugin_override_view(&res); init_external_files(&mut res); init_files_group(&mut res); init_lowering_group(&mut res, optimizations, Some(estimate_code_size)); @@ -130,6 +163,9 @@ impl RootDatabase { storage: self.storage.clone(), file_contents: self.file_contents.clone(), crate_configs: self.crate_configs.clone(), + macro_plugin_overrides: self.macro_plugin_overrides.clone(), + inline_macro_plugin_overrides: self.inline_macro_plugin_overrides.clone(), + analyzer_plugin_overrides: self.analyzer_plugin_overrides.clone(), } } } diff --git a/crates/cairo-lang-defs/src/db.rs b/crates/cairo-lang-defs/src/db.rs index b3b518fdf77..ce3c16f75c0 100644 --- a/crates/cairo-lang-defs/src/db.rs +++ b/crates/cairo-lang-defs/src/db.rs @@ -1,5 +1,7 @@ -use std::collections::VecDeque; -use std::sync::Arc; +use std::collections::{HashMap, VecDeque}; +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::ptr::NonNull; +use std::sync::{Arc, RwLock}; use cairo_lang_diagnostics::{ DiagnosticNote, Maybe, MaybeAsRef, PluginFileDiagnosticNotes, ToMaybe, skip_diagnostic, @@ -23,6 +25,7 @@ use cairo_lang_utils::Intern; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use cairo_lang_utils::ordered_hash_set::OrderedHashSet; use itertools::{Itertools, chain}; +use salsa::plumbing::views; use salsa::{Database, Setter}; use crate::cache::{DefCacheLoadingData, load_cached_crate_modules}; @@ -34,43 +37,135 @@ use crate::plugin_utils::try_extract_unnamed_arg; pub struct DefsGroupInput { #[returns(ref)] pub default_macro_plugins: Option>, - #[returns(ref)] - pub macro_plugin_overrides: Option>>, + pub macro_plugin_overrides_revision: u64, #[returns(ref)] pub default_inline_macro_plugins: Option>, - #[returns(ref)] - pub inline_macro_plugin_overrides: Option< - OrderedHashMap>>, - >, + pub inline_macro_plugin_overrides_revision: u64, } /// Returns a reference to the inputs of [DefsGroup]. /// The reference is also used to set the inputs to new values. #[salsa::tracked(returns(ref))] pub fn defs_group_input(db: &dyn Database) -> DefsGroupInput { - DefsGroupInput::new(db, None, None, None, None) + DefsGroupInput::new(db, None, 0, None, 0) } -/// Salsa database interface. -/// See [`super::ids`] for further details. -pub trait DefsGroup: Database { - fn default_macro_plugins_input(&self) -> &[MacroPluginLongId] { - defs_group_input(self.as_dyn_database()).default_macro_plugins(self).as_ref().unwrap() +#[salsa::input] +pub struct MacroPluginOverride { + #[returns(ref)] + pub plugins: Option>, +} + +#[salsa::input] +pub struct InlineMacroPluginOverride { + #[returns(ref)] + pub plugins: Option>>, +} + +pub type MacroPluginOverrideStorage = Arc>>; +pub type InlineMacroPluginOverrideStorage = + Arc>>; + +pub fn new_macro_plugin_override_storage() -> MacroPluginOverrideStorage { + Default::default() +} + +pub fn new_inline_macro_plugin_override_storage() -> InlineMacroPluginOverrideStorage { + Default::default() +} + +pub trait MacroPluginOverrideView: Database { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + None } - fn macro_plugin_overrides_input( + fn macro_plugin_override_for_input( &self, - ) -> &OrderedHashMap> { - defs_group_input(self.as_dyn_database()).macro_plugin_overrides(self).as_ref().unwrap() + crate_input: &CrateInput, + ) -> Option { + self.macro_plugin_override_storage()?.read().unwrap().get(crate_input).copied() + } +} + +pub trait InlineMacroPluginOverrideView: Database { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + None } - fn inline_macro_plugin_overrides_input( + fn inline_macro_plugin_override_for_input( &self, - ) -> &OrderedHashMap>> { - defs_group_input(self.as_dyn_database()) - .inline_macro_plugin_overrides(self) - .as_ref() - .unwrap() + crate_input: &CrateInput, + ) -> Option { + self.inline_macro_plugin_override_storage()?.read().unwrap().get(crate_input).copied() + } +} + +fn cast_macro_plugin_override_view( + db: NonNull, +) -> NonNull { + let db_ref = unsafe { db.as_ref() }; + NonNull::from(db_ref as &dyn MacroPluginOverrideView) +} + +fn macro_plugin_override_view(db: &dyn Database) -> &dyn MacroPluginOverrideView { + let caster = catch_unwind(AssertUnwindSafe(|| { + *views(db).downcaster_for::() + })) + .ok(); + + let caster = caster.expect("granular macro plugin override view is not registered"); + unsafe { caster.downcast_unchecked(db.into()) } +} + +pub fn register_macro_plugin_override_view(db: &Db) +where + Db: Database + MacroPluginOverrideView + 'static, +{ + views(db).add::(cast_macro_plugin_override_view::); +} + +fn cast_inline_macro_plugin_override_view( + db: NonNull, +) -> NonNull { + let db_ref = unsafe { db.as_ref() }; + NonNull::from(db_ref as &dyn InlineMacroPluginOverrideView) +} + +fn inline_macro_plugin_override_view(db: &dyn Database) -> &dyn InlineMacroPluginOverrideView { + let caster = catch_unwind(AssertUnwindSafe(|| { + *views(db).downcaster_for::() + })) + .ok(); + + let caster = caster.expect("granular inline macro plugin override view is not registered"); + unsafe { caster.downcast_unchecked(db.into()) } +} + +pub fn register_inline_macro_plugin_override_view(db: &Db) +where + Db: Database + InlineMacroPluginOverrideView + 'static, +{ + views(db) + .add::(cast_inline_macro_plugin_override_view::); +} + +fn macro_plugin_override_storage(db: &dyn Database) -> &MacroPluginOverrideStorage { + macro_plugin_override_view(db) + .macro_plugin_override_storage() + .expect("granular macro plugin override storage is not available") +} + +fn inline_macro_plugin_override_storage(db: &dyn Database) -> &InlineMacroPluginOverrideStorage { + inline_macro_plugin_override_view(db) + .inline_macro_plugin_override_storage() + .expect("granular inline macro plugin override storage is not available") +} + +/// Salsa database interface. +/// See [`super::ids`] for further details. +pub trait DefsGroup: Database { + fn default_macro_plugins_input(&self) -> &[MacroPluginLongId] { + defs_group_input(self.as_dyn_database()).default_macro_plugins(self).as_ref().unwrap() } fn default_inline_macro_plugins_input( @@ -374,8 +469,8 @@ impl DefsGroup for T {} /// Initializes the [`DefsGroup`] database to a proper state. pub fn init_defs_group(db: &mut dyn Database) { - defs_group_input(db).set_macro_plugin_overrides(db).to(Some(OrderedHashMap::default())); - defs_group_input(db).set_inline_macro_plugin_overrides(db).to(Some(OrderedHashMap::default())); + defs_group_input(db).set_macro_plugin_overrides_revision(db).to(0); + defs_group_input(db).set_inline_macro_plugin_overrides_revision(db).to(0); } #[salsa::tracked(returns(ref))] @@ -394,12 +489,21 @@ pub fn default_macro_plugins<'db>(db: &'db dyn Database) -> &'db [MacroPluginId< pub fn macro_plugin_overrides<'db>( db: &'db dyn Database, ) -> OrderedHashMap, Vec>> { - let inp = db.macro_plugin_overrides_input(); - inp.iter() + let _ = defs_group_input(db).macro_plugin_overrides_revision(db); + macro_plugin_override_storage(db) + .read() + .unwrap() + .iter() .map(|(crate_id, plugins)| { ( crate_id.clone().into_crate_long_id(db).intern(db), - plugins.iter().map(|plugin| plugin.clone().intern(db)).collect(), + plugins + .plugins(db) + .as_ref() + .expect("granular macro plugin override handle should always contain plugins") + .iter() + .map(|plugin| plugin.clone().intern(db)) + .collect(), ) }) .collect() @@ -409,12 +513,21 @@ pub fn macro_plugin_overrides<'db>( pub fn inline_macro_plugin_overrides<'db>( db: &'db dyn Database, ) -> OrderedHashMap, OrderedHashMap>> { - let inp = db.inline_macro_plugin_overrides_input(); - inp.iter() + let _ = defs_group_input(db).inline_macro_plugin_overrides_revision(db); + inline_macro_plugin_override_storage(db) + .read() + .unwrap() + .iter() .map(|(crate_id, plugins)| { ( crate_id.clone().into_crate_long_id(db).intern(db), plugins + .plugins(db) + .as_ref() + .expect( + "granular inline macro plugin override handle should always contain \ + plugins", + ) .iter() .map(|(name, plugin)| (name.clone(), plugin.clone().intern(db))) .collect(), @@ -447,6 +560,69 @@ fn crate_inline_macro_plugins<'db>( .unwrap_or_else(|| db.default_inline_macro_plugins()) } +pub fn set_macro_plugin_overrides_for_input( + db: &mut Db, + crate_input: CrateInput, + plugins: Option>, +) { + let storage = macro_plugin_override_storage(db); + let handle = if let Some(handle) = storage.read().unwrap().get(&crate_input).copied() { + handle + } else { + let handle = MacroPluginOverride::new(db, None); + storage.write().unwrap().insert(crate_input, handle); + let next = defs_group_input(db).macro_plugin_overrides_revision(db).saturating_add(1); + defs_group_input(db).set_macro_plugin_overrides_revision(db).to(next); + handle + }; + handle.set_plugins(db).to(plugins); +} + +pub fn set_inline_macro_plugin_overrides_for_input( + db: &mut Db, + crate_input: CrateInput, + plugins: Option>>, +) { + let storage = inline_macro_plugin_override_storage(db); + let handle = if let Some(handle) = storage.read().unwrap().get(&crate_input).copied() { + handle + } else { + let handle = InlineMacroPluginOverride::new(db, None); + storage.write().unwrap().insert(crate_input, handle); + let next = + defs_group_input(db).inline_macro_plugin_overrides_revision(db).saturating_add(1); + defs_group_input(db).set_inline_macro_plugin_overrides_revision(db).to(next); + handle + }; + handle.set_plugins(db).to(plugins); +} + +pub fn snapshot_macro_plugin_overrides( + db: &dyn Database, +) -> OrderedHashMap> { + macro_plugin_override_storage(db) + .read() + .unwrap() + .iter() + .filter_map(|(crate_input, handle)| { + Some((crate_input.clone(), handle.plugins(db).as_ref()?.clone())) + }) + .collect() +} + +pub fn snapshot_inline_macro_plugin_overrides( + db: &dyn Database, +) -> OrderedHashMap>> { + inline_macro_plugin_override_storage(db) + .read() + .unwrap() + .iter() + .filter_map(|(crate_input, handle)| { + Some((crate_input.clone(), handle.plugins(db).as_ref()?.clone())) + }) + .collect() +} + #[salsa::tracked(returns(ref))] fn allowed_attributes<'db>( db: &'db dyn Database, @@ -1876,14 +2052,14 @@ pub trait DefsGroupEx: DefsGroup { &'db mut self, crate_id: CrateId<'db>, plugins: Arc>>, - ) { - let crate_input = self.crate_input(crate_id); - let mut overrides = self.macro_plugin_overrides_input().clone(); - let plugins = plugins.iter().map(|plugin| plugin.long(self).clone()).collect(); - overrides.insert(crate_input.clone(), plugins); - defs_group_input(self.as_dyn_database()) - .set_macro_plugin_overrides(self) - .to(Some(overrides)); + ) where + Self: Sized, + { + let crate_input = self.crate_input(crate_id).clone(); + let plugins = Arc::<[MacroPluginLongId]>::from( + plugins.iter().map(|plugin| plugin.long(self).clone()).collect::>(), + ); + set_macro_plugin_overrides_for_input(self, crate_input, Some(plugins)); } /// Overrides the default inline macro plugins available for [`CrateId`] with `plugins`. @@ -1893,19 +2069,17 @@ pub trait DefsGroupEx: DefsGroup { &'db mut self, crate_id: CrateId<'db>, plugins: Arc>>, - ) { - let crate_input = self.crate_input(crate_id); - let mut overrides = self.inline_macro_plugin_overrides_input().clone(); + ) where + Self: Sized, + { + let crate_input = self.crate_input(crate_id).clone(); let plugins = Arc::new( plugins .iter() .map(|(name, plugin)| (name.clone(), plugin.long(self).clone())) .collect(), ); - overrides.insert(crate_input.clone(), plugins); - defs_group_input(self.as_dyn_database()) - .set_inline_macro_plugin_overrides(self) - .to(Some(overrides)); + set_inline_macro_plugin_overrides_for_input(self, crate_input, Some(plugins)); } } diff --git a/crates/cairo-lang-defs/src/test.rs b/crates/cairo-lang-defs/src/test.rs index 499dced8ecd..83c863d27b1 100644 --- a/crates/cairo-lang-defs/src/test.rs +++ b/crates/cairo-lang-defs/src/test.rs @@ -19,7 +19,13 @@ use cairo_lang_utils::{Intern, extract_matches, try_extract_matches}; use indoc::indoc; use salsa::{Database, Setter}; -use crate::db::{DefsGroup, defs_group_input, init_defs_group, init_external_files}; +use crate::db::{ + DefsGroup, InlineMacroPluginOverrideStorage, InlineMacroPluginOverrideView, + MacroPluginOverrideStorage, MacroPluginOverrideView, defs_group_input, init_defs_group, + init_external_files, new_inline_macro_plugin_override_storage, + new_macro_plugin_override_storage, register_inline_macro_plugin_override_view, + register_macro_plugin_override_view, +}; use crate::ids::{ GenericParamLongId, MacroPluginLongId, ModuleId, ModuleItemId, NamedLanguageElementId, SubmoduleLongId, @@ -34,6 +40,8 @@ pub struct DatabaseForTesting { storage: salsa::Storage, file_contents: FileContentStorage, crate_configs: CrateConfigStorage, + macro_plugin_overrides: MacroPluginOverrideStorage, + inline_macro_plugin_overrides: InlineMacroPluginOverrideStorage, } #[salsa::db] impl salsa::Database for DatabaseForTesting {} @@ -47,6 +55,16 @@ impl CrateConfigView for DatabaseForTesting { Some(&self.crate_configs) } } +impl MacroPluginOverrideView for DatabaseForTesting { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + Some(&self.macro_plugin_overrides) + } +} +impl InlineMacroPluginOverrideView for DatabaseForTesting { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + Some(&self.inline_macro_plugin_overrides) + } +} impl Default for DatabaseForTesting { fn default() -> Self { @@ -54,10 +72,14 @@ impl Default for DatabaseForTesting { storage: Default::default(), file_contents: Default::default(), crate_configs: Default::default(), + macro_plugin_overrides: new_macro_plugin_override_storage(), + inline_macro_plugin_overrides: new_inline_macro_plugin_override_storage(), }; register_files_group_view(&res); register_crate_config_view(&res); + register_macro_plugin_override_view(&res); + register_inline_macro_plugin_override_view(&res); init_external_files(&mut res); init_files_group(&mut res); init_defs_group(&mut res); diff --git a/crates/cairo-lang-doc/src/tests/test_utils.rs b/crates/cairo-lang-doc/src/tests/test_utils.rs index 836009c3a60..1ce9483e3a9 100644 --- a/crates/cairo-lang-doc/src/tests/test_utils.rs +++ b/crates/cairo-lang-doc/src/tests/test_utils.rs @@ -1,5 +1,10 @@ use anyhow::{Result, anyhow}; -use cairo_lang_defs::db::{DefsGroup, init_defs_group}; +use cairo_lang_defs::db::{ + DefsGroup, InlineMacroPluginOverrideStorage, InlineMacroPluginOverrideView, + MacroPluginOverrideStorage, MacroPluginOverrideView, init_defs_group, + new_inline_macro_plugin_override_storage, new_macro_plugin_override_storage, + register_inline_macro_plugin_override_view, register_macro_plugin_override_view, +}; use cairo_lang_defs::ids::ModuleId; use cairo_lang_filesystem::db::{ CrateConfigStorage, CrateConfigView, CrateConfiguration, FileContentStorage, FileContentView, @@ -10,7 +15,11 @@ use cairo_lang_filesystem::detect::detect_corelib; use cairo_lang_filesystem::ids::{CrateId, Directory, FileLongId, SmolStrId}; use cairo_lang_filesystem::set_crate_config; use cairo_lang_parser::db::ParserGroup; -use cairo_lang_semantic::db::{PluginSuiteInput, init_semantic_group}; +use cairo_lang_semantic::db::{ + AnalyzerPluginOverrideStorage, AnalyzerPluginOverrideView, PluginSuiteInput, + init_semantic_group, new_analyzer_plugin_override_storage, + register_analyzer_plugin_override_view, +}; use cairo_lang_semantic::plugin::PluginSuite; use cairo_lang_utils::Intern; use salsa::Database; @@ -21,6 +30,9 @@ pub struct TestDatabase { storage: salsa::Storage, file_contents: FileContentStorage, crate_configs: CrateConfigStorage, + macro_plugin_overrides: MacroPluginOverrideStorage, + inline_macro_plugin_overrides: InlineMacroPluginOverrideStorage, + analyzer_plugin_overrides: AnalyzerPluginOverrideStorage, } #[salsa::db] impl salsa::Database for TestDatabase {} @@ -34,6 +46,21 @@ impl CrateConfigView for TestDatabase { Some(&self.crate_configs) } } +impl MacroPluginOverrideView for TestDatabase { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + Some(&self.macro_plugin_overrides) + } +} +impl InlineMacroPluginOverrideView for TestDatabase { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + Some(&self.inline_macro_plugin_overrides) + } +} +impl AnalyzerPluginOverrideView for TestDatabase { + fn analyzer_plugin_override_storage(&self) -> Option<&AnalyzerPluginOverrideStorage> { + Some(&self.analyzer_plugin_overrides) + } +} impl Default for TestDatabase { fn default() -> Self { @@ -41,10 +68,16 @@ impl Default for TestDatabase { storage: Default::default(), file_contents: Default::default(), crate_configs: Default::default(), + macro_plugin_overrides: new_macro_plugin_override_storage(), + inline_macro_plugin_overrides: new_inline_macro_plugin_override_storage(), + analyzer_plugin_overrides: new_analyzer_plugin_override_storage(), }; register_files_group_view(&res); register_crate_config_view(&res); + register_macro_plugin_override_view(&res); + register_inline_macro_plugin_override_view(&res); + register_analyzer_plugin_override_view(&res); init_files_group(&mut res); init_defs_group(&mut res); init_semantic_group(&mut res); diff --git a/crates/cairo-lang-lowering/src/test_utils.rs b/crates/cairo-lang-lowering/src/test_utils.rs index 53cbedc9e2a..efeeadd361a 100644 --- a/crates/cairo-lang-lowering/src/test_utils.rs +++ b/crates/cairo-lang-lowering/src/test_utils.rs @@ -1,15 +1,24 @@ use std::sync::{LazyLock, Mutex}; use cairo_lang_debug::DebugWithDb; -use cairo_lang_defs::db::{init_defs_group, init_external_files}; +use cairo_lang_defs::db::{ + InlineMacroPluginOverrideStorage, InlineMacroPluginOverrideView, MacroPluginOverrideStorage, + MacroPluginOverrideView, init_defs_group, init_external_files, + new_inline_macro_plugin_override_storage, new_macro_plugin_override_storage, + register_inline_macro_plugin_override_view, register_macro_plugin_override_view, +}; use cairo_lang_filesystem::db::{ CrateConfigStorage, CrateConfigView, FileContentView, init_dev_corelib, init_files_group, - register_crate_config_view, + register_crate_config_view, register_files_group_view, }; use cairo_lang_filesystem::detect::detect_corelib; use cairo_lang_filesystem::flag::{Flag, FlagsGroup}; use cairo_lang_filesystem::ids::FlagLongId; -use cairo_lang_semantic::db::{PluginSuiteInput, init_semantic_group}; +use cairo_lang_semantic::db::{ + AnalyzerPluginOverrideStorage, AnalyzerPluginOverrideView, PluginSuiteInput, + init_semantic_group, new_analyzer_plugin_override_storage, + register_analyzer_plugin_override_view, +}; use cairo_lang_semantic::inline_macros::get_default_plugin_suite; use salsa::Database; @@ -24,6 +33,9 @@ use crate::utils::InliningStrategy; pub struct LoweringDatabaseForTesting { storage: salsa::Storage, crate_configs: CrateConfigStorage, + macro_plugin_overrides: MacroPluginOverrideStorage, + inline_macro_plugin_overrides: InlineMacroPluginOverrideStorage, + analyzer_plugin_overrides: AnalyzerPluginOverrideStorage, } #[salsa::db] impl salsa::Database for LoweringDatabaseForTesting {} @@ -33,14 +45,36 @@ impl CrateConfigView for LoweringDatabaseForTesting { Some(&self.crate_configs) } } +impl MacroPluginOverrideView for LoweringDatabaseForTesting { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + Some(&self.macro_plugin_overrides) + } +} +impl InlineMacroPluginOverrideView for LoweringDatabaseForTesting { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + Some(&self.inline_macro_plugin_overrides) + } +} +impl AnalyzerPluginOverrideView for LoweringDatabaseForTesting { + fn analyzer_plugin_override_storage(&self) -> Option<&AnalyzerPluginOverrideStorage> { + Some(&self.analyzer_plugin_overrides) + } +} impl LoweringDatabaseForTesting { pub fn new() -> Self { let mut res = LoweringDatabaseForTesting { storage: Default::default(), crate_configs: Default::default(), + macro_plugin_overrides: new_macro_plugin_override_storage(), + inline_macro_plugin_overrides: new_inline_macro_plugin_override_storage(), + analyzer_plugin_overrides: new_analyzer_plugin_override_storage(), }; + register_files_group_view(&res); register_crate_config_view(&res); + register_macro_plugin_override_view(&res); + register_inline_macro_plugin_override_view(&res); + register_analyzer_plugin_override_view(&res); init_external_files(&mut res); init_files_group(&mut res); init_defs_group(&mut res); diff --git a/crates/cairo-lang-parser/src/utils.rs b/crates/cairo-lang-parser/src/utils.rs index 2c073d461c3..c34cce68162 100644 --- a/crates/cairo-lang-parser/src/utils.rs +++ b/crates/cairo-lang-parser/src/utils.rs @@ -1,7 +1,9 @@ use std::path::PathBuf; use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder}; -use cairo_lang_filesystem::db::{FileContentView, FilesGroup, init_files_group}; +use cairo_lang_filesystem::db::{ + FileContentView, FilesGroup, init_files_group, register_files_group_view, +}; use cairo_lang_filesystem::ids::{FileId, FileKind, FileLongId, SmolStrId, VirtualFile}; use cairo_lang_filesystem::span::{TextOffset, TextWidth}; use cairo_lang_primitive_token::{PrimitiveToken, ToPrimitiveTokenStream}; @@ -25,6 +27,7 @@ impl FileContentView for SimpleParserDatabase {} impl Default for SimpleParserDatabase { fn default() -> Self { let mut res = Self { storage: Default::default() }; + register_files_group_view(&res); init_files_group(&mut res); res } diff --git a/crates/cairo-lang-plugins/src/test.rs b/crates/cairo-lang-plugins/src/test.rs index 46ad85a899b..9ec4455b6bf 100644 --- a/crates/cairo-lang-plugins/src/test.rs +++ b/crates/cairo-lang-plugins/src/test.rs @@ -1,7 +1,13 @@ use std::default::Default; use std::sync::Arc; -use cairo_lang_defs::db::{DefsGroup, defs_group_input, init_defs_group, init_external_files}; +use cairo_lang_defs::db::{ + DefsGroup, InlineMacroPluginOverrideStorage, InlineMacroPluginOverrideView, + MacroPluginOverrideStorage, MacroPluginOverrideView, defs_group_input, init_defs_group, + init_external_files, new_inline_macro_plugin_override_storage, + new_macro_plugin_override_storage, register_inline_macro_plugin_override_view, + register_macro_plugin_override_view, +}; use cairo_lang_defs::ids::{MacroPluginLongId, ModuleId}; use cairo_lang_defs::plugin::{ MacroPlugin, MacroPluginMetadata, PluginDiagnostic, PluginGeneratedFile, PluginResult, @@ -59,6 +65,8 @@ pub struct DatabaseForTesting { storage: salsa::Storage, file_contents: FileContentStorage, crate_configs: CrateConfigStorage, + macro_plugin_overrides: MacroPluginOverrideStorage, + inline_macro_plugin_overrides: InlineMacroPluginOverrideStorage, } #[salsa::db] impl salsa::Database for DatabaseForTesting {} @@ -72,6 +80,16 @@ impl CrateConfigView for DatabaseForTesting { Some(&self.crate_configs) } } +impl MacroPluginOverrideView for DatabaseForTesting { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + Some(&self.macro_plugin_overrides) + } +} +impl InlineMacroPluginOverrideView for DatabaseForTesting { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + Some(&self.inline_macro_plugin_overrides) + } +} impl Default for DatabaseForTesting { fn default() -> Self { @@ -79,10 +97,14 @@ impl Default for DatabaseForTesting { storage: Default::default(), file_contents: Default::default(), crate_configs: Default::default(), + macro_plugin_overrides: new_macro_plugin_override_storage(), + inline_macro_plugin_overrides: new_inline_macro_plugin_override_storage(), }; register_files_group_view(&res); register_crate_config_view(&res); + register_macro_plugin_override_view(&res); + register_inline_macro_plugin_override_view(&res); init_external_files(&mut res); init_files_group(&mut res); init_defs_group(&mut res); diff --git a/crates/cairo-lang-semantic/src/db.rs b/crates/cairo-lang-semantic/src/db.rs index 4a03040ea7c..aae3d4749c8 100644 --- a/crates/cairo-lang-semantic/src/db.rs +++ b/crates/cairo-lang-semantic/src/db.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::collections::HashMap; +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::ptr::NonNull; +use std::sync::{Arc, RwLock}; use cairo_lang_defs::db::{DefsGroup, DefsGroupEx, defs_group_input}; use cairo_lang_defs::ids::{ @@ -15,6 +18,7 @@ use cairo_lang_utils::ordered_hash_set::OrderedHashSet; use cairo_lang_utils::unordered_hash_set::UnorderedHashSet; use cairo_lang_utils::{Intern, require, try_extract_matches}; use itertools::{Itertools, chain}; +use salsa::plumbing::views; use salsa::{Database, Setter}; use crate::SemanticDiagnostic; @@ -43,23 +47,71 @@ use crate::resolve::{ResolvedConcreteItem, ResolvedGenericItem, ResolverData}; pub struct SemanticGroupInput { #[returns(ref)] pub default_analyzer_plugins: Option>, - #[returns(ref)] - pub analyzer_plugin_overrides: Option>>, + pub analyzer_plugin_overrides_revision: u64, } #[salsa::tracked(returns(ref))] pub fn semantic_group_input(db: &dyn Database) -> SemanticGroupInput { - SemanticGroupInput::new(db, None, None) + SemanticGroupInput::new(db, None, 0) +} + +#[salsa::input] +pub struct AnalyzerPluginOverride { + #[returns(ref)] + pub plugins: Option>, +} + +pub type AnalyzerPluginOverrideStorage = Arc>>; + +pub fn new_analyzer_plugin_override_storage() -> AnalyzerPluginOverrideStorage { + Default::default() +} + +pub trait AnalyzerPluginOverrideView: Database { + fn analyzer_plugin_override_storage(&self) -> Option<&AnalyzerPluginOverrideStorage> { + None + } + + fn analyzer_plugin_override_for_input( + &self, + crate_input: &CrateInput, + ) -> Option { + self.analyzer_plugin_override_storage()?.read().unwrap().get(crate_input).copied() + } +} + +fn cast_analyzer_plugin_override_view( + db: NonNull, +) -> NonNull { + let db_ref = unsafe { db.as_ref() }; + NonNull::from(db_ref as &dyn AnalyzerPluginOverrideView) +} + +fn analyzer_plugin_override_view(db: &dyn Database) -> &dyn AnalyzerPluginOverrideView { + let caster = catch_unwind(AssertUnwindSafe(|| { + *views(db).downcaster_for::() + })) + .ok(); + + let caster = caster.expect("granular analyzer plugin override view is not registered"); + unsafe { caster.downcast_unchecked(db.into()) } +} + +pub fn register_analyzer_plugin_override_view(db: &Db) +where + Db: Database + AnalyzerPluginOverrideView + 'static, +{ + views(db).add::(cast_analyzer_plugin_override_view::); } fn default_analyzer_plugins_input(db: &dyn Database) -> &[AnalyzerPluginLongId] { semantic_group_input(db).default_analyzer_plugins(db).as_ref().unwrap() } -fn analyzer_plugin_overrides_input( - db: &dyn Database, -) -> &OrderedHashMap> { - semantic_group_input(db).analyzer_plugin_overrides(db).as_ref().unwrap() +fn analyzer_plugin_override_storage(db: &dyn Database) -> &AnalyzerPluginOverrideStorage { + analyzer_plugin_override_view(db) + .analyzer_plugin_override_storage() + .expect("granular analyzer plugin override storage is not available") } // Salsa database interface. @@ -116,12 +168,6 @@ pub trait SemanticGroup: Database { default_analyzer_plugins(self.as_dyn_database()) } - fn analyzer_plugin_overrides_input( - &self, - ) -> &OrderedHashMap> { - analyzer_plugin_overrides_input(self.as_dyn_database()) - } - /// Interned version of `analyzer_plugin_overrides_input`. fn analyzer_plugin_overrides<'db>( &'db self, @@ -160,7 +206,7 @@ impl SemanticGroup for T {} /// Initializes the [`SemanticGroup`] database to a proper state. pub fn init_semantic_group(db: &mut dyn Database) { - semantic_group_input(db).set_analyzer_plugin_overrides(db).to(Some(OrderedHashMap::default())); + semantic_group_input(db).set_analyzer_plugin_overrides_revision(db).to(0); } #[salsa::tracked] @@ -173,19 +219,65 @@ fn default_analyzer_plugins(db: &dyn Database) -> Arc>> fn analyzer_plugin_overrides( db: &dyn Database, ) -> Arc, Arc>>>> { - let inp = db.analyzer_plugin_overrides_input(); + let _ = semantic_group_input(db).analyzer_plugin_overrides_revision(db); Arc::new( - inp.iter() + analyzer_plugin_override_storage(db) + .read() + .unwrap() + .iter() .map(|(crate_input, plugins)| { ( crate_input.clone().into_crate_long_id(db).intern(db), - Arc::new(plugins.iter().map(|plugin| plugin.clone().intern(db)).collect_vec()), + Arc::new( + plugins + .plugins(db) + .as_ref() + .expect( + "granular analyzer plugin override handle should always contain \ + plugins", + ) + .iter() + .map(|plugin| plugin.clone().intern(db)) + .collect_vec(), + ), ) }) .collect(), ) } +pub fn set_analyzer_plugin_overrides_for_input( + db: &mut Db, + crate_input: CrateInput, + plugins: Option>, +) { + let storage = analyzer_plugin_override_storage(db); + let handle = if let Some(handle) = storage.read().unwrap().get(&crate_input).copied() { + handle + } else { + let handle = AnalyzerPluginOverride::new(db, None); + storage.write().unwrap().insert(crate_input, handle); + let next = + semantic_group_input(db).analyzer_plugin_overrides_revision(db).saturating_add(1); + semantic_group_input(db).set_analyzer_plugin_overrides_revision(db).to(next); + handle + }; + handle.set_plugins(db).to(plugins); +} + +pub fn snapshot_analyzer_plugin_overrides( + db: &dyn Database, +) -> OrderedHashMap> { + analyzer_plugin_override_storage(db) + .read() + .unwrap() + .iter() + .filter_map(|(crate_input, handle)| { + Some((crate_input.clone(), handle.plugins(db).as_ref()?.clone())) + }) + .collect() +} + #[salsa::tracked] fn module_semantic_diagnostics_tracked<'db>( db: &'db dyn Database, @@ -521,12 +613,17 @@ pub trait SemanticGroupEx: Database { &mut self, crate_id: CrateId<'_>, plugins: Arc<[AnalyzerPluginId<'_>]>, - ) { - let mut overrides = self.analyzer_plugin_overrides_input().clone(); - let plugins = plugins.iter().map(|plugin| plugin.long(self).clone()).collect_vec(); - overrides.insert(self.crate_input(crate_id).clone(), Arc::from(plugins)); - let db_ref = self.as_dyn_database(); - semantic_group_input(db_ref).set_analyzer_plugin_overrides(self).to(Some(overrides)); + ) where + Self: Sized, + { + let plugins = Arc::<[AnalyzerPluginLongId]>::from( + plugins.iter().map(|plugin| plugin.long(self).clone()).collect_vec(), + ); + set_analyzer_plugin_overrides_for_input( + self, + self.crate_input(crate_id).clone(), + Some(plugins), + ); } } @@ -601,7 +698,9 @@ pub trait PluginSuiteInput: Database { &mut self, crate_id: CrateId<'_>, suite: InternedPluginSuite<'_>, - ) { + ) where + Self: Sized, + { let InternedPluginSuite { macro_plugins, inline_macro_plugins, analyzer_plugins } = suite; self.set_override_crate_macro_plugins(crate_id, Arc::new(macro_plugins.to_vec())); diff --git a/crates/cairo-lang-semantic/src/test_utils.rs b/crates/cairo-lang-semantic/src/test_utils.rs index 1a0daee7c3d..9ecbe12fd07 100644 --- a/crates/cairo-lang-semantic/src/test_utils.rs +++ b/crates/cairo-lang-semantic/src/test_utils.rs @@ -1,6 +1,11 @@ use std::sync::{LazyLock, Mutex}; -use cairo_lang_defs::db::{DefsGroup, init_defs_group, init_external_files}; +use cairo_lang_defs::db::{ + DefsGroup, InlineMacroPluginOverrideStorage, InlineMacroPluginOverrideView, + MacroPluginOverrideStorage, MacroPluginOverrideView, init_defs_group, init_external_files, + new_inline_macro_plugin_override_storage, new_macro_plugin_override_storage, + register_inline_macro_plugin_override_view, register_macro_plugin_override_view, +}; use cairo_lang_defs::ids::{FunctionWithBodyId, ModuleId}; use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder}; use cairo_lang_filesystem::db::{ @@ -19,7 +24,11 @@ use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use cairo_lang_utils::{Intern, OptionFrom, extract_matches}; use salsa::Database; -use crate::db::{PluginSuiteInput, SemanticGroup, init_semantic_group}; +use crate::db::{ + AnalyzerPluginOverrideStorage, AnalyzerPluginOverrideView, PluginSuiteInput, SemanticGroup, + init_semantic_group, new_analyzer_plugin_override_storage, + register_analyzer_plugin_override_view, +}; use crate::inline_macros::get_default_plugin_suite; use crate::items::function_with_body::FunctionWithBodySemantic; use crate::items::functions::GenericFunctionId; @@ -33,6 +42,9 @@ pub struct SemanticDatabaseForTesting { storage: salsa::Storage, file_contents: FileContentStorage, crate_configs: CrateConfigStorage, + macro_plugin_overrides: MacroPluginOverrideStorage, + inline_macro_plugin_overrides: InlineMacroPluginOverrideStorage, + analyzer_plugin_overrides: AnalyzerPluginOverrideStorage, } #[salsa::db] @@ -47,6 +59,21 @@ impl CrateConfigView for SemanticDatabaseForTesting { Some(&self.crate_configs) } } +impl MacroPluginOverrideView for SemanticDatabaseForTesting { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + Some(&self.macro_plugin_overrides) + } +} +impl InlineMacroPluginOverrideView for SemanticDatabaseForTesting { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + Some(&self.inline_macro_plugin_overrides) + } +} +impl AnalyzerPluginOverrideView for SemanticDatabaseForTesting { + fn analyzer_plugin_override_storage(&self) -> Option<&AnalyzerPluginOverrideStorage> { + Some(&self.analyzer_plugin_overrides) + } +} impl SemanticDatabaseForTesting { pub fn new_empty() -> Self { @@ -59,9 +86,15 @@ impl SemanticDatabaseForTesting { storage: Default::default(), file_contents: Default::default(), crate_configs: Default::default(), + macro_plugin_overrides: new_macro_plugin_override_storage(), + inline_macro_plugin_overrides: new_inline_macro_plugin_override_storage(), + analyzer_plugin_overrides: new_analyzer_plugin_override_storage(), }; register_files_group_view(&res); register_crate_config_view(&res); + register_macro_plugin_override_view(&res); + register_inline_macro_plugin_override_view(&res); + register_analyzer_plugin_override_view(&res); init_external_files(&mut res); init_files_group(&mut res); init_defs_group(&mut res); diff --git a/crates/cairo-lang-sierra-generator/src/test_utils.rs b/crates/cairo-lang-sierra-generator/src/test_utils.rs index 493108aed83..409af4ed49f 100644 --- a/crates/cairo-lang-sierra-generator/src/test_utils.rs +++ b/crates/cairo-lang-sierra-generator/src/test_utils.rs @@ -1,7 +1,12 @@ use std::sync::{LazyLock, Mutex}; use cairo_lang_defs as defs; -use cairo_lang_defs::db::{DefsGroup, init_defs_group, init_external_files}; +use cairo_lang_defs::db::{ + DefsGroup, InlineMacroPluginOverrideStorage, InlineMacroPluginOverrideView, + MacroPluginOverrideStorage, MacroPluginOverrideView, init_defs_group, init_external_files, + new_inline_macro_plugin_override_storage, new_macro_plugin_override_storage, + register_inline_macro_plugin_override_view, register_macro_plugin_override_view, +}; use cairo_lang_defs::ids::ModuleId; use cairo_lang_filesystem::db::{ CrateConfigStorage, CrateConfigView, FileContentStorage, FileContentView, init_dev_corelib, @@ -14,7 +19,11 @@ use cairo_lang_lowering as lowering; use cairo_lang_lowering::db::{LoweringGroup, lowering_group_input}; use cairo_lang_semantic as semantic; use cairo_lang_semantic::corelib::CorelibSemantic; -use cairo_lang_semantic::db::{PluginSuiteInput, SemanticGroup, init_semantic_group}; +use cairo_lang_semantic::db::{ + AnalyzerPluginOverrideStorage, AnalyzerPluginOverrideView, PluginSuiteInput, SemanticGroup, + init_semantic_group, new_analyzer_plugin_override_storage, + register_analyzer_plugin_override_view, +}; use cairo_lang_semantic::test_utils::setup_test_crate; use cairo_lang_sierra::ids::{ConcreteLibfuncId, GenericLibfuncId}; use cairo_lang_sierra::program; @@ -37,10 +46,12 @@ pub struct SierraGenDatabaseForTesting { storage: salsa::Storage, file_contents: FileContentStorage, crate_configs: CrateConfigStorage, + macro_plugin_overrides: MacroPluginOverrideStorage, + inline_macro_plugin_overrides: InlineMacroPluginOverrideStorage, + analyzer_plugin_overrides: AnalyzerPluginOverrideStorage, } #[salsa::db] impl Database for SierraGenDatabaseForTesting {} - impl FileContentView for SierraGenDatabaseForTesting { fn file_content_storage(&self) -> Option<&FileContentStorage> { Some(&self.file_contents) @@ -51,7 +62,21 @@ impl CrateConfigView for SierraGenDatabaseForTesting { Some(&self.crate_configs) } } - +impl MacroPluginOverrideView for SierraGenDatabaseForTesting { + fn macro_plugin_override_storage(&self) -> Option<&MacroPluginOverrideStorage> { + Some(&self.macro_plugin_overrides) + } +} +impl InlineMacroPluginOverrideView for SierraGenDatabaseForTesting { + fn inline_macro_plugin_override_storage(&self) -> Option<&InlineMacroPluginOverrideStorage> { + Some(&self.inline_macro_plugin_overrides) + } +} +impl AnalyzerPluginOverrideView for SierraGenDatabaseForTesting { + fn analyzer_plugin_override_storage(&self) -> Option<&AnalyzerPluginOverrideStorage> { + Some(&self.analyzer_plugin_overrides) + } +} impl CloneableDatabase for SierraGenDatabaseForTesting { fn dyn_clone(&self) -> Box { Box::new(self.clone()) @@ -81,9 +106,15 @@ impl SierraGenDatabaseForTesting { storage: Default::default(), file_contents: Default::default(), crate_configs: Default::default(), + macro_plugin_overrides: new_macro_plugin_override_storage(), + inline_macro_plugin_overrides: new_inline_macro_plugin_override_storage(), + analyzer_plugin_overrides: new_analyzer_plugin_override_storage(), }; register_files_group_view(&res); register_crate_config_view(&res); + register_macro_plugin_override_view(&res); + register_inline_macro_plugin_override_view(&res); + register_analyzer_plugin_override_view(&res); init_external_files(&mut res); init_files_group(&mut res); init_defs_group(&mut res); @@ -112,6 +143,9 @@ impl SierraGenDatabaseForTesting { storage: self.storage.clone(), file_contents: self.file_contents.clone(), crate_configs: self.crate_configs.clone(), + macro_plugin_overrides: self.macro_plugin_overrides.clone(), + inline_macro_plugin_overrides: self.inline_macro_plugin_overrides.clone(), + analyzer_plugin_overrides: self.analyzer_plugin_overrides.clone(), } } }