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