1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs as defs;
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5 ExternFunctionId, LanguageElementId, ModuleId, ModuleItemId, NamedLanguageElementLongId,
6};
7use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe, MaybeAsRef};
8use cairo_lang_filesystem::ids::{FileId, Tracked};
9use cairo_lang_semantic::items::enm::SemanticEnumEx;
10use cairo_lang_semantic::items::imp::ImplSemantic;
11use cairo_lang_semantic::items::macro_call::MacroCallSemantic;
12use cairo_lang_semantic::items::structure::StructSemantic;
13use cairo_lang_semantic::items::trt::TraitSemantic;
14use cairo_lang_semantic::{self as semantic, ConcreteTypeId, TypeId, TypeLongId, corelib};
15use cairo_lang_utils::Intern;
16use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
17use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
18use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
19use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
20use defs::ids::NamedLanguageElementId;
21use itertools::Itertools;
22use num_traits::ToPrimitive;
23use salsa::{Database, Setter};
24
25use crate::add_withdraw_gas::add_withdraw_gas;
26use crate::borrow_check::{BorrowCheckResult, borrow_check, borrow_check_possible_withdraw_gas};
27use crate::cache::load_cached_crate_functions;
28use crate::concretize::concretize_lowered;
29use crate::destructs::add_destructs;
30use crate::diagnostic::{LoweringDiagnostic, LoweringDiagnosticKind};
31use crate::graph_algorithms::feedback_set::flag_add_withdraw_gas;
32use crate::ids::{ConcreteFunctionWithBodyId, FunctionId, FunctionLongId, GenericOrSpecialized};
33use crate::inline::get_inline_diagnostics;
34use crate::inline::statements_weights::{ApproxCasmInlineWeight, InlineWeight};
35use crate::lower::{MultiLowering, lower_semantic_function};
36use crate::optimizations::config::Optimizations;
37use crate::optimizations::scrub_units::scrub_units;
38use crate::optimizations::strategy::OptimizationStrategyId;
39use crate::panic::lower_panics;
40use crate::specialization::specialized_function_lowered;
41use crate::{
42 BlockEnd, BlockId, DependencyType, Location, Lowered, LoweringStage, MatchInfo, Statement, ids,
43};
44
45type CodeSizeEstimator = fn(&dyn Database, ConcreteFunctionWithBodyId<'_>) -> Maybe<isize>;
46#[salsa::input]
47pub struct LoweringGroupInput {
48 #[returns(ref)]
49 pub optimizations: Option<Optimizations>,
50 #[returns(ref)]
52 code_size_estimator: Option<CodeSizeEstimator>,
53}
54
55#[salsa::tracked(returns(ref))]
56pub fn lowering_group_input(db: &dyn Database) -> LoweringGroupInput {
57 LoweringGroupInput::new(db, None, None)
58}
59
60pub trait LoweringGroup: Database {
62 fn priv_function_with_body_multi_lowering<'db>(
65 &'db self,
66 function_id: defs::ids::FunctionWithBodyId<'db>,
67 ) -> Maybe<&'db MultiLowering<'db>> {
68 priv_function_with_body_multi_lowering(self.as_dyn_database(), (), function_id)
69 .maybe_as_ref()
70 }
71
72 fn cached_multi_lowerings<'db>(
75 &'db self,
76 crate_id: cairo_lang_filesystem::ids::CrateId<'db>,
77 ) -> Option<&'db OrderedHashMap<defs::ids::FunctionWithBodyId<'db>, MultiLowering<'db>>> {
78 cached_multi_lowerings(self.as_dyn_database(), crate_id).as_ref()
79 }
80
81 fn function_with_body_lowering<'db>(
83 &'db self,
84 function_id: ids::FunctionWithBodyId<'db>,
85 ) -> Maybe<&'db Lowered<'db>> {
86 function_with_body_lowering(self.as_dyn_database(), function_id)
87 }
88
89 fn borrow_check<'db>(
91 &'db self,
92 function_id: ids::FunctionWithBodyId<'db>,
93 ) -> Maybe<&'db BorrowCheckResult<'db>> {
94 borrow_check_tracked(self.as_dyn_database(), function_id).maybe_as_ref()
95 }
96
97 fn lowered_body<'db>(
99 &'db self,
100 function_id: ids::ConcreteFunctionWithBodyId<'db>,
101 stage: LoweringStage,
102 ) -> Maybe<&'db Lowered<'db>> {
103 lowered_body(self.as_dyn_database(), function_id, stage).maybe_as_ref()
104 }
105
106 fn lowered_direct_callees<'db>(
109 &'db self,
110 function_id: ids::ConcreteFunctionWithBodyId<'db>,
111 dependency_type: DependencyType,
112 stage: LoweringStage,
113 ) -> Maybe<&'db [ids::FunctionId<'db>]> {
114 Ok(lowered_direct_callees(self.as_dyn_database(), function_id, dependency_type, stage)
115 .maybe_as_ref()?)
116 }
117
118 fn lowered_direct_callees_with_body<'db>(
121 &'db self,
122 function_id: ids::ConcreteFunctionWithBodyId<'db>,
123 dependency_type: DependencyType,
124 stage: LoweringStage,
125 ) -> Maybe<&'db [ids::ConcreteFunctionWithBodyId<'db>]> {
126 Ok(lowered_direct_callees_with_body(
127 self.as_dyn_database(),
128 function_id,
129 dependency_type,
130 stage,
131 )
132 .maybe_as_ref()?)
133 }
134
135 fn function_with_body_lowering_diagnostics<'db>(
137 &'db self,
138 function_id: ids::FunctionWithBodyId<'db>,
139 ) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
140 function_with_body_lowering_diagnostics(self.as_dyn_database(), function_id)
141 }
142 fn semantic_function_with_body_lowering_diagnostics<'db>(
145 &'db self,
146 function_id: defs::ids::FunctionWithBodyId<'db>,
147 ) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
148 semantic_function_with_body_lowering_diagnostics(self.as_dyn_database(), (), function_id)
149 }
150 fn module_lowering_diagnostics<'db>(
152 &'db self,
153 module_id: ModuleId<'db>,
154 ) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
155 module_lowering_diagnostics(self.as_dyn_database(), (), module_id)
156 }
157
158 fn file_lowering_diagnostics<'db>(
160 &'db self,
161 file_id: FileId<'db>,
162 ) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
163 file_lowering_diagnostics(self.as_dyn_database(), file_id)
164 }
165
166 fn function_implicits<'db>(
173 &'db self,
174 function: ids::FunctionId<'db>,
175 ) -> Maybe<Vec<TypeId<'db>>> {
176 crate::implicits::function_implicits(self.as_dyn_database(), function)
177 }
178
179 fn function_may_panic<'db>(&'db self, function: ids::FunctionId<'db>) -> Maybe<bool> {
183 crate::panic::function_may_panic(self.as_dyn_database(), function)
184 }
185
186 fn has_direct_panic<'db>(
188 &'db self,
189 function_id: ids::ConcreteFunctionWithBodyId<'db>,
190 ) -> Maybe<bool> {
191 crate::panic::has_direct_panic(self.as_dyn_database(), function_id)
192 }
193
194 fn function_with_body_direct_callees<'db>(
198 &'db self,
199 function_id: ids::FunctionWithBodyId<'db>,
200 dependency_type: DependencyType,
201 ) -> Maybe<&'db OrderedHashSet<ids::FunctionId<'db>>> {
202 crate::graph_algorithms::cycles::function_with_body_direct_callees(
203 self.as_dyn_database(),
204 function_id,
205 dependency_type,
206 )
207 .maybe_as_ref()
208 }
209 fn function_with_body_direct_function_with_body_callees<'db>(
212 &'db self,
213 function_id: ids::FunctionWithBodyId<'db>,
214 dependency_type: DependencyType,
215 ) -> Maybe<&'db OrderedHashSet<ids::FunctionWithBodyId<'db>>> {
216 crate::graph_algorithms::cycles::function_with_body_direct_function_with_body_callees(
217 self.as_dyn_database(),
218 function_id,
219 dependency_type,
220 )
221 .maybe_as_ref()
222 }
223
224 fn final_contains_call_cycle<'db>(
229 &'db self,
230 function_id: ids::ConcreteFunctionWithBodyId<'db>,
231 ) -> Maybe<bool> {
232 crate::graph_algorithms::cycles::final_contains_call_cycle(
233 self.as_dyn_database(),
234 function_id,
235 )
236 }
237
238 fn in_cycle<'db>(
242 &'db self,
243 function_id: ids::FunctionWithBodyId<'db>,
244 dependency_type: DependencyType,
245 ) -> Maybe<bool> {
246 crate::graph_algorithms::cycles::in_cycle(
247 self.as_dyn_database(),
248 function_id,
249 dependency_type,
250 )
251 }
252
253 fn concrete_in_cycle<'db>(
255 &'db self,
256 function_id: ids::ConcreteFunctionWithBodyId<'db>,
257 dependency_type: DependencyType,
258 stage: LoweringStage,
259 ) -> Maybe<bool> {
260 crate::graph_algorithms::cycles::concrete_in_cycle(
261 self.as_dyn_database(),
262 function_id,
263 dependency_type,
264 stage,
265 )
266 }
267
268 fn lowered_scc_representative<'db>(
273 &'db self,
274 function: ids::ConcreteFunctionWithBodyId<'db>,
275 dependency_type: DependencyType,
276 stage: LoweringStage,
277 ) -> ConcreteSCCRepresentative<'db> {
278 crate::graph_algorithms::strongly_connected_components::lowered_scc_representative(
279 self.as_dyn_database(),
280 function,
281 dependency_type,
282 stage,
283 )
284 }
285
286 fn lowered_scc<'db>(
289 &'db self,
290 function_id: ids::ConcreteFunctionWithBodyId<'db>,
291 dependency_type: DependencyType,
292 stage: LoweringStage,
293 ) -> Vec<ids::ConcreteFunctionWithBodyId<'db>> {
294 crate::graph_algorithms::strongly_connected_components::lowered_scc(
295 self.as_dyn_database(),
296 function_id,
297 dependency_type,
298 stage,
299 )
300 }
301
302 fn function_with_body_scc<'db>(
304 &'db self,
305 function_id: ids::FunctionWithBodyId<'db>,
306 dependency_type: DependencyType,
307 ) -> &'db [ids::FunctionWithBodyId<'db>] {
308 crate::scc::function_with_body_scc(self.as_dyn_database(), function_id, dependency_type)
309 }
310
311 fn function_with_body_feedback_set<'db>(
316 &'db self,
317 function: ids::ConcreteFunctionWithBodyId<'db>,
318 stage: LoweringStage,
319 ) -> Maybe<&'db OrderedHashSet<ids::ConcreteFunctionWithBodyId<'db>>> {
320 crate::graph_algorithms::feedback_set::function_with_body_feedback_set(
321 self.as_dyn_database(),
322 function,
323 stage,
324 )
325 .maybe_as_ref()
326 }
327
328 fn needs_withdraw_gas<'db>(
330 &'db self,
331 function: ids::ConcreteFunctionWithBodyId<'db>,
332 ) -> Maybe<bool> {
333 crate::graph_algorithms::feedback_set::needs_withdraw_gas(self.as_dyn_database(), function)
334 }
335
336 fn priv_movable_function_ids<'db>(&'db self) -> &'db UnorderedHashSet<ExternFunctionId<'db>> {
338 crate::optimizations::config::priv_movable_function_ids(self.as_dyn_database())
339 }
340
341 fn priv_should_inline<'db>(
343 &'db self,
344 function_id: ids::ConcreteFunctionWithBodyId<'db>,
345 ) -> Maybe<bool> {
346 crate::inline::priv_should_inline(self.as_dyn_database(), function_id)
347 }
348
349 fn priv_never_inline<'db>(
351 &'db self,
352 function_id: ids::ConcreteFunctionWithBodyId<'db>,
353 ) -> Maybe<bool> {
354 crate::inline::priv_never_inline(self.as_dyn_database(), function_id)
355 }
356
357 fn priv_should_specialize<'db>(
359 &'db self,
360 function_id: ids::ConcreteFunctionWithBodyId<'db>,
361 ) -> Maybe<bool> {
362 crate::specialization::priv_should_specialize(self.as_dyn_database(), function_id)
363 }
364
365 fn optimizations(&self) -> &Optimizations {
367 let db = self.as_dyn_database();
368 lowering_group_input(db).optimizations(db).as_ref().unwrap()
369 }
370
371 fn final_optimization_strategy<'db>(&'db self) -> OptimizationStrategyId<'db> {
374 crate::optimizations::strategy::final_optimization_strategy(self.as_dyn_database())
375 }
376
377 fn baseline_optimization_strategy<'db>(&'db self) -> OptimizationStrategyId<'db> {
380 crate::optimizations::strategy::baseline_optimization_strategy(self.as_dyn_database())
381 }
382
383 fn type_size<'db>(&'db self, ty: TypeId<'db>) -> usize {
385 type_size(self.as_dyn_database(), ty)
386 }
387
388 fn estimate_size<'db>(&'db self, function_id: ConcreteFunctionWithBodyId<'db>) -> Maybe<isize> {
390 estimate_size(self.as_dyn_database(), function_id)
391 }
392}
393impl<T: Database + ?Sized> LoweringGroup for T {}
394
395pub fn init_lowering_group(
396 db: &mut (dyn Database + 'static),
397 optimizations: Optimizations,
398 code_size_estimator: Option<CodeSizeEstimator>,
399) {
400 lowering_group_input(db).set_optimizations(db).to(Some(optimizations));
401 lowering_group_input(db).set_code_size_estimator(db).to(code_size_estimator);
402}
403
404#[derive(Debug, Eq, PartialEq, Clone, Hash)]
405pub struct GenericSCCRepresentative<'db>(pub ids::FunctionWithBodyId<'db>);
406
407#[derive(Debug, Eq, PartialEq, Clone, Hash, salsa::Update)]
408pub struct ConcreteSCCRepresentative<'db>(pub ids::ConcreteFunctionWithBodyId<'db>);
409
410#[salsa::tracked(returns(ref))]
413fn priv_function_with_body_multi_lowering<'db>(
414 db: &'db dyn Database,
415 _tracked: Tracked,
416 function_id: defs::ids::FunctionWithBodyId<'db>,
417) -> Maybe<MultiLowering<'db>> {
418 let crate_id = function_id.parent_module(db).owning_crate(db);
419 if let Some(map) = db.cached_multi_lowerings(crate_id) {
420 if let Some(multi_lowering) = map.get(&function_id) {
421 return Ok(multi_lowering.clone());
422 } else {
423 panic!("function not found in cached lowering {:?}", function_id.debug(db));
424 }
425 };
426
427 lower_semantic_function(db, function_id)
428}
429
430#[salsa::tracked(returns(ref))]
431fn cached_multi_lowerings<'db>(
432 db: &'db dyn Database,
433 crate_id: cairo_lang_filesystem::ids::CrateId<'db>,
434) -> Option<OrderedHashMap<defs::ids::FunctionWithBodyId<'db>, MultiLowering<'db>>> {
435 load_cached_crate_functions(db, crate_id)
436}
437
438fn function_with_body_lowering<'db>(
439 db: &'db dyn Database,
440 function_id: ids::FunctionWithBodyId<'db>,
441) -> Maybe<&'db Lowered<'db>> {
442 let semantic_function_id = function_id.base_semantic_function(db);
443 let multi_lowering = db.priv_function_with_body_multi_lowering(semantic_function_id)?;
444 Ok(match &function_id.long(db) {
445 ids::FunctionWithBodyLongId::Semantic(_) => &multi_lowering.main_lowering,
446 ids::FunctionWithBodyLongId::Generated { key, .. } => {
447 &multi_lowering.generated_lowerings[key]
448 }
449 })
450}
451
452#[salsa::tracked(returns(ref))]
453fn borrow_check_tracked<'db>(
454 db: &'db dyn Database,
455 function_id: ids::FunctionWithBodyId<'db>,
456) -> Maybe<BorrowCheckResult<'db>> {
457 let lowered = db.function_with_body_lowering(function_id)?;
458 Ok(borrow_check(db, function_id.to_concrete(db)?.is_panic_destruct_fn(db)?, lowered))
459}
460
461#[salsa::tracked(returns(ref))]
462fn lowered_body<'db>(
463 db: &'db dyn Database,
464 function: ids::ConcreteFunctionWithBodyId<'db>,
465 stage: LoweringStage,
466) -> Maybe<Lowered<'db>> {
467 Ok(match stage {
468 LoweringStage::Monomorphized => match function.generic_or_specialized(db) {
469 GenericOrSpecialized::Generic(generic_function_id) => {
470 db.function_with_body_lowering_diagnostics(generic_function_id)?
471 .check_error_free()?;
472 let mut lowered = db.function_with_body_lowering(generic_function_id)?.clone();
473 concretize_lowered(db, &mut lowered, &function.substitution(db)?)?;
474 lowered
475 }
476 GenericOrSpecialized::Specialized(specialized) => {
477 specialized_function_lowered(db, specialized)?
478 }
479 },
480 LoweringStage::PreOptimizations => {
481 let mut lowered = db.lowered_body(function, LoweringStage::Monomorphized)?.clone();
482 add_withdraw_gas(db, function, &mut lowered)?;
483 lower_panics(db, function, &mut lowered)?;
484 add_destructs(db, function, &mut lowered);
485 scrub_units(db, &mut lowered);
486 lowered
487 }
488 LoweringStage::PostBaseline => {
489 let mut lowered = db.lowered_body(function, LoweringStage::PreOptimizations)?.clone();
490 db.baseline_optimization_strategy().apply_strategy(db, function, &mut lowered)?;
491 lowered
492 }
493 LoweringStage::Final => {
494 let mut lowered = db.lowered_body(function, LoweringStage::PostBaseline)?.clone();
495 db.final_optimization_strategy().apply_strategy(db, function, &mut lowered)?;
496 lowered
497 }
498 })
499}
500
501pub(crate) fn get_direct_callees<'db>(
505 db: &dyn Database,
506 lowered_function: &Lowered<'db>,
507 dependency_type: DependencyType,
508 block_extra_calls: &UnorderedHashMap<BlockId, Vec<FunctionId<'db>>>,
509) -> Vec<ids::FunctionId<'db>> {
510 let mut direct_callees = Vec::new();
511 if lowered_function.blocks.is_empty() {
512 return direct_callees;
513 }
514 let withdraw_gas_fns =
515 corelib::core_withdraw_gas_fns(db).map(|id| FunctionLongId::Semantic(id).intern(db));
516 let mut visited = vec![false; lowered_function.blocks.len()];
517 let mut stack = vec![BlockId(0)];
518 while let Some(block_id) = stack.pop() {
519 if visited[block_id.0] {
520 continue;
521 }
522 visited[block_id.0] = true;
523 let block = &lowered_function.blocks[block_id];
524 for statement in &block.statements {
525 if let Statement::Call(statement_call) = statement {
526 if dependency_type != DependencyType::Cost || !statement_call.with_coupon {
530 direct_callees.push(statement_call.function);
531 }
532 }
533 }
534 if let Some(extra_calls) = block_extra_calls.get(&block_id) {
535 direct_callees.extend(extra_calls.iter().copied());
536 }
537 match &block.end {
538 BlockEnd::NotSet | BlockEnd::Return(..) | BlockEnd::Panic(_) => {}
539 BlockEnd::Goto(next, _) => stack.push(*next),
540 BlockEnd::Match { info } => {
541 let mut arms = info.arms().iter();
542 if let MatchInfo::Extern(s) = info {
543 direct_callees.push(s.function);
544 if DependencyType::Cost == dependency_type
545 && withdraw_gas_fns.contains(&s.function)
546 {
547 arms.next();
549 }
550 }
551 stack.extend(arms.map(|arm| arm.block_id));
552 }
553 }
554 }
555 direct_callees
556}
557
558fn functions_with_body_from_function_ids<'db>(
565 db: &'db dyn Database,
566 function_ids: &'db [ids::FunctionId<'db>],
567 dependency_type: DependencyType,
568) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId<'db>>> {
569 Ok(function_ids
570 .iter()
571 .map(|concrete| {
572 if dependency_type == DependencyType::Cost
573 && let Some(function_with_body) = extract_coupon_function(db, *concrete)?
574 {
575 return Ok(Some(function_with_body));
576 }
577 concrete.body(db)
578 })
579 .collect::<Maybe<Vec<_>>>()?
580 .into_iter()
581 .flatten()
582 .collect_vec())
583}
584
585fn extract_coupon_function<'db>(
590 db: &'db dyn Database,
591 concrete: ids::FunctionId<'db>,
592) -> Maybe<Option<ids::ConcreteFunctionWithBodyId<'db>>> {
593 let ids::FunctionLongId::Semantic(function_id) = concrete.long(db) else {
595 return Ok(None);
596 };
597
598 let concrete_function = function_id.get_concrete(db);
600 let generic_function = concrete_function.generic_function;
601 let semantic::items::functions::GenericFunctionId::Extern(extern_function_id) =
602 generic_function
603 else {
604 return Ok(None);
605 };
606 let name = extern_function_id.long(db).name(db).long(db);
607 if !(name == "coupon_buy" || name == "coupon_refund") {
608 return Ok(None);
609 }
610
611 let [semantic::GenericArgumentId::Type(type_id)] = concrete_function.generic_args[..] else {
613 panic!("Unexpected generic_args for coupon_buy().");
614 };
615 let semantic::TypeLongId::Coupon(coupon_function) = type_id.long(db) else {
616 panic!("Unexpected generic_args for coupon_buy().");
617 };
618
619 let Some(coupon_function_with_body_id) = coupon_function.get_concrete(db).body(db)? else {
621 panic!("Unexpected generic_args for coupon_buy().");
622 };
623
624 Ok(Some(ids::ConcreteFunctionWithBodyId::from_semantic(db, coupon_function_with_body_id)))
625}
626
627#[salsa::tracked(returns(ref))]
628fn lowered_direct_callees<'db>(
629 db: &'db dyn Database,
630 function_id: ids::ConcreteFunctionWithBodyId<'db>,
631 dependency_type: DependencyType,
632 stage: LoweringStage,
633) -> Maybe<Vec<ids::FunctionId<'db>>> {
634 let lowered_function = db.lowered_body(function_id, stage)?;
635 Ok(get_direct_callees(db, lowered_function, dependency_type, &Default::default()))
636}
637
638#[salsa::tracked(returns(ref))]
639fn lowered_direct_callees_with_body<'db>(
640 db: &'db dyn Database,
641 function_id: ids::ConcreteFunctionWithBodyId<'db>,
642 dependency_type: DependencyType,
643 stage: LoweringStage,
644) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId<'db>>> {
645 functions_with_body_from_function_ids(
646 db,
647 db.lowered_direct_callees(function_id, dependency_type, stage)?,
648 dependency_type,
649 )
650}
651
652#[salsa::tracked]
653fn function_with_body_lowering_diagnostics<'db>(
654 db: &'db dyn Database,
655 function_id: ids::FunctionWithBodyId<'db>,
656) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
657 let mut diagnostics = DiagnosticsBuilder::default();
658
659 if let Ok(lowered) = db.function_with_body_lowering(function_id) {
660 diagnostics.extend(lowered.diagnostics.clone());
661 if let Ok(bc) = db.borrow_check(function_id) {
662 diagnostics.extend(bc.diagnostics.clone());
663 }
664 if flag_add_withdraw_gas(db) && db.in_cycle(function_id, DependencyType::Cost)? {
665 let location =
666 Location::new(function_id.base_semantic_function(db).stable_location(db));
667 if !lowered.signature.panicable {
668 diagnostics.add(LoweringDiagnostic {
669 location: location.clone(),
670 kind: LoweringDiagnosticKind::NoPanicFunctionCycle,
671 });
672 }
673 borrow_check_possible_withdraw_gas(db, location.intern(db), lowered, &mut diagnostics)
674 }
675 }
676
677 if let Ok(diag) = get_inline_diagnostics(db, function_id) {
678 diagnostics.extend(diag);
679 }
680
681 Ok(diagnostics.build())
682}
683
684#[salsa::tracked]
685fn semantic_function_with_body_lowering_diagnostics<'db>(
686 db: &'db dyn Database,
687 _tracked: Tracked,
688 semantic_function_id: defs::ids::FunctionWithBodyId<'db>,
689) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
690 let mut diagnostics = DiagnosticsBuilder::default();
691
692 if let Ok(multi_lowering) = db.priv_function_with_body_multi_lowering(semantic_function_id) {
693 let function_id = ids::FunctionWithBodyLongId::Semantic(semantic_function_id).intern(db);
694 diagnostics
695 .extend(db.function_with_body_lowering_diagnostics(function_id).unwrap_or_default());
696 for (key, _) in multi_lowering.generated_lowerings.iter() {
697 let function_id =
698 ids::FunctionWithBodyLongId::Generated { parent: semantic_function_id, key: *key }
699 .intern(db);
700 diagnostics.extend(
701 db.function_with_body_lowering_diagnostics(function_id).unwrap_or_default(),
702 );
703 }
704 }
705
706 Ok(diagnostics.build())
707}
708
709#[salsa::tracked]
710fn module_lowering_diagnostics<'db>(
711 db: &'db dyn Database,
712 _tracked: Tracked,
713 module_id: ModuleId<'db>,
714) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
715 let mut diagnostics = DiagnosticsBuilder::default();
716 for item in module_id.module_data(db)?.items(db).iter() {
717 match item {
718 ModuleItemId::FreeFunction(free_function) => {
719 let function_id = defs::ids::FunctionWithBodyId::Free(*free_function);
720 diagnostics
721 .extend(db.semantic_function_with_body_lowering_diagnostics(function_id)?);
722 }
723 ModuleItemId::Constant(_) => {}
724 ModuleItemId::Submodule(_) => {}
725 ModuleItemId::Use(_) => {}
726 ModuleItemId::Struct(_) => {}
727 ModuleItemId::Enum(_) => {}
728 ModuleItemId::TypeAlias(_) => {}
729 ModuleItemId::ImplAlias(_) => {}
730 ModuleItemId::Trait(trait_id) => {
731 for trait_func in db.trait_functions(*trait_id)?.values() {
732 if matches!(db.trait_function_body(*trait_func), Ok(Some(_))) {
733 let function_id = defs::ids::FunctionWithBodyId::Trait(*trait_func);
734 diagnostics.extend(
735 db.semantic_function_with_body_lowering_diagnostics(function_id)?,
736 );
737 }
738 }
739 }
740 ModuleItemId::Impl(impl_def_id) => {
741 for impl_func in db.impl_functions(*impl_def_id)?.values() {
742 let function_id = defs::ids::FunctionWithBodyId::Impl(*impl_func);
743 diagnostics
744 .extend(db.semantic_function_with_body_lowering_diagnostics(function_id)?);
745 }
746 }
747 ModuleItemId::ExternType(_) => {}
748 ModuleItemId::ExternFunction(_) => {}
749 ModuleItemId::MacroDeclaration(_) => {}
750 }
751 }
752 for macro_call in db.module_macro_calls_ids(module_id)?.iter() {
753 if let Ok(macro_module_id) = db.macro_call_module_id(*macro_call)
754 && let Ok(lowering_diags) = db.module_lowering_diagnostics(macro_module_id)
755 {
756 diagnostics.extend(lowering_diags);
757 }
758 }
759 Ok(diagnostics.build())
760}
761
762#[salsa::tracked]
763fn file_lowering_diagnostics<'db>(
764 db: &'db dyn Database,
765 file_id: FileId<'db>,
766) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
767 let mut diagnostics = DiagnosticsBuilder::default();
768 for module_id in db.file_modules(file_id)?.iter().copied() {
769 if let Ok(module_diagnostics) = db.module_lowering_diagnostics(module_id) {
770 diagnostics.extend(module_diagnostics)
771 }
772 }
773 Ok(diagnostics.build())
774}
775
776#[salsa::tracked]
777fn type_size<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> usize {
778 match ty.long(db) {
779 TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
780 ConcreteTypeId::Struct(struct_id) => db
781 .concrete_struct_members(*struct_id)
782 .unwrap()
783 .iter()
784 .map(|(_, member)| db.type_size(member.ty))
785 .sum::<usize>(),
786 ConcreteTypeId::Enum(enum_id) => {
787 1 + db
788 .concrete_enum_variants(*enum_id)
789 .unwrap()
790 .into_iter()
791 .map(|variant| db.type_size(variant.ty))
792 .max()
793 .unwrap_or_default()
794 }
795 ConcreteTypeId::Extern(extern_id) => {
796 match extern_id.extern_type_id(db).name(db).long(db).as_str() {
797 "Array" | "SquashedFelt252Dict" | "EcPoint" => 2,
798 "EcState" => 3,
799 "Uint128MulGuarantee" => 4,
800 _ => 1,
801 }
802 }
803 },
804 TypeLongId::Tuple(types) => types.iter().map(|ty| db.type_size(*ty)).sum::<usize>(),
805 TypeLongId::Snapshot(ty) => db.type_size(*ty),
806 TypeLongId::FixedSizeArray { type_id, size } => {
807 db.type_size(*type_id)
808 * size
809 .long(db)
810 .to_int()
811 .expect("Expected ConstValue::Int for size")
812 .to_usize()
813 .unwrap()
814 }
815 TypeLongId::Closure(closure_ty) => {
816 closure_ty.captured_types.iter().map(|ty| db.type_size(*ty)).sum()
817 }
818 TypeLongId::Coupon(_) => 0,
819 TypeLongId::GenericParameter(_)
820 | TypeLongId::Var(_)
821 | TypeLongId::ImplType(_)
822 | TypeLongId::Missing(_) => {
823 panic!("Function should only be called with fully concrete types")
824 }
825 }
826}
827
828fn estimate_size<'db>(
829 db: &'db dyn Database,
830 function_id: ConcreteFunctionWithBodyId<'db>,
831) -> Maybe<isize> {
832 let code_size_estimator = lowering_group_input(db).code_size_estimator(db);
833 if let Some(estimator) = code_size_estimator {
834 estimator(db, function_id)
835 } else {
836 let lowered = db.lowered_body(function_id, LoweringStage::PostBaseline)?;
838 Ok(ApproxCasmInlineWeight::new(db, lowered).lowered_weight(lowered))
839 }
840}