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