cairo_lang_lowering/add_withdraw_gas/
mod.rs1use cairo_lang_diagnostics::Maybe;
2use cairo_lang_semantic::corelib::{
3 core_array_felt252_ty, core_felt252_ty, core_module, core_submodule, get_function_id,
4 get_ty_by_name, option_none_variant, option_some_variant, unit_ty,
5};
6use cairo_lang_semantic::items::constant::ConstValue;
7use cairo_lang_semantic::{GenericArgumentId, MatchArmSelector, TypeLongId};
8use cairo_lang_utils::Intern;
9use num_bigint::{BigInt, Sign};
10
11use crate::db::LoweringGroup;
12use crate::ids::{ConcreteFunctionWithBodyId, LocationId, SemanticFunctionIdEx};
13use crate::lower::context::{VarRequest, VariableAllocator};
14use crate::{
15 BlockId, FlatBlock, FlatBlockEnd, FlatLowered, MatchArm, MatchExternInfo, MatchInfo, Statement,
16 StatementCall, StatementConst, StatementStructConstruct, VarUsage,
17};
18
19pub fn add_withdraw_gas(
22 db: &dyn LoweringGroup,
23 function: ConcreteFunctionWithBodyId,
24 lowered: &mut FlatLowered,
25) -> Maybe<()> {
26 if db.needs_withdraw_gas(function)? {
27 add_withdraw_gas_to_function(db, function, lowered)?;
28 }
29
30 Ok(())
31}
32
33fn add_withdraw_gas_to_function(
37 db: &dyn LoweringGroup,
38 function: ConcreteFunctionWithBodyId,
39 lowered: &mut FlatLowered,
40) -> Maybe<()> {
41 let location = LocationId::from_stable_location(db, function.stable_location(db)?)
42 .with_auto_generation_note(db, "withdraw_gas");
43 let panic_block = create_panic_block(db, function, lowered, location)?;
44
45 let old_root_block = lowered.blocks.root_block()?.clone();
46 let old_root_new_id = lowered.blocks.push(old_root_block);
47 let panic_block_id = lowered.blocks.push(panic_block);
48 let new_root_block = FlatBlock {
49 statements: vec![],
50 end: FlatBlockEnd::Match {
51 info: MatchInfo::Extern(MatchExternInfo {
52 function: get_function_id(
53 db.upcast(),
54 core_submodule(db.upcast(), "gas"),
55 "withdraw_gas".into(),
56 vec![],
57 )
58 .lowered(db),
59 inputs: vec![],
60 arms: vec![
61 MatchArm {
62 arm_selector: MatchArmSelector::VariantId(option_some_variant(
63 db.upcast(),
64 GenericArgumentId::Type(unit_ty(db.upcast())),
65 )),
66 block_id: old_root_new_id,
67 var_ids: vec![],
68 },
69 MatchArm {
70 arm_selector: MatchArmSelector::VariantId(option_none_variant(
71 db.upcast(),
72 GenericArgumentId::Type(unit_ty(db.upcast())),
73 )),
74 block_id: panic_block_id,
75 var_ids: vec![],
76 },
77 ],
78 location,
79 }),
80 },
81 };
82
83 lowered.blocks.reset_block(BlockId(0), new_root_block);
84
85 Ok(())
86}
87
88fn create_panic_block(
90 db: &dyn LoweringGroup,
91 function: ConcreteFunctionWithBodyId,
92 lowered: &mut FlatLowered,
93 location: LocationId,
94) -> Maybe<FlatBlock> {
95 let mut variables = VariableAllocator::new(
96 db,
97 function.function_with_body_id(db).base_semantic_function(db),
98 lowered.variables.clone(),
99 )?;
100 let new_array_var =
101 variables.new_var(VarRequest { ty: core_array_felt252_ty(db.upcast()), location });
102 let out_of_gas_err_var =
103 variables.new_var(VarRequest { ty: core_felt252_ty(db.upcast()), location });
104 let panic_instance_var = variables.new_var(VarRequest {
105 ty: get_ty_by_name(db.upcast(), core_module(db.upcast()), "Panic".into(), vec![]),
106 location,
107 });
108 let panic_data_var =
109 variables.new_var(VarRequest { ty: core_array_felt252_ty(db.upcast()), location });
110 let err_data_var = variables.new_var(VarRequest {
111 ty: TypeLongId::Tuple(vec![variables[panic_instance_var].ty, variables[panic_data_var].ty])
112 .intern(db),
113 location,
114 });
115 lowered.variables = variables.variables;
116
117 let array_module = core_submodule(db.upcast(), "array");
118
119 let add_location = |var_id| VarUsage { var_id, location };
120
121 Ok(FlatBlock {
124 statements: vec![
125 Statement::Call(StatementCall {
126 function: get_function_id(db.upcast(), array_module, "array_new".into(), vec![
127 GenericArgumentId::Type(core_felt252_ty(db.upcast())),
128 ])
129 .lowered(db),
130 inputs: vec![],
131 with_coupon: false,
132 outputs: vec![new_array_var],
133 location,
134 }),
135 Statement::Const(StatementConst {
136 value: ConstValue::Int(
137 BigInt::from_bytes_be(Sign::Plus, "Out of gas".as_bytes()),
138 core_felt252_ty(db.upcast()),
139 ),
140 output: out_of_gas_err_var,
141 }),
142 Statement::Call(StatementCall {
143 function: get_function_id(db.upcast(), array_module, "array_append".into(), vec![
144 GenericArgumentId::Type(core_felt252_ty(db.upcast())),
145 ])
146 .lowered(db),
147 inputs: vec![new_array_var, out_of_gas_err_var]
148 .into_iter()
149 .map(add_location)
150 .collect(),
151 with_coupon: false,
152 outputs: vec![panic_data_var],
153 location,
154 }),
155 Statement::StructConstruct(StatementStructConstruct {
156 inputs: vec![],
157 output: panic_instance_var,
158 }),
159 Statement::StructConstruct(StatementStructConstruct {
160 inputs: vec![panic_instance_var, panic_data_var]
161 .into_iter()
162 .map(add_location)
163 .collect(),
164 output: err_data_var,
165 }),
166 ],
167 end: FlatBlockEnd::Panic(VarUsage { var_id: err_data_var, location }),
168 })
169}