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