cairo_lang_compiler/
db.rs1use std::sync::Arc;
2
3use anyhow::{Result, anyhow, bail};
4use cairo_lang_defs::db::{DefsDatabase, DefsGroup, init_defs_group, try_ext_as_virtual_impl};
5use cairo_lang_filesystem::cfg::CfgSet;
6use cairo_lang_filesystem::db::{
7 AsFilesGroupMut, CORELIB_VERSION, ExternalFiles, FilesDatabase, FilesGroup, FilesGroupEx,
8 init_dev_corelib, init_files_group,
9};
10use cairo_lang_filesystem::detect::detect_corelib;
11use cairo_lang_filesystem::flag::Flag;
12use cairo_lang_filesystem::ids::{CrateId, FlagId, VirtualFile};
13use cairo_lang_lowering::db::{LoweringDatabase, LoweringGroup, init_lowering_group};
14use cairo_lang_parser::db::{ParserDatabase, ParserGroup};
15use cairo_lang_project::ProjectConfig;
16use cairo_lang_semantic::db::{
17 PluginSuiteInput, SemanticDatabase, SemanticGroup, init_semantic_group,
18};
19use cairo_lang_semantic::inline_macros::get_default_plugin_suite;
20use cairo_lang_semantic::plugin::PluginSuite;
21use cairo_lang_sierra_generator::db::{SierraGenDatabase, SierraGenGroup};
22use cairo_lang_syntax::node::db::{SyntaxDatabase, SyntaxGroup};
23use cairo_lang_utils::Upcast;
24
25use crate::InliningStrategy;
26use crate::project::update_crate_roots_from_project_config;
27
28#[salsa::database(
29 DefsDatabase,
30 FilesDatabase,
31 LoweringDatabase,
32 ParserDatabase,
33 SemanticDatabase,
34 SierraGenDatabase,
35 SyntaxDatabase
36)]
37pub struct RootDatabase {
38 storage: salsa::Storage<RootDatabase>,
39}
40impl salsa::Database for RootDatabase {}
41impl ExternalFiles for RootDatabase {
42 fn try_ext_as_virtual(&self, external_id: salsa::InternId) -> Option<VirtualFile> {
43 try_ext_as_virtual_impl(self.upcast(), external_id)
44 }
45}
46impl salsa::ParallelDatabase for RootDatabase {
47 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
48 salsa::Snapshot::new(RootDatabase { storage: self.storage.snapshot() })
49 }
50}
51impl RootDatabase {
52 fn new(default_plugin_suite: PluginSuite, inlining_strategy: InliningStrategy) -> Self {
53 let mut res = Self { storage: Default::default() };
54 init_files_group(&mut res);
55 init_lowering_group(&mut res, inlining_strategy);
56 init_defs_group(&mut res);
57 init_semantic_group(&mut res);
58
59 let suite = res.intern_plugin_suite(default_plugin_suite);
60 res.set_default_plugins_from_suite(suite);
61
62 res
63 }
64
65 pub fn empty() -> Self {
66 Self::builder().clear_plugins().build().unwrap()
67 }
68
69 pub fn builder() -> RootDatabaseBuilder {
70 RootDatabaseBuilder::new()
71 }
72
73 pub fn snapshot(&self) -> RootDatabase {
75 RootDatabase { storage: self.storage.snapshot() }
76 }
77}
78
79impl Default for RootDatabase {
80 fn default() -> Self {
81 Self::builder().build().unwrap()
82 }
83}
84
85#[derive(Clone, Debug)]
86pub struct RootDatabaseBuilder {
87 default_plugin_suite: PluginSuite,
88 detect_corelib: bool,
89 auto_withdraw_gas: bool,
90 panic_backtrace: bool,
91 project_config: Option<Box<ProjectConfig>>,
92 cfg_set: Option<CfgSet>,
93 inlining_strategy: InliningStrategy,
94}
95
96impl RootDatabaseBuilder {
97 fn new() -> Self {
98 Self {
99 default_plugin_suite: get_default_plugin_suite(),
100 detect_corelib: false,
101 auto_withdraw_gas: true,
102 panic_backtrace: false,
103 project_config: None,
104 cfg_set: None,
105 inlining_strategy: InliningStrategy::Default,
106 }
107 }
108
109 pub fn with_default_plugin_suite(&mut self, suite: PluginSuite) -> &mut Self {
110 self.default_plugin_suite.add(suite);
111 self
112 }
113
114 pub fn clear_plugins(&mut self) -> &mut Self {
115 self.default_plugin_suite = get_default_plugin_suite();
116 self
117 }
118
119 pub fn with_inlining_strategy(&mut self, inlining_strategy: InliningStrategy) -> &mut Self {
120 self.inlining_strategy = inlining_strategy;
121 self
122 }
123
124 pub fn detect_corelib(&mut self) -> &mut Self {
125 self.detect_corelib = true;
126 self
127 }
128
129 pub fn with_project_config(&mut self, config: ProjectConfig) -> &mut Self {
130 self.project_config = Some(Box::new(config));
131 self
132 }
133
134 pub fn with_cfg(&mut self, cfg_set: impl Into<CfgSet>) -> &mut Self {
135 self.cfg_set = Some(cfg_set.into());
136 self
137 }
138
139 pub fn skip_auto_withdraw_gas(&mut self) -> &mut Self {
140 self.auto_withdraw_gas = false;
141 self
142 }
143
144 pub fn with_panic_backtrace(&mut self) -> &mut Self {
145 self.panic_backtrace = true;
146 self
147 }
148
149 pub fn build(&mut self) -> Result<RootDatabase> {
150 let mut db = RootDatabase::new(self.default_plugin_suite.clone(), self.inlining_strategy);
155
156 if let Some(cfg_set) = &self.cfg_set {
157 db.use_cfg(cfg_set);
158 }
159
160 if self.detect_corelib {
161 let path =
162 detect_corelib().ok_or_else(|| anyhow!("Failed to find development corelib."))?;
163 init_dev_corelib(&mut db, path)
164 }
165
166 let add_withdraw_gas_flag_id = FlagId::new(db.upcast(), "add_withdraw_gas");
167 db.set_flag(
168 add_withdraw_gas_flag_id,
169 Some(Arc::new(Flag::AddWithdrawGas(self.auto_withdraw_gas))),
170 );
171 let panic_backtrace_flag_id = FlagId::new(db.upcast(), "panic_backtrace");
172 db.set_flag(
173 panic_backtrace_flag_id,
174 Some(Arc::new(Flag::PanicBacktrace(self.panic_backtrace))),
175 );
176
177 if let Some(config) = &self.project_config {
178 update_crate_roots_from_project_config(&mut db, config.as_ref());
179 }
180 validate_corelib(&db)?;
181
182 Ok(db)
183 }
184}
185
186pub fn validate_corelib(db: &(dyn FilesGroup + 'static)) -> Result<()> {
188 let Some(config) = db.crate_config(CrateId::core(db)) else {
189 return Ok(());
190 };
191 let Some(found) = config.settings.version else {
192 return Ok(());
193 };
194 let Ok(expected) = semver::Version::parse(CORELIB_VERSION) else {
195 return Ok(());
196 };
197 if found == expected {
198 return Ok(());
199 }
200 let path_part = match config.root {
201 cairo_lang_filesystem::ids::Directory::Real(path) => {
202 format!(" for `{}`", path.to_string_lossy())
203 }
204 cairo_lang_filesystem::ids::Directory::Virtual { .. } => "".to_string(),
205 };
206 bail!("Corelib version mismatch: expected `{expected}`, found `{found}`{path_part}.");
207}
208
209impl AsFilesGroupMut for RootDatabase {
210 fn as_files_group_mut(&mut self) -> &mut (dyn FilesGroup + 'static) {
211 self
212 }
213}
214impl Upcast<dyn FilesGroup> for RootDatabase {
215 fn upcast(&self) -> &(dyn FilesGroup + 'static) {
216 self
217 }
218}
219impl Upcast<dyn SyntaxGroup> for RootDatabase {
220 fn upcast(&self) -> &(dyn SyntaxGroup + 'static) {
221 self
222 }
223}
224impl Upcast<dyn DefsGroup> for RootDatabase {
225 fn upcast(&self) -> &(dyn DefsGroup + 'static) {
226 self
227 }
228}
229impl Upcast<dyn SemanticGroup> for RootDatabase {
230 fn upcast(&self) -> &(dyn SemanticGroup + 'static) {
231 self
232 }
233}
234impl Upcast<dyn LoweringGroup> for RootDatabase {
235 fn upcast(&self) -> &(dyn LoweringGroup + 'static) {
236 self
237 }
238}
239impl Upcast<dyn SierraGenGroup> for RootDatabase {
240 fn upcast(&self) -> &(dyn SierraGenGroup + 'static) {
241 self
242 }
243}
244impl Upcast<dyn ParserGroup> for RootDatabase {
245 fn upcast(&self) -> &(dyn ParserGroup + 'static) {
246 self
247 }
248}