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_diagnostics::Maybe;
6use cairo_lang_filesystem::cfg::CfgSet;
7use cairo_lang_filesystem::db::{
8 CORELIB_VERSION, ExternalFiles, FilesDatabase, FilesGroup, FilesGroupEx, init_dev_corelib,
9 init_files_group,
10};
11use cairo_lang_filesystem::detect::detect_corelib;
12use cairo_lang_filesystem::flag::Flag;
13use cairo_lang_filesystem::ids::{CrateId, FlagId, VirtualFile};
14use cairo_lang_lowering::db::{
15 ExternalCodeSizeEstimator, LoweringDatabase, LoweringGroup, init_lowering_group,
16};
17use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
18use cairo_lang_parser::db::{ParserDatabase, ParserGroup};
19use cairo_lang_project::ProjectConfig;
20use cairo_lang_runnable_utils::builder::RunnableBuilder;
21use cairo_lang_semantic::db::{
22 PluginSuiteInput, SemanticDatabase, SemanticGroup, init_semantic_group,
23};
24use cairo_lang_semantic::inline_macros::get_default_plugin_suite;
25use cairo_lang_semantic::plugin::PluginSuite;
26use cairo_lang_sierra_generator::db::{SierraGenDatabase, SierraGenGroup};
27use cairo_lang_sierra_generator::program_generator::get_dummy_program_for_size_estimation;
28use cairo_lang_syntax::node::db::{SyntaxDatabase, SyntaxGroup};
29use cairo_lang_utils::Upcast;
30
31use crate::InliningStrategy;
32use crate::project::update_crate_roots_from_project_config;
33
34impl ExternalCodeSizeEstimator for RootDatabase {
35 fn estimate_size(&self, function_id: ConcreteFunctionWithBodyId) -> Maybe<isize> {
39 let program = get_dummy_program_for_size_estimation(self, function_id)?;
40
41 let n_dummy_functions = program.funcs.len() - 1;
43
44 let builder = match RunnableBuilder::new(program, Default::default()) {
46 Ok(builder) => builder,
47 Err(err) => {
48 if err.is_ap_overflow_error() {
49 return Ok(isize::MAX);
53 }
54
55 panic!("Failed to compile program to casm.");
56 }
57 };
58 let casm = builder.casm_program();
59 let total_size = casm.instructions.iter().map(|inst| inst.body.op_size()).sum::<usize>();
60
61 const DUMMY_FUNCTION_SIZE: usize = 3;
63 Ok((total_size - (n_dummy_functions * DUMMY_FUNCTION_SIZE)).try_into().unwrap_or(0))
64 }
65}
66
67#[salsa::database(
68 DefsDatabase,
69 FilesDatabase,
70 LoweringDatabase,
71 ParserDatabase,
72 SemanticDatabase,
73 SierraGenDatabase,
74 SyntaxDatabase
75)]
76pub struct RootDatabase {
77 storage: salsa::Storage<RootDatabase>,
78}
79impl salsa::Database for RootDatabase {}
80impl ExternalFiles for RootDatabase {
81 fn try_ext_as_virtual(&self, external_id: salsa::InternId) -> Option<VirtualFile> {
82 try_ext_as_virtual_impl(self, external_id)
83 }
84}
85impl salsa::ParallelDatabase for RootDatabase {
86 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
87 salsa::Snapshot::new(RootDatabase { storage: self.storage.snapshot() })
88 }
89}
90impl RootDatabase {
91 fn new(default_plugin_suite: PluginSuite, inlining_strategy: InliningStrategy) -> Self {
92 let mut res = Self { storage: Default::default() };
93 init_files_group(&mut res);
94 init_lowering_group(&mut res, inlining_strategy);
95 init_defs_group(&mut res);
96 init_semantic_group(&mut res);
97
98 let suite = res.intern_plugin_suite(default_plugin_suite);
99 res.set_default_plugins_from_suite(suite);
100
101 res
102 }
103
104 pub fn empty() -> Self {
105 Self::builder().clear_plugins().build().unwrap()
106 }
107
108 pub fn builder() -> RootDatabaseBuilder {
109 RootDatabaseBuilder::new()
110 }
111
112 pub fn snapshot(&self) -> RootDatabase {
114 RootDatabase { storage: self.storage.snapshot() }
115 }
116}
117
118impl Default for RootDatabase {
119 fn default() -> Self {
120 Self::builder().build().unwrap()
121 }
122}
123
124#[derive(Clone, Debug)]
125pub struct RootDatabaseBuilder {
126 default_plugin_suite: PluginSuite,
127 detect_corelib: bool,
128 auto_withdraw_gas: bool,
129 panic_backtrace: bool,
130 unsafe_panic: bool,
131 project_config: Option<Box<ProjectConfig>>,
132 cfg_set: Option<CfgSet>,
133 inlining_strategy: InliningStrategy,
134}
135
136impl RootDatabaseBuilder {
137 fn new() -> Self {
138 Self {
139 default_plugin_suite: get_default_plugin_suite(),
140 detect_corelib: false,
141 auto_withdraw_gas: true,
142 panic_backtrace: false,
143 unsafe_panic: false,
144 project_config: None,
145 cfg_set: None,
146 inlining_strategy: InliningStrategy::Default,
147 }
148 }
149
150 pub fn with_default_plugin_suite(&mut self, suite: PluginSuite) -> &mut Self {
151 self.default_plugin_suite.add(suite);
152 self
153 }
154
155 pub fn clear_plugins(&mut self) -> &mut Self {
156 self.default_plugin_suite = get_default_plugin_suite();
157 self
158 }
159
160 pub fn with_inlining_strategy(&mut self, inlining_strategy: InliningStrategy) -> &mut Self {
161 self.inlining_strategy = inlining_strategy;
162 self
163 }
164
165 pub fn detect_corelib(&mut self) -> &mut Self {
166 self.detect_corelib = true;
167 self
168 }
169
170 pub fn with_project_config(&mut self, config: ProjectConfig) -> &mut Self {
171 self.project_config = Some(Box::new(config));
172 self
173 }
174
175 pub fn with_cfg(&mut self, cfg_set: impl Into<CfgSet>) -> &mut Self {
176 self.cfg_set = Some(cfg_set.into());
177 self
178 }
179
180 pub fn skip_auto_withdraw_gas(&mut self) -> &mut Self {
181 self.auto_withdraw_gas = false;
182 self
183 }
184
185 pub fn with_panic_backtrace(&mut self) -> &mut Self {
186 self.panic_backtrace = true;
187 self
188 }
189
190 pub fn with_unsafe_panic(&mut self) -> &mut Self {
191 self.unsafe_panic = true;
192 self
193 }
194
195 pub fn build(&mut self) -> Result<RootDatabase> {
196 let mut db = RootDatabase::new(self.default_plugin_suite.clone(), self.inlining_strategy);
201
202 if let Some(cfg_set) = &self.cfg_set {
203 db.use_cfg(cfg_set);
204 }
205
206 if self.detect_corelib {
207 let path =
208 detect_corelib().ok_or_else(|| anyhow!("Failed to find development corelib."))?;
209 init_dev_corelib(&mut db, path)
210 }
211
212 let add_withdraw_gas_flag_id = FlagId::new(&db, "add_withdraw_gas");
213 db.set_flag(
214 add_withdraw_gas_flag_id,
215 Some(Arc::new(Flag::AddWithdrawGas(self.auto_withdraw_gas))),
216 );
217 let panic_backtrace_flag_id = FlagId::new(&db, "panic_backtrace");
218 db.set_flag(
219 panic_backtrace_flag_id,
220 Some(Arc::new(Flag::PanicBacktrace(self.panic_backtrace))),
221 );
222
223 let unsafe_panic_flag_id = FlagId::new(&db, "unsafe_panic");
224 db.set_flag(unsafe_panic_flag_id, Some(Arc::new(Flag::UnsafePanic(self.unsafe_panic))));
225
226 if let Some(config) = &self.project_config {
227 update_crate_roots_from_project_config(&mut db, config.as_ref());
228 }
229 validate_corelib(&db)?;
230
231 Ok(db)
232 }
233}
234
235pub fn validate_corelib(db: &(dyn FilesGroup + 'static)) -> Result<()> {
237 let Some(config) = db.crate_config(CrateId::core(db)) else {
238 return Ok(());
239 };
240 let Some(found) = config.settings.version else {
241 return Ok(());
242 };
243 let Ok(expected) = semver::Version::parse(CORELIB_VERSION) else {
244 return Ok(());
245 };
246 if found == expected {
247 return Ok(());
248 }
249 let path_part = match config.root {
250 cairo_lang_filesystem::ids::Directory::Real(path) => {
251 format!(" for `{}`", path.to_string_lossy())
252 }
253 cairo_lang_filesystem::ids::Directory::Virtual { .. } => "".to_string(),
254 };
255 bail!("Corelib version mismatch: expected `{expected}`, found `{found}`{path_part}.");
256}
257
258impl Upcast<dyn FilesGroup> for RootDatabase {
259 fn upcast(&self) -> &(dyn FilesGroup + 'static) {
260 self
261 }
262}
263impl Upcast<dyn SyntaxGroup> for RootDatabase {
264 fn upcast(&self) -> &(dyn SyntaxGroup + 'static) {
265 self
266 }
267}
268impl Upcast<dyn DefsGroup> for RootDatabase {
269 fn upcast(&self) -> &(dyn DefsGroup + 'static) {
270 self
271 }
272}
273impl Upcast<dyn SemanticGroup> for RootDatabase {
274 fn upcast(&self) -> &(dyn SemanticGroup + 'static) {
275 self
276 }
277}
278impl Upcast<dyn LoweringGroup> for RootDatabase {
279 fn upcast(&self) -> &(dyn LoweringGroup + 'static) {
280 self
281 }
282}
283impl Upcast<dyn SierraGenGroup> for RootDatabase {
284 fn upcast(&self) -> &(dyn SierraGenGroup + 'static) {
285 self
286 }
287}
288impl Upcast<dyn ParserGroup> for RootDatabase {
289 fn upcast(&self) -> &(dyn ParserGroup + 'static) {
290 self
291 }
292}