use cairo_lang_debug::DebugWithDb;
use cairo_lang_defs as defs;
use cairo_lang_defs::db::DefsGroup;
use cairo_lang_defs::ids::{
ExternFunctionId, LanguageElementId, ModuleId, ModuleItemId, NamedLanguageElementLongId,
};
use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe, MaybeAsRef};
use cairo_lang_filesystem::flag::FlagsGroup;
use cairo_lang_filesystem::ids::{FileId, Tracked};
use cairo_lang_semantic::items::enm::SemanticEnumEx;
use cairo_lang_semantic::items::imp::ImplSemantic;
use cairo_lang_semantic::items::macro_call::MacroCallSemantic;
use cairo_lang_semantic::items::structure::StructSemantic;
use cairo_lang_semantic::items::trt::TraitSemantic;
use cairo_lang_semantic::{self as semantic, ConcreteTypeId, TypeId, TypeLongId, corelib};
use cairo_lang_utils::Intern;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
use defs::ids::NamedLanguageElementId;
use itertools::Itertools;
use num_traits::ToPrimitive;
use salsa::{Database, Setter};
use crate::add_withdraw_gas::add_withdraw_gas;
use crate::borrow_check::{BorrowCheckResult, borrow_check, borrow_check_possible_withdraw_gas};
use crate::cache::load_cached_crate_functions;
use crate::concretize::concretize_lowered;
use crate::destructs::add_destructs;
use crate::diagnostic::{LoweringDiagnostic, LoweringDiagnosticKind};
use crate::ids::{ConcreteFunctionWithBodyId, FunctionId, FunctionLongId, GenericOrSpecialized};
use crate::inline::get_inline_diagnostics;
use crate::inline::statements_weights::{ApproxCasmInlineWeight, InlineWeight};
use crate::lower::{MultiLowering, lower_semantic_function};
use crate::optimizations::config::Optimizations;
use crate::optimizations::scrub_units::scrub_units;
use crate::optimizations::strategy::OptimizationStrategyId;
use crate::panic::lower_panics;
use crate::specialization::specialized_function_lowered;
use crate::{
BlockEnd, BlockId, DependencyType, Location, Lowered, LoweringStage, MatchInfo, Statement, ids,
};
type CodeSizeEstimator = fn(&dyn Database, ConcreteFunctionWithBodyId<'_>) -> Maybe<isize>;
#[salsa::input]
pub struct LoweringGroupInput {
#[returns(ref)]
pub optimizations: Option<Optimizations>,
#[returns(ref)]
code_size_estimator: Option<CodeSizeEstimator>,
}
#[salsa::tracked(returns(ref))]
pub fn lowering_group_input(db: &dyn Database) -> LoweringGroupInput {
LoweringGroupInput::new(db, None, None)
}
pub trait LoweringGroup: Database {
fn priv_function_with_body_multi_lowering<'db>(
&'db self,
function_id: defs::ids::FunctionWithBodyId<'db>,
) -> Maybe<&'db MultiLowering<'db>> {
priv_function_with_body_multi_lowering(self.as_dyn_database(), (), function_id)
.maybe_as_ref()
}
fn cached_multi_lowerings<'db>(
&'db self,
crate_id: cairo_lang_filesystem::ids::CrateId<'db>,
) -> Option<&'db OrderedHashMap<defs::ids::FunctionWithBodyId<'db>, MultiLowering<'db>>> {
cached_multi_lowerings(self.as_dyn_database(), crate_id).as_ref()
}
fn function_with_body_lowering<'db>(
&'db self,
function_id: ids::FunctionWithBodyId<'db>,
) -> Maybe<&'db Lowered<'db>> {
function_with_body_lowering(self.as_dyn_database(), function_id)
}
fn borrow_check<'db>(
&'db self,
function_id: ids::FunctionWithBodyId<'db>,
) -> Maybe<&'db BorrowCheckResult<'db>> {
borrow_check_tracked(self.as_dyn_database(), function_id).maybe_as_ref()
}
fn lowered_body<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
stage: LoweringStage,
) -> Maybe<&'db Lowered<'db>> {
lowered_body(self.as_dyn_database(), function_id, stage).maybe_as_ref()
}
fn lowered_direct_callees<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
dependency_type: DependencyType,
stage: LoweringStage,
) -> Maybe<&'db [ids::FunctionId<'db>]> {
Ok(lowered_direct_callees(self.as_dyn_database(), function_id, dependency_type, stage)
.maybe_as_ref()?)
}
fn lowered_direct_callees_with_body<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
dependency_type: DependencyType,
stage: LoweringStage,
) -> Maybe<&'db [ids::ConcreteFunctionWithBodyId<'db>]> {
Ok(lowered_direct_callees_with_body(
self.as_dyn_database(),
function_id,
dependency_type,
stage,
)
.maybe_as_ref()?)
}
fn function_with_body_lowering_diagnostics<'db>(
&'db self,
function_id: ids::FunctionWithBodyId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
function_with_body_lowering_diagnostics(self.as_dyn_database(), function_id)
}
fn semantic_function_with_body_lowering_diagnostics<'db>(
&'db self,
function_id: defs::ids::FunctionWithBodyId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
semantic_function_with_body_lowering_diagnostics(self.as_dyn_database(), (), function_id)
}
fn module_lowering_diagnostics<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
module_lowering_diagnostics(self.as_dyn_database(), (), module_id)
}
fn file_lowering_diagnostics<'db>(
&'db self,
file_id: FileId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
file_lowering_diagnostics(self.as_dyn_database(), file_id)
}
fn function_implicits<'db>(
&'db self,
function: ids::FunctionId<'db>,
) -> Maybe<Vec<TypeId<'db>>> {
crate::implicits::function_implicits(self.as_dyn_database(), function)
}
fn function_may_panic<'db>(&'db self, function: ids::FunctionId<'db>) -> Maybe<bool> {
crate::panic::function_may_panic(self.as_dyn_database(), function)
}
fn has_direct_panic<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
) -> Maybe<bool> {
crate::panic::has_direct_panic(self.as_dyn_database(), function_id)
}
fn function_with_body_direct_callees<'db>(
&'db self,
function_id: ids::FunctionWithBodyId<'db>,
dependency_type: DependencyType,
) -> Maybe<&'db OrderedHashSet<ids::FunctionId<'db>>> {
crate::graph_algorithms::cycles::function_with_body_direct_callees(
self.as_dyn_database(),
function_id,
dependency_type,
)
.maybe_as_ref()
}
fn function_with_body_direct_function_with_body_callees<'db>(
&'db self,
function_id: ids::FunctionWithBodyId<'db>,
dependency_type: DependencyType,
) -> Maybe<&'db OrderedHashSet<ids::FunctionWithBodyId<'db>>> {
crate::graph_algorithms::cycles::function_with_body_direct_function_with_body_callees(
self.as_dyn_database(),
function_id,
dependency_type,
)
.maybe_as_ref()
}
fn final_contains_call_cycle<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
) -> Maybe<bool> {
crate::graph_algorithms::cycles::final_contains_call_cycle(
self.as_dyn_database(),
function_id,
)
}
fn in_cycle<'db>(
&'db self,
function_id: ids::FunctionWithBodyId<'db>,
dependency_type: DependencyType,
) -> Maybe<bool> {
crate::graph_algorithms::cycles::in_cycle(
self.as_dyn_database(),
function_id,
dependency_type,
)
}
fn concrete_in_cycle<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
dependency_type: DependencyType,
stage: LoweringStage,
) -> Maybe<bool> {
crate::graph_algorithms::cycles::concrete_in_cycle(
self.as_dyn_database(),
function_id,
dependency_type,
stage,
)
}
fn lowered_scc_representative<'db>(
&'db self,
function: ids::ConcreteFunctionWithBodyId<'db>,
dependency_type: DependencyType,
stage: LoweringStage,
) -> ConcreteSCCRepresentative<'db> {
crate::graph_algorithms::strongly_connected_components::lowered_scc_representative(
self.as_dyn_database(),
function,
dependency_type,
stage,
)
}
fn lowered_scc<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
dependency_type: DependencyType,
stage: LoweringStage,
) -> Vec<ids::ConcreteFunctionWithBodyId<'db>> {
crate::graph_algorithms::strongly_connected_components::lowered_scc(
self.as_dyn_database(),
function_id,
dependency_type,
stage,
)
}
fn function_with_body_scc<'db>(
&'db self,
function_id: ids::FunctionWithBodyId<'db>,
dependency_type: DependencyType,
) -> &'db [ids::FunctionWithBodyId<'db>] {
crate::scc::function_with_body_scc(self.as_dyn_database(), function_id, dependency_type)
}
fn function_with_body_feedback_set<'db>(
&'db self,
function: ids::ConcreteFunctionWithBodyId<'db>,
stage: LoweringStage,
) -> Maybe<&'db OrderedHashSet<ids::ConcreteFunctionWithBodyId<'db>>> {
crate::graph_algorithms::feedback_set::function_with_body_feedback_set(
self.as_dyn_database(),
function,
stage,
)
.maybe_as_ref()
}
fn needs_withdraw_gas<'db>(
&'db self,
function: ids::ConcreteFunctionWithBodyId<'db>,
) -> Maybe<bool> {
crate::graph_algorithms::feedback_set::needs_withdraw_gas(self.as_dyn_database(), function)
}
fn priv_movable_function_ids<'db>(&'db self) -> &'db UnorderedHashSet<ExternFunctionId<'db>> {
crate::optimizations::config::priv_movable_function_ids(self.as_dyn_database())
}
fn priv_should_inline<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
) -> Maybe<bool> {
crate::inline::priv_should_inline(self.as_dyn_database(), function_id)
}
fn priv_never_inline<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
) -> Maybe<bool> {
crate::inline::priv_never_inline(self.as_dyn_database(), function_id)
}
fn priv_should_specialize<'db>(
&'db self,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
) -> Maybe<bool> {
crate::specialization::priv_should_specialize(self.as_dyn_database(), function_id)
}
fn optimizations(&self) -> &Optimizations {
let db = self.as_dyn_database();
lowering_group_input(db).optimizations(db).as_ref().unwrap()
}
fn final_optimization_strategy<'db>(&'db self) -> OptimizationStrategyId<'db> {
crate::optimizations::strategy::final_optimization_strategy(self.as_dyn_database())
}
fn baseline_optimization_strategy<'db>(&'db self) -> OptimizationStrategyId<'db> {
crate::optimizations::strategy::baseline_optimization_strategy(self.as_dyn_database())
}
fn type_size<'db>(&'db self, ty: TypeId<'db>) -> usize {
type_size(self.as_dyn_database(), ty)
}
fn estimate_size<'db>(&'db self, function_id: ConcreteFunctionWithBodyId<'db>) -> Maybe<isize> {
estimate_size(self.as_dyn_database(), function_id)
}
}
impl<T: Database + ?Sized> LoweringGroup for T {}
pub fn init_lowering_group(
db: &mut (dyn Database + 'static),
optimizations: Optimizations,
code_size_estimator: Option<CodeSizeEstimator>,
) {
lowering_group_input(db).set_optimizations(db).to(Some(optimizations));
lowering_group_input(db).set_code_size_estimator(db).to(code_size_estimator);
}
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct GenericSCCRepresentative<'db>(pub ids::FunctionWithBodyId<'db>);
#[derive(Debug, Eq, PartialEq, Clone, Hash, salsa::Update)]
pub struct ConcreteSCCRepresentative<'db>(pub ids::ConcreteFunctionWithBodyId<'db>);
#[salsa::tracked(returns(ref))]
fn priv_function_with_body_multi_lowering<'db>(
db: &'db dyn Database,
_tracked: Tracked,
function_id: defs::ids::FunctionWithBodyId<'db>,
) -> Maybe<MultiLowering<'db>> {
let crate_id = function_id.parent_module(db).owning_crate(db);
if let Some(map) = db.cached_multi_lowerings(crate_id) {
if let Some(multi_lowering) = map.get(&function_id) {
return Ok(multi_lowering.clone());
} else {
panic!("function not found in cached lowering {:?}", function_id.debug(db));
}
};
lower_semantic_function(db, function_id)
}
#[salsa::tracked(returns(ref))]
fn cached_multi_lowerings<'db>(
db: &'db dyn Database,
crate_id: cairo_lang_filesystem::ids::CrateId<'db>,
) -> Option<OrderedHashMap<defs::ids::FunctionWithBodyId<'db>, MultiLowering<'db>>> {
load_cached_crate_functions(db, crate_id)
}
fn function_with_body_lowering<'db>(
db: &'db dyn Database,
function_id: ids::FunctionWithBodyId<'db>,
) -> Maybe<&'db Lowered<'db>> {
let semantic_function_id = function_id.base_semantic_function(db);
let multi_lowering = db.priv_function_with_body_multi_lowering(semantic_function_id)?;
Ok(match &function_id.long(db) {
ids::FunctionWithBodyLongId::Semantic(_) => &multi_lowering.main_lowering,
ids::FunctionWithBodyLongId::Generated { key, .. } => {
&multi_lowering.generated_lowerings[key]
}
})
}
#[salsa::tracked(returns(ref))]
fn borrow_check_tracked<'db>(
db: &'db dyn Database,
function_id: ids::FunctionWithBodyId<'db>,
) -> Maybe<BorrowCheckResult<'db>> {
let lowered = db.function_with_body_lowering(function_id)?;
Ok(borrow_check(db, function_id.to_concrete(db)?.is_panic_destruct_fn(db)?, lowered))
}
#[salsa::tracked(returns(ref))]
fn lowered_body<'db>(
db: &'db dyn Database,
function: ids::ConcreteFunctionWithBodyId<'db>,
stage: LoweringStage,
) -> Maybe<Lowered<'db>> {
Ok(match stage {
LoweringStage::Monomorphized => match function.generic_or_specialized(db) {
GenericOrSpecialized::Generic(generic_function_id) => {
db.function_with_body_lowering_diagnostics(generic_function_id)?
.check_error_free()?;
let mut lowered = db.function_with_body_lowering(generic_function_id)?.clone();
concretize_lowered(db, &mut lowered, &function.substitution(db)?)?;
lowered
}
GenericOrSpecialized::Specialized(specialized) => {
specialized_function_lowered(db, specialized)?
}
},
LoweringStage::PreOptimizations => {
let mut lowered = db.lowered_body(function, LoweringStage::Monomorphized)?.clone();
add_withdraw_gas(db, function, &mut lowered)?;
lower_panics(db, function, &mut lowered)?;
add_destructs(db, function, &mut lowered);
scrub_units(db, &mut lowered);
lowered
}
LoweringStage::PostBaseline => {
let mut lowered = db.lowered_body(function, LoweringStage::PreOptimizations)?.clone();
db.baseline_optimization_strategy().apply_strategy(db, function, &mut lowered)?;
lowered
}
LoweringStage::Final => {
let mut lowered = db.lowered_body(function, LoweringStage::PostBaseline)?.clone();
db.final_optimization_strategy().apply_strategy(db, function, &mut lowered)?;
lowered
}
})
}
pub(crate) fn get_direct_callees<'db>(
db: &dyn Database,
lowered_function: &Lowered<'db>,
dependency_type: DependencyType,
block_extra_calls: &UnorderedHashMap<BlockId, Vec<FunctionId<'db>>>,
) -> Vec<ids::FunctionId<'db>> {
let mut direct_callees = Vec::new();
if lowered_function.blocks.is_empty() {
return direct_callees;
}
let withdraw_gas_fns =
corelib::core_withdraw_gas_fns(db).map(|id| FunctionLongId::Semantic(id).intern(db));
let mut visited = vec![false; lowered_function.blocks.len()];
let mut stack = vec![BlockId(0)];
while let Some(block_id) = stack.pop() {
if visited[block_id.0] {
continue;
}
visited[block_id.0] = true;
let block = &lowered_function.blocks[block_id];
for statement in &block.statements {
if let Statement::Call(statement_call) = statement {
if dependency_type != DependencyType::Cost || !statement_call.with_coupon {
direct_callees.push(statement_call.function);
}
}
}
if let Some(extra_calls) = block_extra_calls.get(&block_id) {
direct_callees.extend(extra_calls.iter().copied());
}
match &block.end {
BlockEnd::NotSet | BlockEnd::Return(..) | BlockEnd::Panic(_) => {}
BlockEnd::Goto(next, _) => stack.push(*next),
BlockEnd::Match { info } => {
let mut arms = info.arms().iter();
if let MatchInfo::Extern(s) = info {
direct_callees.push(s.function);
if DependencyType::Cost == dependency_type
&& withdraw_gas_fns.contains(&s.function)
{
arms.next();
}
}
stack.extend(arms.map(|arm| arm.block_id));
}
}
}
direct_callees
}
fn functions_with_body_from_function_ids<'db>(
db: &'db dyn Database,
function_ids: &'db [ids::FunctionId<'db>],
dependency_type: DependencyType,
) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId<'db>>> {
Ok(function_ids
.iter()
.map(|concrete| {
if dependency_type == DependencyType::Cost
&& let Some(function_with_body) = extract_coupon_function(db, *concrete)?
{
return Ok(Some(function_with_body));
}
concrete.body(db)
})
.collect::<Maybe<Vec<_>>>()?
.into_iter()
.flatten()
.collect_vec())
}
fn extract_coupon_function<'db>(
db: &'db dyn Database,
concrete: ids::FunctionId<'db>,
) -> Maybe<Option<ids::ConcreteFunctionWithBodyId<'db>>> {
let ids::FunctionLongId::Semantic(function_id) = concrete.long(db) else {
return Ok(None);
};
let concrete_function = function_id.get_concrete(db);
let generic_function = concrete_function.generic_function;
let semantic::items::functions::GenericFunctionId::Extern(extern_function_id) =
generic_function
else {
return Ok(None);
};
let name = extern_function_id.long(db).name(db).long(db);
if !(name == "coupon_buy" || name == "coupon_refund") {
return Ok(None);
}
let [semantic::GenericArgumentId::Type(type_id)] = concrete_function.generic_args[..] else {
panic!("Unexpected generic_args for coupon_buy().");
};
let semantic::TypeLongId::Coupon(coupon_function) = type_id.long(db) else {
panic!("Unexpected generic_args for coupon_buy().");
};
let Some(coupon_function_with_body_id) = coupon_function.get_concrete(db).body(db)? else {
panic!("Unexpected generic_args for coupon_buy().");
};
Ok(Some(ids::ConcreteFunctionWithBodyId::from_semantic(db, coupon_function_with_body_id)))
}
#[salsa::tracked(returns(ref))]
fn lowered_direct_callees<'db>(
db: &'db dyn Database,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
dependency_type: DependencyType,
stage: LoweringStage,
) -> Maybe<Vec<ids::FunctionId<'db>>> {
let lowered_function = db.lowered_body(function_id, stage)?;
Ok(get_direct_callees(db, lowered_function, dependency_type, &Default::default()))
}
#[salsa::tracked(returns(ref))]
fn lowered_direct_callees_with_body<'db>(
db: &'db dyn Database,
function_id: ids::ConcreteFunctionWithBodyId<'db>,
dependency_type: DependencyType,
stage: LoweringStage,
) -> Maybe<Vec<ids::ConcreteFunctionWithBodyId<'db>>> {
functions_with_body_from_function_ids(
db,
db.lowered_direct_callees(function_id, dependency_type, stage)?,
dependency_type,
)
}
#[salsa::tracked]
fn function_with_body_lowering_diagnostics<'db>(
db: &'db dyn Database,
function_id: ids::FunctionWithBodyId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
let mut diagnostics = DiagnosticsBuilder::default();
if let Ok(lowered) = db.function_with_body_lowering(function_id) {
diagnostics.extend(lowered.diagnostics.clone());
if let Ok(bc) = db.borrow_check(function_id) {
diagnostics.extend(bc.diagnostics.clone());
}
if db.flag_add_withdraw_gas() && db.in_cycle(function_id, DependencyType::Cost)? {
let location =
Location::new(function_id.base_semantic_function(db).stable_location(db));
if !lowered.signature.panicable {
diagnostics.add(LoweringDiagnostic {
location: location.clone(),
kind: LoweringDiagnosticKind::NoPanicFunctionCycle,
});
}
borrow_check_possible_withdraw_gas(db, location.intern(db), lowered, &mut diagnostics)
}
}
if let Ok(diag) = get_inline_diagnostics(db, function_id) {
diagnostics.extend(diag);
}
Ok(diagnostics.build())
}
#[salsa::tracked]
fn semantic_function_with_body_lowering_diagnostics<'db>(
db: &'db dyn Database,
_tracked: Tracked,
semantic_function_id: defs::ids::FunctionWithBodyId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
let mut diagnostics = DiagnosticsBuilder::default();
if let Ok(multi_lowering) = db.priv_function_with_body_multi_lowering(semantic_function_id) {
let function_id = ids::FunctionWithBodyLongId::Semantic(semantic_function_id).intern(db);
diagnostics
.extend(db.function_with_body_lowering_diagnostics(function_id).unwrap_or_default());
for (key, _) in multi_lowering.generated_lowerings.iter() {
let function_id =
ids::FunctionWithBodyLongId::Generated { parent: semantic_function_id, key: *key }
.intern(db);
diagnostics.extend(
db.function_with_body_lowering_diagnostics(function_id).unwrap_or_default(),
);
}
}
Ok(diagnostics.build())
}
#[salsa::tracked]
fn module_lowering_diagnostics<'db>(
db: &'db dyn Database,
_tracked: Tracked,
module_id: ModuleId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
let mut diagnostics = DiagnosticsBuilder::default();
for item in module_id.module_data(db)?.items(db).iter() {
match item {
ModuleItemId::FreeFunction(free_function) => {
let function_id = defs::ids::FunctionWithBodyId::Free(*free_function);
diagnostics
.extend(db.semantic_function_with_body_lowering_diagnostics(function_id)?);
}
ModuleItemId::Constant(_) => {}
ModuleItemId::Submodule(_) => {}
ModuleItemId::Use(_) => {}
ModuleItemId::Struct(_) => {}
ModuleItemId::Enum(_) => {}
ModuleItemId::TypeAlias(_) => {}
ModuleItemId::ImplAlias(_) => {}
ModuleItemId::Trait(trait_id) => {
for trait_func in db.trait_functions(*trait_id)?.values() {
if matches!(db.trait_function_body(*trait_func), Ok(Some(_))) {
let function_id = defs::ids::FunctionWithBodyId::Trait(*trait_func);
diagnostics.extend(
db.semantic_function_with_body_lowering_diagnostics(function_id)?,
);
}
}
}
ModuleItemId::Impl(impl_def_id) => {
for impl_func in db.impl_functions(*impl_def_id)?.values() {
let function_id = defs::ids::FunctionWithBodyId::Impl(*impl_func);
diagnostics
.extend(db.semantic_function_with_body_lowering_diagnostics(function_id)?);
}
}
ModuleItemId::ExternType(_) => {}
ModuleItemId::ExternFunction(_) => {}
ModuleItemId::MacroDeclaration(_) => {}
}
}
for macro_call in db.module_macro_calls_ids(module_id)?.iter() {
if let Ok(macro_module_id) = db.macro_call_module_id(*macro_call)
&& let Ok(lowering_diags) = db.module_lowering_diagnostics(macro_module_id)
{
diagnostics.extend(lowering_diags);
}
}
Ok(diagnostics.build())
}
#[salsa::tracked]
fn file_lowering_diagnostics<'db>(
db: &'db dyn Database,
file_id: FileId<'db>,
) -> Maybe<Diagnostics<'db, LoweringDiagnostic<'db>>> {
let mut diagnostics = DiagnosticsBuilder::default();
for module_id in db.file_modules(file_id)?.iter().copied() {
if let Ok(module_diagnostics) = db.module_lowering_diagnostics(module_id) {
diagnostics.extend(module_diagnostics)
}
}
Ok(diagnostics.build())
}
#[salsa::tracked]
fn type_size<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> usize {
match ty.long(db) {
TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
ConcreteTypeId::Struct(struct_id) => db
.concrete_struct_members(*struct_id)
.unwrap()
.iter()
.map(|(_, member)| db.type_size(member.ty))
.sum::<usize>(),
ConcreteTypeId::Enum(enum_id) => {
1 + db
.concrete_enum_variants(*enum_id)
.unwrap()
.into_iter()
.map(|variant| db.type_size(variant.ty))
.max()
.unwrap_or_default()
}
ConcreteTypeId::Extern(extern_id) => {
match extern_id.extern_type_id(db).name(db).long(db).as_str() {
"Array" | "SquashedFelt252Dict" | "EcPoint" => 2,
"EcState" => 3,
"Uint128MulGuarantee" => 4,
_ => 1,
}
}
},
TypeLongId::Tuple(types) => types.iter().map(|ty| db.type_size(*ty)).sum::<usize>(),
TypeLongId::Snapshot(ty) => db.type_size(*ty),
TypeLongId::FixedSizeArray { type_id, size } => {
db.type_size(*type_id)
* size
.long(db)
.to_int()
.expect("Expected ConstValue::Int for size")
.to_usize()
.unwrap()
}
TypeLongId::Closure(closure_ty) => {
closure_ty.captured_types.iter().map(|ty| db.type_size(*ty)).sum()
}
TypeLongId::Coupon(_) => 0,
TypeLongId::GenericParameter(_)
| TypeLongId::Var(_)
| TypeLongId::ImplType(_)
| TypeLongId::Missing(_) => {
panic!("Function should only be called with fully concrete types")
}
}
}
fn estimate_size<'db>(
db: &'db dyn Database,
function_id: ConcreteFunctionWithBodyId<'db>,
) -> Maybe<isize> {
let code_size_estimator = lowering_group_input(db).code_size_estimator(db);
if let Some(estimator) = code_size_estimator {
estimator(db, function_id)
} else {
let lowered = db.lowered_body(function_id, LoweringStage::PostBaseline)?;
Ok(ApproxCasmInlineWeight::new(db, lowered).lowered_weight(lowered))
}
}