Skip to main content

cairo_lang_lowering/lower/
generators.rs

1//! Statement generators. Add statements to BlockBuilder while respecting variable liveness and
2//! ownership of OwnedVariable.
3
4use 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    /// Adds a statement to the block.
29    pub fn push_statement(&mut self, statement: Statement<'db>) {
30        self.statements.push(statement);
31    }
32}
33
34/// Generator for [StatementConst].
35pub struct Const<'db> {
36    pub value: ConstValueId<'db>,
37    pub location: LocationId<'db>,
38    // TODO(TomerStarkware): Remove this field and use the type from value.
39    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
53/// Generator for [StatementCall].
54/// Note that builder.finalize_statement() must be called manually after ref bindings.
55pub struct Call<'db> {
56    /// Called function.
57    pub function: crate::ids::FunctionId<'db>,
58    /// Inputs to function.
59    pub inputs: Vec<VarUsage<'db>>,
60    /// The `__coupon__` input to the function, if exists.
61    pub coupon_input: Option<VarUsage<'db>>,
62    /// Types for `ref` parameters of the function. An output variable will be introduced for each.
63    pub extra_ret_tys: Vec<semantic::TypeId<'db>>,
64    /// Types for the returns of the function. An output variable will be introduced for each.
65    pub ret_tys: Vec<semantic::TypeId<'db>>,
66    /// Location associated with this statement.
67    pub location: LocationId<'db>,
68}
69impl<'db> Call<'db> {
70    /// Adds a call statement to the builder.
71    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}
105/// Result of adding a Call statement.
106pub struct CallResult<'db> {
107    /// Output variables for function's return value.
108    pub returns: Vec<VarUsage<'db>>,
109    /// Output variables for function's `ref` parameters.
110    pub extra_outputs: Vec<VarUsage<'db>>,
111}
112
113/// Generator for [StatementEnumConstruct].
114pub 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
139/// Generator for [StatementSnapshot].
140pub 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        // The location of the original input var is likely to be more relevant to the user.
155        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
167/// Generator for [StatementDesnap].
168pub 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
188/// Generator for [StatementStructDestructure].
189///
190/// Note that we return `Vec<VariableId>` rather than `Vec<VarUsage>` as the caller typically
191/// has a more accurate location than the one we have in the var requests.
192pub struct StructDestructure<'db> {
193    /// Variable that holds the struct value.
194    pub input: VarUsage<'db>,
195    /// Variable requests for the newly generated member values.
196    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
213/// Generator for [StatementStructDestructure] as member access.
214pub 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
242/// Generator for [StatementStructConstruct].
243pub 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
263/// Generator for [StatementIntoBox].
264pub 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
282/// Generator for [StatementUnbox].
283pub 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}