1use std::sync::Arc;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_defs as defs;
5use cairo_lang_defs::ids::{LanguageElementId, ModuleId, ModuleItemId, NamedLanguageElementLongId};
6use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe};
7use cairo_lang_filesystem::ids::FileId;
8use cairo_lang_semantic::db::SemanticGroup;
9use cairo_lang_semantic::items::enm::SemanticEnumEx;
10use cairo_lang_semantic::{self as semantic, ConcreteTypeId, TypeId, TypeLongId, corelib};
11use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
12use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
13use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
14use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
15use cairo_lang_utils::{Intern, LookupIntern, Upcast};
16use defs::ids::NamedLanguageElementId;
17use itertools::Itertools;
18use num_traits::ToPrimitive;
19
20use crate::add_withdraw_gas::add_withdraw_gas;
21use crate::borrow_check::{PotentialDestructCalls, borrow_check};
22use crate::cache::load_cached_crate_functions;
23use crate::concretize::concretize_lowered;
24use crate::destructs::add_destructs;
25use crate::diagnostic::{LoweringDiagnostic, LoweringDiagnosticKind};
26use crate::graph_algorithms::feedback_set::flag_add_withdraw_gas;
27use crate::ids::{FunctionId, FunctionLongId};
28use crate::inline::get_inline_diagnostics;
29use crate::lower::{MultiLowering, lower_semantic_function};
30use crate::optimizations::config::OptimizationConfig;
31use crate::optimizations::scrub_units::scrub_units;
32use crate::optimizations::strategy::{OptimizationStrategy, OptimizationStrategyId};
33use crate::panic::lower_panics;
34use crate::utils::InliningStrategy;
35use crate::{
36 BlockId, DependencyType, FlatBlockEnd, FlatLowered, Location, MatchInfo, Statement, ids,
37};
38
39#[salsa::query_group(LoweringDatabase)]
41pub trait LoweringGroup: SemanticGroup + Upcast<dyn SemanticGroup> {
42 #[salsa::interned]
43 fn intern_lowering_function(&self, id: ids::FunctionLongId) -> ids::FunctionId;
44 #[salsa::interned]
45 fn intern_lowering_concrete_function_with_body(
46 &self,
47 id: ids::ConcreteFunctionWithBodyLongId,
48 ) -> ids::ConcreteFunctionWithBodyId;
49 #[salsa::interned]
50 fn intern_lowering_function_with_body(
51 &self,
52 id: ids::FunctionWithBodyLongId,
53 ) -> ids::FunctionWithBodyId;
54
55 #[salsa::interned]
56 fn intern_location(&self, id: Location) -> ids::LocationId;
57
58 #[salsa::interned]
59 fn intern_strategy(&self, id: OptimizationStrategy) -> OptimizationStrategyId;
60
61 fn priv_function_with_body_multi_lowering(
64 &self,
65 function_id: defs::ids::FunctionWithBodyId,
66 ) -> Maybe<Arc<MultiLowering>>;
67
68 fn cached_multi_lowerings(
71 &self,
72 crate_id: cairo_lang_filesystem::ids::CrateId,
73 ) -> Option<Arc<OrderedHashMap<defs::ids::FunctionWithBodyId, MultiLowering>>>;
74
75 fn priv_function_with_body_lowering(
77 &self,
78 function_id: ids::FunctionWithBodyId,
79 ) -> Maybe<Arc<FlatLowered>>;
80
81 fn function_with_body_lowering_with_borrow_check(
84 &self,
85 function_id: ids::FunctionWithBodyId,
86 ) -> Maybe<(Arc<FlatLowered>, Arc<PotentialDestructCalls>)>;
87
88 fn function_with_body_lowering(
90 &self,
91 function_id: ids::FunctionWithBodyId,
92 ) -> Maybe<Arc<FlatLowered>>;
93
94 fn priv_concrete_function_with_body_lowered_flat(
96 &self,
97 function_id: ids::ConcreteFunctionWithBodyId,
98 ) -> Maybe<Arc<FlatLowered>>;
99
100 fn concrete_function_with_body_postpanic_lowered(
102 &self,
103 function_id: ids::ConcreteFunctionWithBodyId,
104 ) -> Maybe<Arc<FlatLowered>>;
105
106 fn optimized_concrete_function_with_body_lowered(
108 &self,
109 function: ids::ConcreteFunctionWithBodyId,
110 optimization_strategy: OptimizationStrategyId,
111 ) -> Maybe<Arc<FlatLowered>>;
112
113 fn inlined_function_with_body_lowered(
115 &self,
116 function_id: ids::ConcreteFunctionWithBodyId,
117 ) -> Maybe<Arc<FlatLowered>>;
118
119 fn final_concrete_function_with_body_lowered(
121 &self,
122 function_id: ids::ConcreteFunctionWithBodyId,
123 ) -> Maybe<Arc<FlatLowered>>;
124
125 fn concrete_function_with_body_direct_callees(
127 &self,
128 function_id: ids::ConcreteFunctionWithBodyId,
129 dependency_type: DependencyType,
130 ) -> Maybe<Vec<ids::FunctionId>>;
131
132 fn concrete_function_with_body_inlined_direct_callees(
135 &self,
136 function_id: ids::ConcreteFunctionWithBodyId,
137 dependency_type: DependencyType,
138 ) -> Maybe<Vec<ids::FunctionId>>;
139
140 fn concrete_function_with_body_direct_callees_with_body(
143 &self,
144 function_id: ids::ConcreteFunctionWithBodyId,
145 dependency_type: DependencyType,
146 ) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId>>;
147
148 fn concrete_function_with_body_inlined_direct_callees_with_body(
151 &self,
152 function_id: ids::ConcreteFunctionWithBodyId,
153 dependency_type: DependencyType,
154 ) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId>>;
155
156 fn final_concrete_function_with_body_lowered_direct_callees(
159 &self,
160 function_id: ids::ConcreteFunctionWithBodyId,
161 dependency_type: DependencyType,
162 ) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId>>;
163
164 fn function_with_body_lowering_diagnostics(
166 &self,
167 function_id: ids::FunctionWithBodyId,
168 ) -> Maybe<Diagnostics<LoweringDiagnostic>>;
169 fn semantic_function_with_body_lowering_diagnostics(
172 &self,
173 function_id: defs::ids::FunctionWithBodyId,
174 ) -> Maybe<Diagnostics<LoweringDiagnostic>>;
175 fn module_lowering_diagnostics(
177 &self,
178 module_id: ModuleId,
179 ) -> Maybe<Diagnostics<LoweringDiagnostic>>;
180
181 fn file_lowering_diagnostics(&self, file_id: FileId) -> Maybe<Diagnostics<LoweringDiagnostic>>;
183
184 #[salsa::invoke(crate::implicits::function_implicits)]
191 fn function_implicits(&self, function: ids::FunctionId) -> Maybe<Vec<TypeId>>;
192
193 #[salsa::invoke(crate::implicits::scc_implicits)]
195 fn scc_implicits(&self, function: ConcreteSCCRepresentative) -> Maybe<Vec<TypeId>>;
196
197 #[salsa::invoke(crate::panic::function_may_panic)]
201 fn function_may_panic(&self, function: ids::FunctionId) -> Maybe<bool>;
202
203 #[salsa::invoke(crate::panic::scc_may_panic)]
205 fn scc_may_panic(&self, scc: ConcreteSCCRepresentative) -> Maybe<bool>;
206
207 #[salsa::invoke(crate::panic::has_direct_panic)]
209 fn has_direct_panic(&self, function_id: ids::ConcreteFunctionWithBodyId) -> Maybe<bool>;
210
211 #[salsa::invoke(crate::graph_algorithms::cycles::function_with_body_direct_callees)]
215 fn function_with_body_direct_callees(
216 &self,
217 function_id: ids::FunctionWithBodyId,
218 dependency_type: DependencyType,
219 ) -> Maybe<OrderedHashSet<ids::FunctionId>>;
220 #[salsa::invoke(
223 crate::graph_algorithms::cycles::function_with_body_direct_function_with_body_callees
224 )]
225 fn function_with_body_direct_function_with_body_callees(
226 &self,
227 function_id: ids::FunctionWithBodyId,
228 dependency_type: DependencyType,
229 ) -> Maybe<OrderedHashSet<ids::FunctionWithBodyId>>;
230
231 #[salsa::invoke(crate::graph_algorithms::cycles::final_contains_call_cycle)]
236 #[salsa::cycle(crate::graph_algorithms::cycles::final_contains_call_cycle_handle_cycle)]
237 fn final_contains_call_cycle(
238 &self,
239 function_id: ids::ConcreteFunctionWithBodyId,
240 ) -> Maybe<bool>;
241
242 #[salsa::invoke(crate::graph_algorithms::cycles::in_cycle)]
246 fn in_cycle(
247 &self,
248 function_id: ids::FunctionWithBodyId,
249 dependency_type: DependencyType,
250 ) -> Maybe<bool>;
251
252 #[salsa::invoke(
257 crate::graph_algorithms::strongly_connected_components::concrete_function_with_body_scc_representative
258 )]
259 fn concrete_function_with_body_scc_representative(
260 &self,
261 function: ids::ConcreteFunctionWithBodyId,
262 dependency_type: DependencyType,
263 ) -> ConcreteSCCRepresentative;
264
265 #[salsa::invoke(
268 crate::graph_algorithms::strongly_connected_components::concrete_function_with_body_scc
269 )]
270 fn concrete_function_with_body_scc(
271 &self,
272 function_id: ids::ConcreteFunctionWithBodyId,
273 dependency_type: DependencyType,
274 ) -> Vec<ids::ConcreteFunctionWithBodyId>;
275
276 #[salsa::invoke(
280 crate::graph_algorithms::strongly_connected_components::concrete_function_with_body_scc_inlined_representative
281 )]
282 fn concrete_function_with_body_scc_inlined_representative(
283 &self,
284 function: ids::ConcreteFunctionWithBodyId,
285 dependency_type: DependencyType,
286 ) -> ConcreteSCCRepresentative;
287
288 #[salsa::invoke(
292 crate::graph_algorithms::strongly_connected_components::concrete_function_with_body_inlined_scc
293 )]
294 fn concrete_function_with_body_inlined_scc(
295 &self,
296 function_id: ids::ConcreteFunctionWithBodyId,
297 dependency_type: DependencyType,
298 ) -> Vec<ids::ConcreteFunctionWithBodyId>;
299
300 #[salsa::invoke(crate::scc::function_with_body_scc)]
302 fn function_with_body_scc(
303 &self,
304 function_id: ids::FunctionWithBodyId,
305 dependency_type: DependencyType,
306 ) -> Vec<ids::FunctionWithBodyId>;
307
308 #[salsa::invoke(crate::graph_algorithms::feedback_set::function_with_body_feedback_set)]
313 fn function_with_body_feedback_set(
314 &self,
315 function: ids::ConcreteFunctionWithBodyId,
316 ) -> Maybe<OrderedHashSet<ids::ConcreteFunctionWithBodyId>>;
317
318 #[salsa::invoke(crate::graph_algorithms::feedback_set::needs_withdraw_gas)]
320 fn needs_withdraw_gas(&self, function: ids::ConcreteFunctionWithBodyId) -> Maybe<bool>;
321
322 #[salsa::invoke(crate::graph_algorithms::feedback_set::priv_function_with_body_feedback_set_of_representative)]
325 fn priv_function_with_body_feedback_set_of_representative(
326 &self,
327 function: ConcreteSCCRepresentative,
328 ) -> Maybe<OrderedHashSet<ids::ConcreteFunctionWithBodyId>>;
329
330 #[salsa::invoke(crate::optimizations::config::priv_movable_function_ids)]
332 fn priv_movable_function_ids(&self) -> Arc<UnorderedHashSet<ids::FunctionId>>;
333
334 #[salsa::invoke(crate::optimizations::const_folding::priv_const_folding_info)]
336 fn priv_const_folding_info(
337 &self,
338 ) -> Arc<crate::optimizations::const_folding::ConstFoldingLibfuncInfo>;
339
340 #[salsa::invoke(crate::inline::priv_should_inline)]
342 fn priv_should_inline(&self, function_id: ids::ConcreteFunctionWithBodyId) -> Maybe<bool>;
343
344 #[salsa::input]
346 fn optimization_config(&self) -> Arc<OptimizationConfig>;
347
348 #[salsa::invoke(crate::optimizations::strategy::final_optimization_strategy)]
351 fn final_optimization_strategy(&self) -> OptimizationStrategyId;
352
353 #[salsa::invoke(crate::optimizations::strategy::baseline_optimization_strategy)]
356 fn baseline_optimization_strategy(&self) -> OptimizationStrategyId;
357
358 fn type_size(&self, ty: TypeId) -> usize;
360}
361
362pub fn init_lowering_group(
363 db: &mut (dyn LoweringGroup + 'static),
364 inlining_strategy: InliningStrategy,
365) {
366 let mut moveable_functions: Vec<String> =
367 ["bool_not_impl", "felt252_add", "felt252_sub", "felt252_mul", "felt252_div"]
368 .into_iter()
369 .map(|s| s.to_string())
370 .collect();
371
372 for ty in ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "u128"] {
373 moveable_functions.push(format!("integer::{}_wide_mul", ty));
374 }
375
376 db.set_optimization_config(Arc::new(
377 OptimizationConfig::default()
378 .with_moveable_functions(moveable_functions)
379 .with_inlining_strategy(inlining_strategy),
380 ));
381}
382
383#[derive(Debug, Eq, PartialEq, Clone, Hash)]
384pub struct GenericSCCRepresentative(pub ids::FunctionWithBodyId);
385
386#[derive(Debug, Eq, PartialEq, Clone, Hash)]
387pub struct ConcreteSCCRepresentative(pub ids::ConcreteFunctionWithBodyId);
388
389fn priv_function_with_body_multi_lowering(
392 db: &dyn LoweringGroup,
393 function_id: defs::ids::FunctionWithBodyId,
394) -> Maybe<Arc<MultiLowering>> {
395 let crate_id = function_id.module_file_id(db.upcast()).0.owning_crate(db.upcast());
396 if let Some(map) = db.cached_multi_lowerings(crate_id) {
397 if let Some(multi_lowering) = map.get(&function_id) {
398 return Ok(Arc::new(multi_lowering.clone()));
399 } else {
400 panic!("function not found in cached lowering {:?}", function_id.debug(db));
401 }
402 };
403
404 let multi_lowering = lower_semantic_function(db.upcast(), function_id)?;
405 Ok(Arc::new(multi_lowering))
406}
407
408fn cached_multi_lowerings(
409 db: &dyn LoweringGroup,
410 crate_id: cairo_lang_filesystem::ids::CrateId,
411) -> Option<Arc<OrderedHashMap<defs::ids::FunctionWithBodyId, MultiLowering>>> {
412 load_cached_crate_functions(db, crate_id)
413}
414
415fn priv_function_with_body_lowering(
417 db: &dyn LoweringGroup,
418 function_id: ids::FunctionWithBodyId,
419) -> Maybe<Arc<FlatLowered>> {
420 let semantic_function_id = function_id.base_semantic_function(db);
421 let multi_lowering = db.priv_function_with_body_multi_lowering(semantic_function_id)?;
422 let lowered = match &function_id.lookup_intern(db) {
423 ids::FunctionWithBodyLongId::Semantic(_) => multi_lowering.main_lowering.clone(),
424 ids::FunctionWithBodyLongId::Generated { key, .. } => {
425 multi_lowering.generated_lowerings[key].clone()
426 }
427 };
428 Ok(Arc::new(lowered))
429}
430
431fn function_with_body_lowering_with_borrow_check(
432 db: &dyn LoweringGroup,
433 function_id: ids::FunctionWithBodyId,
434) -> Maybe<(Arc<FlatLowered>, Arc<PotentialDestructCalls>)> {
435 let mut lowered = (*db.priv_function_with_body_lowering(function_id)?).clone();
436 let block_extra_calls =
437 borrow_check(db, function_id.to_concrete(db)?.is_panic_destruct_fn(db)?, &mut lowered);
438 Ok((Arc::new(lowered), Arc::new(block_extra_calls)))
439}
440
441fn function_with_body_lowering(
442 db: &dyn LoweringGroup,
443 function_id: ids::FunctionWithBodyId,
444) -> Maybe<Arc<FlatLowered>> {
445 Ok(db.function_with_body_lowering_with_borrow_check(function_id)?.0)
446}
447
448fn priv_concrete_function_with_body_lowered_flat(
450 db: &dyn LoweringGroup,
451 function: ids::ConcreteFunctionWithBodyId,
452) -> Maybe<Arc<FlatLowered>> {
453 let semantic_db = db.upcast();
454 let mut lowered =
455 (*db.function_with_body_lowering(function.function_with_body_id(db))?).clone();
456 concretize_lowered(db, &mut lowered, &function.substitution(semantic_db)?)?;
457 Ok(Arc::new(lowered))
458}
459
460fn concrete_function_with_body_postpanic_lowered(
464 db: &dyn LoweringGroup,
465 function: ids::ConcreteFunctionWithBodyId,
466) -> Maybe<Arc<FlatLowered>> {
467 let mut lowered = (*db.priv_concrete_function_with_body_lowered_flat(function)?).clone();
468
469 add_withdraw_gas(db, function, &mut lowered)?;
470 lowered = lower_panics(db, function, &lowered)?;
471 add_destructs(db, function, &mut lowered);
472 scrub_units(db, &mut lowered);
473
474 Ok(Arc::new(lowered))
475}
476
477fn optimized_concrete_function_with_body_lowered(
479 db: &dyn LoweringGroup,
480 function: ids::ConcreteFunctionWithBodyId,
481 optimization_strategy: OptimizationStrategyId,
482) -> Maybe<Arc<FlatLowered>> {
483 let mut lowered = (*db.concrete_function_with_body_postpanic_lowered(function)?).clone();
484 optimization_strategy.apply_strategy(db, function, &mut lowered)?;
485 Ok(Arc::new(lowered))
486}
487
488fn inlined_function_with_body_lowered(
490 db: &dyn LoweringGroup,
491 function: ids::ConcreteFunctionWithBodyId,
492) -> Maybe<Arc<FlatLowered>> {
493 db.optimized_concrete_function_with_body_lowered(function, db.baseline_optimization_strategy())
494}
495
496fn final_concrete_function_with_body_lowered(
498 db: &dyn LoweringGroup,
499 function: ids::ConcreteFunctionWithBodyId,
500) -> Maybe<Arc<FlatLowered>> {
501 let mut lowered = (*db.inlined_function_with_body_lowered(function)?).clone();
503
504 db.final_optimization_strategy().apply_strategy(db, function, &mut lowered)?;
505 Ok(Arc::new(lowered))
506}
507
508pub(crate) fn get_direct_callees(
512 db: &dyn LoweringGroup,
513 lowered_function: &FlatLowered,
514 dependency_type: DependencyType,
515 block_extra_calls: &UnorderedHashMap<BlockId, Vec<FunctionId>>,
516) -> Vec<ids::FunctionId> {
517 let mut direct_callees = Vec::new();
518 if lowered_function.blocks.is_empty() {
519 return direct_callees;
520 }
521 let withdraw_gas_fns = corelib::core_withdraw_gas_fns(db.upcast())
522 .map(|id| FunctionLongId::Semantic(id).intern(db));
523 let mut visited = vec![false; lowered_function.blocks.len()];
524 let mut stack = vec![BlockId(0)];
525 while let Some(block_id) = stack.pop() {
526 if visited[block_id.0] {
527 continue;
528 }
529 visited[block_id.0] = true;
530 let block = &lowered_function.blocks[block_id];
531 for statement in &block.statements {
532 if let Statement::Call(statement_call) = statement {
533 if dependency_type != DependencyType::Cost || !statement_call.with_coupon {
537 direct_callees.push(statement_call.function);
538 }
539 }
540 }
541 if let Some(extra_calls) = block_extra_calls.get(&block_id) {
542 direct_callees.extend(extra_calls.iter().copied());
543 }
544 match &block.end {
545 FlatBlockEnd::NotSet | FlatBlockEnd::Return(..) | FlatBlockEnd::Panic(_) => {}
546 FlatBlockEnd::Goto(next, _) => stack.push(*next),
547 FlatBlockEnd::Match { info } => {
548 let mut arms = info.arms().iter();
549 if let MatchInfo::Extern(s) = info {
550 direct_callees.push(s.function);
551 if DependencyType::Cost == dependency_type
552 && withdraw_gas_fns.contains(&s.function)
553 {
554 arms.next();
556 }
557 }
558 stack.extend(arms.map(|arm| arm.block_id));
559 }
560 }
561 }
562 direct_callees
563}
564
565fn concrete_function_with_body_direct_callees(
566 db: &dyn LoweringGroup,
567 function_id: ids::ConcreteFunctionWithBodyId,
568 dependency_type: DependencyType,
569) -> Maybe<Vec<ids::FunctionId>> {
570 let lowered_function = db.priv_concrete_function_with_body_lowered_flat(function_id)?;
571 Ok(get_direct_callees(db, &lowered_function, dependency_type, &Default::default()))
572}
573
574fn concrete_function_with_body_inlined_direct_callees(
575 db: &dyn LoweringGroup,
576 function_id: ids::ConcreteFunctionWithBodyId,
577 dependency_type: DependencyType,
578) -> Maybe<Vec<ids::FunctionId>> {
579 let lowered_function = db.inlined_function_with_body_lowered(function_id)?;
580 Ok(get_direct_callees(db, &lowered_function, dependency_type, &Default::default()))
581}
582
583fn functions_with_body_from_function_ids(
590 db: &dyn LoweringGroup,
591 function_ids: Vec<ids::FunctionId>,
592 dependency_type: DependencyType,
593) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId>> {
594 Ok(function_ids
595 .into_iter()
596 .map(|concrete| {
597 if dependency_type == DependencyType::Cost {
598 if let Some(function_with_body) = extract_coupon_function(db, concrete)? {
599 return Ok(Some(function_with_body));
600 }
601 }
602 concrete.body(db.upcast())
603 })
604 .collect::<Maybe<Vec<_>>>()?
605 .into_iter()
606 .flatten()
607 .collect_vec())
608}
609
610fn extract_coupon_function(
615 db: &dyn LoweringGroup,
616 concrete: ids::FunctionId,
617) -> Maybe<Option<ids::ConcreteFunctionWithBodyId>> {
618 let ids::FunctionLongId::Semantic(function_id) = concrete.lookup_intern(db) else {
620 return Ok(None);
621 };
622
623 let concrete_function = function_id.get_concrete(db.upcast());
625 let generic_function = concrete_function.generic_function;
626 let semantic::items::functions::GenericFunctionId::Extern(extern_function_id) =
627 generic_function
628 else {
629 return Ok(None);
630 };
631 let name = extern_function_id.lookup_intern(db).name(db.upcast());
632 if !(name == "coupon_buy" || name == "coupon_refund") {
633 return Ok(None);
634 }
635
636 let [semantic::GenericArgumentId::Type(type_id)] = concrete_function.generic_args[..] else {
638 panic!("Unexpected generic_args for coupon_buy().");
639 };
640 let semantic::TypeLongId::Coupon(coupon_function) = type_id.lookup_intern(db) else {
641 panic!("Unexpected generic_args for coupon_buy().");
642 };
643
644 let Some(coupon_function_with_body_id) =
646 coupon_function.get_concrete(db.upcast()).body(db.upcast())?
647 else {
648 panic!("Unexpected generic_args for coupon_buy().");
649 };
650
651 Ok(Some(ids::ConcreteFunctionWithBodyId::from_semantic(db, coupon_function_with_body_id)))
652}
653
654fn concrete_function_with_body_direct_callees_with_body(
655 db: &dyn LoweringGroup,
656 function_id: ids::ConcreteFunctionWithBodyId,
657 dependency_type: DependencyType,
658) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId>> {
659 functions_with_body_from_function_ids(
660 db,
661 db.concrete_function_with_body_direct_callees(function_id, dependency_type)?,
662 dependency_type,
663 )
664}
665
666fn concrete_function_with_body_inlined_direct_callees_with_body(
667 db: &dyn LoweringGroup,
668 function_id: ids::ConcreteFunctionWithBodyId,
669 dependency_type: DependencyType,
670) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId>> {
671 functions_with_body_from_function_ids(
672 db,
673 db.concrete_function_with_body_inlined_direct_callees(function_id, dependency_type)?,
674 dependency_type,
675 )
676}
677
678fn final_concrete_function_with_body_lowered_direct_callees(
679 db: &dyn LoweringGroup,
680 function_id: ids::ConcreteFunctionWithBodyId,
681 dependency_type: DependencyType,
682) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId>> {
683 let lowered_function = db.final_concrete_function_with_body_lowered(function_id)?;
684 functions_with_body_from_function_ids(
685 db,
686 get_direct_callees(db, &lowered_function, dependency_type, &Default::default()),
687 dependency_type,
688 )
689}
690
691fn function_with_body_lowering_diagnostics(
692 db: &dyn LoweringGroup,
693 function_id: ids::FunctionWithBodyId,
694) -> Maybe<Diagnostics<LoweringDiagnostic>> {
695 let mut diagnostics = DiagnosticsBuilder::default();
696
697 if let Ok(lowered) = db.function_with_body_lowering(function_id) {
698 diagnostics.extend(lowered.diagnostics.clone());
699 if flag_add_withdraw_gas(db)
700 && !lowered.signature.panicable
701 && db.in_cycle(function_id, DependencyType::Cost)?
702 {
703 let location =
704 Location::new(function_id.base_semantic_function(db).stable_location(db.upcast()));
705 diagnostics.add(LoweringDiagnostic {
706 location,
707 kind: LoweringDiagnosticKind::NoPanicFunctionCycle,
708 });
709 }
710 }
711
712 if let Ok(diag) = get_inline_diagnostics(db, function_id) {
713 diagnostics.extend(diag);
714 }
715
716 Ok(diagnostics.build())
717}
718
719fn semantic_function_with_body_lowering_diagnostics(
720 db: &dyn LoweringGroup,
721 semantic_function_id: defs::ids::FunctionWithBodyId,
722) -> Maybe<Diagnostics<LoweringDiagnostic>> {
723 let mut diagnostics = DiagnosticsBuilder::default();
724
725 if let Ok(multi_lowering) = db.priv_function_with_body_multi_lowering(semantic_function_id) {
726 let function_id = ids::FunctionWithBodyLongId::Semantic(semantic_function_id).intern(db);
727 diagnostics
728 .extend(db.function_with_body_lowering_diagnostics(function_id).unwrap_or_default());
729 for (key, _) in multi_lowering.generated_lowerings.iter() {
730 let function_id =
731 ids::FunctionWithBodyLongId::Generated { parent: semantic_function_id, key: *key }
732 .intern(db);
733 diagnostics.extend(
734 db.function_with_body_lowering_diagnostics(function_id).unwrap_or_default(),
735 );
736 }
737 }
738
739 Ok(diagnostics.build())
740}
741
742fn module_lowering_diagnostics(
743 db: &dyn LoweringGroup,
744 module_id: ModuleId,
745) -> Maybe<Diagnostics<LoweringDiagnostic>> {
746 let mut diagnostics = DiagnosticsBuilder::default();
747 for item in db.module_items(module_id)?.iter() {
748 match item {
749 ModuleItemId::FreeFunction(free_function) => {
750 let function_id = defs::ids::FunctionWithBodyId::Free(*free_function);
751 diagnostics
752 .extend(db.semantic_function_with_body_lowering_diagnostics(function_id)?);
753 }
754 ModuleItemId::Constant(_) => {}
755 ModuleItemId::Submodule(_) => {}
756 ModuleItemId::Use(_) => {}
757 ModuleItemId::Struct(_) => {}
758 ModuleItemId::Enum(_) => {}
759 ModuleItemId::TypeAlias(_) => {}
760 ModuleItemId::ImplAlias(_) => {}
761 ModuleItemId::Trait(trait_id) => {
762 for trait_func in db.trait_functions(*trait_id)?.values() {
763 if matches!(db.trait_function_body(*trait_func), Ok(Some(_))) {
764 let function_id = defs::ids::FunctionWithBodyId::Trait(*trait_func);
765 diagnostics.extend(
766 db.semantic_function_with_body_lowering_diagnostics(function_id)?,
767 );
768 }
769 }
770 }
771 ModuleItemId::Impl(impl_def_id) => {
772 for impl_func in db.impl_functions(*impl_def_id)?.values() {
773 let function_id = defs::ids::FunctionWithBodyId::Impl(*impl_func);
774 diagnostics
775 .extend(db.semantic_function_with_body_lowering_diagnostics(function_id)?);
776 }
777 }
778 ModuleItemId::ExternType(_) => {}
779 ModuleItemId::ExternFunction(_) => {}
780 }
781 }
782 Ok(diagnostics.build())
783}
784
785fn file_lowering_diagnostics(
786 db: &dyn LoweringGroup,
787 file_id: FileId,
788) -> Maybe<Diagnostics<LoweringDiagnostic>> {
789 let mut diagnostics = DiagnosticsBuilder::default();
790 for module_id in db.file_modules(file_id)?.iter().copied() {
791 if let Ok(module_diagnostics) = db.module_lowering_diagnostics(module_id) {
792 diagnostics.extend(module_diagnostics)
793 }
794 }
795 Ok(diagnostics.build())
796}
797
798fn type_size(db: &dyn LoweringGroup, ty: TypeId) -> usize {
799 match ty.lookup_intern(db) {
800 TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
801 ConcreteTypeId::Struct(struct_id) => db
802 .concrete_struct_members(struct_id)
803 .unwrap()
804 .iter()
805 .map(|(_, member)| db.type_size(member.ty))
806 .sum::<usize>(),
807 ConcreteTypeId::Enum(enum_id) => {
808 1 + db
809 .concrete_enum_variants(enum_id)
810 .unwrap()
811 .into_iter()
812 .map(|variant| db.type_size(variant.ty))
813 .max()
814 .unwrap_or_default()
815 }
816 ConcreteTypeId::Extern(extern_id) => {
817 match extern_id.extern_type_id(db.upcast()).name(db.upcast()).as_str() {
818 "Array" | "SquashedFelt252Dict" | "EcPoint" => 2,
819 "EcState" => 3,
820 "Uint128MulGuarantee" => 4,
821 _ => 1,
822 }
823 }
824 },
825 TypeLongId::Tuple(types) => types.into_iter().map(|ty| db.type_size(ty)).sum::<usize>(),
826 TypeLongId::Snapshot(ty) => db.type_size(ty),
827 TypeLongId::FixedSizeArray { type_id, size } => {
828 db.type_size(type_id)
829 * size
830 .lookup_intern(db)
831 .into_int()
832 .expect("Expected ConstValue::Int for size")
833 .to_usize()
834 .unwrap()
835 }
836 TypeLongId::Closure(closure_ty) => {
837 closure_ty.captured_types.iter().map(|ty| db.type_size(*ty)).sum()
838 }
839 TypeLongId::Coupon(_) => 0,
840 TypeLongId::GenericParameter(_)
841 | TypeLongId::Var(_)
842 | TypeLongId::ImplType(_)
843 | TypeLongId::Missing(_) => {
844 panic!("Function should only be called with fully concrete types")
845 }
846 }
847}