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        }));
97
98        CallResult { returns, extra_outputs }
99    }
100}
101/// Result of adding a Call statement.
102pub struct CallResult<'db> {
103    /// Output variables for function's return value.
104    pub returns: Vec<VarUsage<'db>>,
105    /// Output variables for function's `ref` parameters.
106    pub extra_outputs: Vec<VarUsage<'db>>,
107}
108
109/// Generator for [StatementEnumConstruct].
110pub struct EnumConstruct<'db> {
111    pub input: VarUsage<'db>,
112    pub variant: ConcreteVariant<'db>,
113    pub location: LocationId<'db>,
114}
115impl<'db> EnumConstruct<'db> {
116    pub fn add(
117        self,
118        ctx: &mut LoweringContext<'db, '_>,
119        builder: &mut StatementsBuilder<'db>,
120    ) -> VarUsage<'db> {
121        let ty = semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(
122            self.variant.concrete_enum_id,
123        ))
124        .intern(ctx.db);
125        let output = ctx.new_var(VarRequest { ty, location: self.location });
126        builder.push_statement(Statement::EnumConstruct(StatementEnumConstruct {
127            variant: self.variant,
128            input: self.input,
129            output,
130        }));
131        VarUsage { var_id: output, location: self.location }
132    }
133}
134
135/// Generator for [StatementSnapshot].
136pub struct Snapshot<'db> {
137    pub input: VarUsage<'db>,
138    pub location: LocationId<'db>,
139}
140impl<'db> Snapshot<'db> {
141    pub fn add(
142        self,
143        ctx: &mut LoweringContext<'db, '_>,
144        builder: &mut StatementsBuilder<'db>,
145    ) -> (VariableId, VariableId) {
146        let input_var = &ctx.variables[self.input.var_id];
147        let input_ty = input_var.ty;
148        let ty = semantic::TypeLongId::Snapshot(input_ty).intern(ctx.db);
149
150        // The location of the original input var is likely to be more relevant to the user.
151        let output_original =
152            ctx.new_var(VarRequest { ty: input_ty, location: input_var.location });
153        let output_snapshot = ctx.new_var(VarRequest { ty, location: self.location });
154        builder.push_statement(Statement::Snapshot(StatementSnapshot::new(
155            self.input,
156            output_original,
157            output_snapshot,
158        )));
159        (output_original, output_snapshot)
160    }
161}
162
163/// Generator for [StatementDesnap].
164pub struct Desnap<'db> {
165    pub input: VarUsage<'db>,
166    pub location: LocationId<'db>,
167}
168impl<'db> Desnap<'db> {
169    pub fn add(
170        self,
171        ctx: &mut LoweringContext<'db, '_>,
172        builder: &mut StatementsBuilder<'db>,
173    ) -> VarUsage<'db> {
174        let ty = extract_matches!(
175            ctx.variables[self.input.var_id].ty.long(ctx.db),
176            semantic::TypeLongId::Snapshot
177        );
178        let output = ctx.new_var(VarRequest { ty: *ty, location: self.location });
179        builder.push_statement(Statement::Desnap(StatementDesnap { input: self.input, output }));
180        VarUsage { var_id: output, location: self.location }
181    }
182}
183
184/// Generator for [StatementStructDestructure].
185///
186/// Note that we return `Vec<VariableId>` rather than `Vec<VarUsage>` as the caller typically
187/// has a more accurate location than the one we have in the var requests.
188pub struct StructDestructure<'db> {
189    /// Variable that holds the struct value.
190    pub input: VarUsage<'db>,
191    /// Variable requests for the newly generated member values.
192    pub var_reqs: Vec<VarRequest<'db>>,
193}
194impl<'db> StructDestructure<'db> {
195    pub fn add(
196        self,
197        ctx: &mut LoweringContext<'db, '_>,
198        builder: &mut StatementsBuilder<'db>,
199    ) -> Vec<VariableId> {
200        let outputs: Vec<_> = self.var_reqs.into_iter().map(|req| ctx.new_var(req)).collect();
201        builder.push_statement(Statement::StructDestructure(StatementStructDestructure {
202            input: self.input,
203            outputs: outputs.clone(),
204        }));
205        outputs
206    }
207}
208
209/// Generator for [StatementStructDestructure] as member access.
210pub struct StructMemberAccess<'db> {
211    pub input: VarUsage<'db>,
212    pub member_tys: Vec<semantic::TypeId<'db>>,
213    pub member_idx: usize,
214    pub location: LocationId<'db>,
215}
216impl<'db> StructMemberAccess<'db> {
217    pub fn add(
218        self,
219        ctx: &mut LoweringContext<'db, '_>,
220        builder: &mut StatementsBuilder<'db>,
221    ) -> VarUsage<'db> {
222        VarUsage {
223            var_id: StructDestructure {
224                input: self.input,
225                var_reqs: self
226                    .member_tys
227                    .into_iter()
228                    .map(|ty| VarRequest { ty, location: self.location })
229                    .collect(),
230            }
231            .add(ctx, builder)
232            .remove(self.member_idx),
233            location: self.location,
234        }
235    }
236}
237
238/// Generator for [StatementStructConstruct].
239pub struct StructConstruct<'db> {
240    pub inputs: Vec<VarUsage<'db>>,
241    pub ty: semantic::TypeId<'db>,
242    pub location: LocationId<'db>,
243}
244impl<'db> StructConstruct<'db> {
245    pub fn add(
246        self,
247        ctx: &mut LoweringContext<'db, '_>,
248        builder: &mut StatementsBuilder<'db>,
249    ) -> VarUsage<'db> {
250        let output = ctx.new_var(VarRequest { ty: self.ty, location: self.location });
251        builder.push_statement(Statement::StructConstruct(StatementStructConstruct {
252            inputs: self.inputs,
253            output,
254        }));
255        VarUsage { var_id: output, location: self.location }
256    }
257}