cairo_lang_lowering/lower/
generators.rs1use cairo_lang_semantic as semantic;
5use cairo_lang_semantic::ConcreteVariant;
6use cairo_lang_semantic::corelib::core_box_ty;
7use cairo_lang_semantic::items::constant::ConstValueId;
8use cairo_lang_utils::{Intern, extract_matches};
9use itertools::chain;
10
11use super::VariableId;
12use super::context::VarRequest;
13use crate::ids::LocationId;
14use crate::lower::context::LoweringContext;
15use crate::objects::{
16 Statement, StatementCall, StatementConst, StatementStructConstruct, StatementStructDestructure,
17 VarUsage,
18};
19use crate::{
20 StatementDesnap, StatementEnumConstruct, StatementIntoBox, StatementSnapshot, StatementUnbox,
21};
22
23#[derive(Clone, Default)]
24pub struct StatementsBuilder<'db> {
25 pub statements: Vec<Statement<'db>>,
26}
27impl<'db> StatementsBuilder<'db> {
28 pub fn push_statement(&mut self, statement: Statement<'db>) {
30 self.statements.push(statement);
31 }
32}
33
34pub struct Const<'db> {
36 pub value: ConstValueId<'db>,
37 pub location: LocationId<'db>,
38 pub ty: semantic::TypeId<'db>,
40}
41impl<'db> Const<'db> {
42 pub fn add(
43 self,
44 ctx: &mut LoweringContext<'db, '_>,
45 builder: &mut StatementsBuilder<'db>,
46 ) -> VarUsage<'db> {
47 let output = ctx.new_var(VarRequest { ty: self.ty, location: self.location });
48 builder.push_statement(Statement::Const(StatementConst::new_flat(self.value, output)));
49 VarUsage { var_id: output, location: self.location }
50 }
51}
52
53pub struct Call<'db> {
56 pub function: crate::ids::FunctionId<'db>,
58 pub inputs: Vec<VarUsage<'db>>,
60 pub coupon_input: Option<VarUsage<'db>>,
62 pub extra_ret_tys: Vec<semantic::TypeId<'db>>,
64 pub ret_tys: Vec<semantic::TypeId<'db>>,
66 pub location: LocationId<'db>,
68}
69impl<'db> Call<'db> {
70 pub fn add(
72 self,
73 ctx: &mut LoweringContext<'db, '_>,
74 builder: &mut StatementsBuilder<'db>,
75 ) -> CallResult<'db> {
76 let returns = self
77 .ret_tys
78 .into_iter()
79 .map(|ty| ctx.new_var_usage(VarRequest { ty, location: self.location }))
80 .collect();
81 let extra_outputs = self
82 .extra_ret_tys
83 .into_iter()
84 .map(|ty| ctx.new_var_usage(VarRequest { ty, location: self.location }))
85 .collect();
86 let outputs = chain!(&extra_outputs, &returns)
87 .map(|var_usage: &VarUsage<'_>| var_usage.var_id)
88 .collect();
89
90 let with_coupon = self.coupon_input.is_some();
91 let mut inputs = self.inputs;
92 inputs.extend(self.coupon_input);
93 builder.push_statement(Statement::Call(StatementCall {
94 function: self.function,
95 inputs,
96 with_coupon,
97 outputs,
98 location: self.location,
99 is_specialization_base_call: false,
100 }));
101
102 CallResult { returns, extra_outputs }
103 }
104}
105pub struct CallResult<'db> {
107 pub returns: Vec<VarUsage<'db>>,
109 pub extra_outputs: Vec<VarUsage<'db>>,
111}
112
113pub struct EnumConstruct<'db> {
115 pub input: VarUsage<'db>,
116 pub variant: ConcreteVariant<'db>,
117 pub location: LocationId<'db>,
118}
119impl<'db> EnumConstruct<'db> {
120 pub fn add(
121 self,
122 ctx: &mut LoweringContext<'db, '_>,
123 builder: &mut StatementsBuilder<'db>,
124 ) -> VarUsage<'db> {
125 let ty = semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(
126 self.variant.concrete_enum_id,
127 ))
128 .intern(ctx.db);
129 let output = ctx.new_var(VarRequest { ty, location: self.location });
130 builder.push_statement(Statement::EnumConstruct(StatementEnumConstruct {
131 variant: self.variant,
132 input: self.input,
133 output,
134 }));
135 VarUsage { var_id: output, location: self.location }
136 }
137}
138
139pub struct Snapshot<'db> {
141 pub input: VarUsage<'db>,
142 pub location: LocationId<'db>,
143}
144impl<'db> Snapshot<'db> {
145 pub fn add(
146 self,
147 ctx: &mut LoweringContext<'db, '_>,
148 builder: &mut StatementsBuilder<'db>,
149 ) -> (VariableId, VariableId) {
150 let input_var = &ctx.variables[self.input.var_id];
151 let input_ty = input_var.ty;
152 let ty = semantic::TypeLongId::Snapshot(input_ty).intern(ctx.db);
153
154 let output_original =
156 ctx.new_var(VarRequest { ty: input_ty, location: input_var.location });
157 let output_snapshot = ctx.new_var(VarRequest { ty, location: self.location });
158 builder.push_statement(Statement::Snapshot(StatementSnapshot::new(
159 self.input,
160 output_original,
161 output_snapshot,
162 )));
163 (output_original, output_snapshot)
164 }
165}
166
167pub struct Desnap<'db> {
169 pub input: VarUsage<'db>,
170 pub location: LocationId<'db>,
171}
172impl<'db> Desnap<'db> {
173 pub fn add(
174 self,
175 ctx: &mut LoweringContext<'db, '_>,
176 builder: &mut StatementsBuilder<'db>,
177 ) -> VarUsage<'db> {
178 let ty = extract_matches!(
179 ctx.variables[self.input.var_id].ty.long(ctx.db),
180 semantic::TypeLongId::Snapshot
181 );
182 let output = ctx.new_var(VarRequest { ty: *ty, location: self.location });
183 builder.push_statement(Statement::Desnap(StatementDesnap { input: self.input, output }));
184 VarUsage { var_id: output, location: self.location }
185 }
186}
187
188pub struct StructDestructure<'db> {
193 pub input: VarUsage<'db>,
195 pub var_reqs: Vec<VarRequest<'db>>,
197}
198impl<'db> StructDestructure<'db> {
199 pub fn add(
200 self,
201 ctx: &mut LoweringContext<'db, '_>,
202 builder: &mut StatementsBuilder<'db>,
203 ) -> Vec<VariableId> {
204 let outputs: Vec<_> = self.var_reqs.into_iter().map(|req| ctx.new_var(req)).collect();
205 builder.push_statement(Statement::StructDestructure(StatementStructDestructure {
206 input: self.input,
207 outputs: outputs.clone(),
208 }));
209 outputs
210 }
211}
212
213pub struct StructMemberAccess<'db> {
215 pub input: VarUsage<'db>,
216 pub member_tys: Vec<semantic::TypeId<'db>>,
217 pub member_idx: usize,
218 pub location: LocationId<'db>,
219}
220impl<'db> StructMemberAccess<'db> {
221 pub fn add(
222 self,
223 ctx: &mut LoweringContext<'db, '_>,
224 builder: &mut StatementsBuilder<'db>,
225 ) -> VarUsage<'db> {
226 VarUsage {
227 var_id: StructDestructure {
228 input: self.input,
229 var_reqs: self
230 .member_tys
231 .into_iter()
232 .map(|ty| VarRequest { ty, location: self.location })
233 .collect(),
234 }
235 .add(ctx, builder)
236 .remove(self.member_idx),
237 location: self.location,
238 }
239 }
240}
241
242pub struct StructConstruct<'db> {
244 pub inputs: Vec<VarUsage<'db>>,
245 pub ty: semantic::TypeId<'db>,
246 pub location: LocationId<'db>,
247}
248impl<'db> StructConstruct<'db> {
249 pub fn add(
250 self,
251 ctx: &mut LoweringContext<'db, '_>,
252 builder: &mut StatementsBuilder<'db>,
253 ) -> VarUsage<'db> {
254 let output = ctx.new_var(VarRequest { ty: self.ty, location: self.location });
255 builder.push_statement(Statement::StructConstruct(StatementStructConstruct {
256 inputs: self.inputs,
257 output,
258 }));
259 VarUsage { var_id: output, location: self.location }
260 }
261}
262
263pub struct IntoBox<'db> {
265 pub input: VarUsage<'db>,
266 pub location: LocationId<'db>,
267}
268impl<'db> IntoBox<'db> {
269 pub fn add(
270 self,
271 ctx: &mut LoweringContext<'db, '_>,
272 builder: &mut StatementsBuilder<'db>,
273 ) -> VarUsage<'db> {
274 let input_ty = ctx.variables[self.input.var_id].ty;
275 let output_ty = core_box_ty(ctx.db, input_ty);
276 let output = ctx.new_var(VarRequest { ty: output_ty, location: self.location });
277 builder.push_statement(Statement::IntoBox(StatementIntoBox { input: self.input, output }));
278 VarUsage { var_id: output, location: self.location }
279 }
280}
281
282pub struct Unbox<'db> {
284 pub input: VarUsage<'db>,
285 pub location: LocationId<'db>,
286}
287impl<'db> Unbox<'db> {
288 pub fn add(
289 self,
290 ctx: &mut LoweringContext<'db, '_>,
291 builder: &mut StatementsBuilder<'db>,
292 ) -> VarUsage<'db> {
293 let box_ty = extract_matches!(
294 ctx.variables[self.input.var_id].ty.long(ctx.db),
295 semantic::TypeLongId::Concrete
296 );
297 let output_ty =
298 extract_matches!(box_ty.generic_args(ctx.db)[0], semantic::GenericArgumentId::Type);
299 let output = ctx.new_var(VarRequest { ty: output_ty, location: self.location });
300 builder.push_statement(Statement::Unbox(StatementUnbox { input: self.input, output }));
301 VarUsage { var_id: output, location: self.location }
302 }
303}