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::items::constant::ConstValueId;
7use cairo_lang_utils::{Intern, extract_matches};
8use itertools::chain;
9
10use super::VariableId;
11use super::context::VarRequest;
12use crate::ids::LocationId;
13use crate::lower::context::LoweringContext;
14use crate::objects::{
15    Statement, StatementCall, StatementConst, StatementStructConstruct, StatementStructDestructure,
16    VarUsage,
17};
18use crate::{StatementDesnap, StatementEnumConstruct, StatementSnapshot};
19
20#[derive(Clone, Default)]
21pub struct StatementsBuilder<'db> {
22    pub statements: Vec<Statement<'db>>,
23}
24impl<'db> StatementsBuilder<'db> {
25    /// Adds a statement to the block.
26    pub fn push_statement(&mut self, statement: Statement<'db>) {
27        self.statements.push(statement);
28    }
29}
30
31/// Generator for [StatementConst].
32pub struct Const<'db> {
33    pub value: ConstValueId<'db>,
34    pub location: LocationId<'db>,
35    // TODO(TomerStarkware): Remove this field and use the type from value.
36    pub ty: semantic::TypeId<'db>,
37}
38impl<'db> Const<'db> {
39    pub fn add(
40        self,
41        ctx: &mut LoweringContext<'db, '_>,
42        builder: &mut StatementsBuilder<'db>,
43    ) -> VarUsage<'db> {
44        let output = ctx.new_var(VarRequest { ty: self.ty, location: self.location });
45        builder.push_statement(Statement::Const(StatementConst::new_flat(self.value, output)));
46        VarUsage { var_id: output, location: self.location }
47    }
48}
49
50/// Generator for [StatementCall].
51/// Note that builder.finalize_statement() must be called manually after ref bindings.
52pub struct Call<'db> {
53    /// Called function.
54    pub function: crate::ids::FunctionId<'db>,
55    /// Inputs to function.
56    pub inputs: Vec<VarUsage<'db>>,
57    /// The `__coupon__` input to the function, if exists.
58    pub coupon_input: Option<VarUsage<'db>>,
59    /// Types for `ref` parameters of the function. An output variable will be introduced for each.
60    pub extra_ret_tys: Vec<semantic::TypeId<'db>>,
61    /// Types for the returns of the function. An output variable will be introduced for each.
62    pub ret_tys: Vec<semantic::TypeId<'db>>,
63    /// Location associated with this statement.
64    pub location: LocationId<'db>,
65}
66impl<'db> Call<'db> {
67    /// Adds a call statement to the builder.
68    pub fn add(
69        self,
70        ctx: &mut LoweringContext<'db, '_>,
71        builder: &mut StatementsBuilder<'db>,
72    ) -> CallResult<'db> {
73        let returns = self
74            .ret_tys
75            .into_iter()
76            .map(|ty| ctx.new_var_usage(VarRequest { ty, location: self.location }))
77            .collect();
78        let extra_outputs = self
79            .extra_ret_tys
80            .into_iter()
81            .map(|ty| ctx.new_var_usage(VarRequest { ty, location: self.location }))
82            .collect();
83        let outputs = chain!(&extra_outputs, &returns)
84            .map(|var_usage: &VarUsage<'_>| var_usage.var_id)
85            .collect();
86
87        let with_coupon = self.coupon_input.is_some();
88        let mut inputs = self.inputs;
89        inputs.extend(self.coupon_input);
90        builder.push_statement(Statement::Call(StatementCall {
91            function: self.function,
92            inputs,
93            with_coupon,
94            outputs,
95            location: self.location,
96            is_specialization_base_call: false,
97        }));
98
99        CallResult { returns, extra_outputs }
100    }
101}
102/// Result of adding a Call statement.
103pub struct CallResult<'db> {
104    /// Output variables for function's return value.
105    pub returns: Vec<VarUsage<'db>>,
106    /// Output variables for function's `ref` parameters.
107    pub extra_outputs: Vec<VarUsage<'db>>,
108}
109
110/// Generator for [StatementEnumConstruct].
111pub struct EnumConstruct<'db> {
112    pub input: VarUsage<'db>,
113    pub variant: ConcreteVariant<'db>,
114    pub location: LocationId<'db>,
115}
116impl<'db> EnumConstruct<'db> {
117    pub fn add(
118        self,
119        ctx: &mut LoweringContext<'db, '_>,
120        builder: &mut StatementsBuilder<'db>,
121    ) -> VarUsage<'db> {
122        let ty = semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(
123            self.variant.concrete_enum_id,
124        ))
125        .intern(ctx.db);
126        let output = ctx.new_var(VarRequest { ty, location: self.location });
127        builder.push_statement(Statement::EnumConstruct(StatementEnumConstruct {
128            variant: self.variant,
129            input: self.input,
130            output,
131        }));
132        VarUsage { var_id: output, location: self.location }
133    }
134}
135
136/// Generator for [StatementSnapshot].
137pub struct Snapshot<'db> {
138    pub input: VarUsage<'db>,
139    pub location: LocationId<'db>,
140}
141impl<'db> Snapshot<'db> {
142    pub fn add(
143        self,
144        ctx: &mut LoweringContext<'db, '_>,
145        builder: &mut StatementsBuilder<'db>,
146    ) -> (VariableId, VariableId) {
147        let input_var = &ctx.variables[self.input.var_id];
148        let input_ty = input_var.ty;
149        let ty = semantic::TypeLongId::Snapshot(input_ty).intern(ctx.db);
150
151        // The location of the original input var is likely to be more relevant to the user.
152        let output_original =
153            ctx.new_var(VarRequest { ty: input_ty, location: input_var.location });
154        let output_snapshot = ctx.new_var(VarRequest { ty, location: self.location });
155        builder.push_statement(Statement::Snapshot(StatementSnapshot::new(
156            self.input,
157            output_original,
158            output_snapshot,
159        )));
160        (output_original, output_snapshot)
161    }
162}
163
164/// Generator for [StatementDesnap].
165pub struct Desnap<'db> {
166    pub input: VarUsage<'db>,
167    pub location: LocationId<'db>,
168}
169impl<'db> Desnap<'db> {
170    pub fn add(
171        self,
172        ctx: &mut LoweringContext<'db, '_>,
173        builder: &mut StatementsBuilder<'db>,
174    ) -> VarUsage<'db> {
175        let ty = extract_matches!(
176            ctx.variables[self.input.var_id].ty.long(ctx.db),
177            semantic::TypeLongId::Snapshot
178        );
179        let output = ctx.new_var(VarRequest { ty: *ty, location: self.location });
180        builder.push_statement(Statement::Desnap(StatementDesnap { input: self.input, output }));
181        VarUsage { var_id: output, location: self.location }
182    }
183}
184
185/// Generator for [StatementStructDestructure].
186///
187/// Note that we return `Vec<VariableId>` rather than `Vec<VarUsage>` as the caller typically
188/// has a more accurate location than the one we have in the var requests.
189pub struct StructDestructure<'db> {
190    /// Variable that holds the struct value.
191    pub input: VarUsage<'db>,
192    /// Variable requests for the newly generated member values.
193    pub var_reqs: Vec<VarRequest<'db>>,
194}
195impl<'db> StructDestructure<'db> {
196    pub fn add(
197        self,
198        ctx: &mut LoweringContext<'db, '_>,
199        builder: &mut StatementsBuilder<'db>,
200    ) -> Vec<VariableId> {
201        let outputs: Vec<_> = self.var_reqs.into_iter().map(|req| ctx.new_var(req)).collect();
202        builder.push_statement(Statement::StructDestructure(StatementStructDestructure {
203            input: self.input,
204            outputs: outputs.clone(),
205        }));
206        outputs
207    }
208}
209
210/// Generator for [StatementStructDestructure] as member access.
211pub struct StructMemberAccess<'db> {
212    pub input: VarUsage<'db>,
213    pub member_tys: Vec<semantic::TypeId<'db>>,
214    pub member_idx: usize,
215    pub location: LocationId<'db>,
216}
217impl<'db> StructMemberAccess<'db> {
218    pub fn add(
219        self,
220        ctx: &mut LoweringContext<'db, '_>,
221        builder: &mut StatementsBuilder<'db>,
222    ) -> VarUsage<'db> {
223        VarUsage {
224            var_id: StructDestructure {
225                input: self.input,
226                var_reqs: self
227                    .member_tys
228                    .into_iter()
229                    .map(|ty| VarRequest { ty, location: self.location })
230                    .collect(),
231            }
232            .add(ctx, builder)
233            .remove(self.member_idx),
234            location: self.location,
235        }
236    }
237}
238
239/// Generator for [StatementStructConstruct].
240pub struct StructConstruct<'db> {
241    pub inputs: Vec<VarUsage<'db>>,
242    pub ty: semantic::TypeId<'db>,
243    pub location: LocationId<'db>,
244}
245impl<'db> StructConstruct<'db> {
246    pub fn add(
247        self,
248        ctx: &mut LoweringContext<'db, '_>,
249        builder: &mut StatementsBuilder<'db>,
250    ) -> VarUsage<'db> {
251        let output = ctx.new_var(VarRequest { ty: self.ty, location: self.location });
252        builder.push_statement(Statement::StructConstruct(StatementStructConstruct {
253            inputs: self.inputs,
254            output,
255        }));
256        VarUsage { var_id: output, location: self.location }
257    }
258}