leo_passes/code_generation/
statement.rs1use super::*;
18
19use leo_ast::{
20 AssertStatement,
21 AssertVariant,
22 AssignStatement,
23 Block,
24 ConditionalStatement,
25 DefinitionPlace,
26 DefinitionStatement,
27 Expression,
28 ExpressionStatement,
29 IterationStatement,
30 Mode,
31 Output,
32 ReturnStatement,
33 Statement,
34 Type,
35};
36
37use indexmap::IndexMap;
38
39impl CodeGeneratingVisitor<'_> {
40 fn visit_statement(&mut self, input: &Statement) -> Vec<AleoStmt> {
41 match input {
42 Statement::Assert(stmt) => self.visit_assert(stmt),
43 Statement::Assign(stmt) => vec![self.visit_assign(stmt)],
44 Statement::Block(stmt) => self.visit_block(stmt),
45 Statement::Conditional(stmt) => self.visit_conditional(stmt),
46 Statement::Const(_) => {
47 panic!("`ConstStatement`s should not be in the AST at this phase of compilation.")
48 }
49 Statement::Definition(stmt) => self.visit_definition(stmt),
50 Statement::Expression(stmt) => self.visit_expression_statement(stmt),
51 Statement::Iteration(stmt) => vec![self.visit_iteration(stmt)],
52 Statement::Return(stmt) => self.visit_return(stmt),
53 }
54 }
55
56 fn visit_assert(&mut self, input: &AssertStatement) -> Vec<AleoStmt> {
57 match &input.variant {
58 AssertVariant::Assert(expr) => {
59 let (operand, mut instructions) = self.visit_expression(expr);
60 let operand = operand.expect("Trying to assert an empty expression.");
61 instructions.push(AleoStmt::AssertEq(operand, AleoExpr::Bool(true)));
62 instructions
63 }
64 AssertVariant::AssertEq(left, right) => {
65 let (left, left_stmts) = self.visit_expression(left);
66 let (right, right_stmts) = self.visit_expression(right);
67 let left = left.expect("Trying to assert an empty expression.");
68 let right = right.expect("Trying to assert an empty expression.");
69 let assert_instruction = AleoStmt::AssertEq(left, right);
70
71 let mut instructions = left_stmts;
73 instructions.extend(right_stmts);
74 instructions.push(assert_instruction);
75 instructions
76 }
77 AssertVariant::AssertNeq(left, right) => {
78 let (left, left_stmts) = self.visit_expression(left);
79 let (right, right_stmts) = self.visit_expression(right);
80 let left = left.expect("Trying to assert an empty expression.");
81 let right = right.expect("Trying to assert an empty expression.");
82 let assert_instruction = AleoStmt::AssertNeq(left, right);
83
84 let mut instructions = left_stmts;
86 instructions.extend(right_stmts);
87 instructions.push(assert_instruction);
88 instructions
89 }
90 }
91 }
92
93 fn visit_return(&mut self, input: &ReturnStatement) -> Vec<AleoStmt> {
94 let mut instructions = vec![];
95 let mut operands: IndexMap<AleoExpr, &Output> =
96 IndexMap::with_capacity(self.current_function.unwrap().output.len());
97
98 if let Expression::Tuple(tuple) = &input.expression {
99 let outputs = &self.current_function.unwrap().output;
102 assert_eq!(tuple.elements.len(), outputs.len());
103
104 for (expr, output) in tuple.elements.iter().zip_eq(outputs) {
105 let (operand, op_instructions) = self.visit_expression(expr);
106 instructions.extend(op_instructions);
107 if let Some(operand) = operand {
108 if self.internal_record_inputs.contains(&operand) || operands.contains_key(&operand) {
109 let (new_operand, new_instr) = self.clone_register(&operand, &output.type_);
113 instructions.extend(new_instr);
114 operands.insert(new_operand, output);
115 } else {
116 operands.insert(operand, output);
117 }
118 }
119 }
120 } else {
121 let (operand, op_instructions) = self.visit_expression(&input.expression);
123 if let Some(operand) = operand {
124 let output = &self.current_function.unwrap().output[0];
125
126 if self.internal_record_inputs.contains(&operand) {
127 let (new_operand, new_instr) =
129 self.clone_register(&operand, &self.current_function.unwrap().output_type);
130 instructions.extend(new_instr);
131 operands.insert(new_operand, output);
132 } else {
133 instructions = op_instructions;
134 operands.insert(operand, output);
135 }
136 }
137 }
138
139 for (operand, output) in operands.iter() {
140 let visibility = match (self.variant.unwrap().is_transition(), output.mode) {
142 (true, Mode::None) => Some(AleoVisibility::Private),
143 (_, mode) => AleoVisibility::maybe_from(mode),
144 };
145 if let Type::Future(_) = output.type_ {
146 instructions.push(AleoStmt::Output(
147 operand.clone(),
148 AleoType::Future {
149 name: self.current_function.unwrap().identifier.to_string(),
150 program: self.program_id.unwrap().name.to_string(),
151 },
152 None,
153 ));
154 } else if output.type_.is_empty() {
155 } else {
157 let (output_type, output_viz) = self.visit_type_with_visibility(&output.type_, visibility);
158 instructions.push(AleoStmt::Output(operand.clone(), output_type, output_viz));
159 }
160 }
161
162 instructions
163 }
164
165 fn visit_definition(&mut self, input: &DefinitionStatement) -> Vec<AleoStmt> {
166 match (&input.place, &input.value) {
167 (DefinitionPlace::Single(identifier), _) => {
168 let (operand, expression_instructions) = self.visit_expression(&input.value);
169 if let Some(operand) = operand {
170 self.variable_mapping.insert(identifier.name, operand);
171 }
172 expression_instructions
173 }
174 (DefinitionPlace::Multiple(identifiers), Expression::Call(_)) => {
175 let (operand, expression_instructions) = self.visit_expression(&input.value);
176 let Some(AleoExpr::Tuple(elems)) = operand else {
177 panic!("Definition with multiple identifiers should yield a tuple")
178 };
179 for (identifier, operand) in identifiers.iter().zip_eq(elems.iter()) {
181 self.variable_mapping.insert(identifier.name, operand.clone());
182 }
183 expression_instructions
184 }
185 _ => panic!("Previous passes should have ensured that a definition with multiple identifiers is a `Call`."),
186 }
187 }
188
189 fn visit_expression_statement(&mut self, input: &ExpressionStatement) -> Vec<AleoStmt> {
190 self.visit_expression(&input.expression).1
191 }
192
193 fn visit_assign(&mut self, _input: &AssignStatement) -> AleoStmt {
194 panic!("AssignStatement's should not exist in SSA form.")
195 }
196
197 fn visit_conditional(&mut self, _input: &ConditionalStatement) -> Vec<AleoStmt> {
198 if !self.variant.unwrap().is_async_function() {
200 panic!("`ConditionalStatement`s should not be in the AST at this phase of compilation.")
201 } else {
202 let end_then_label = format!("end_then_{}_{}", self.conditional_depth, self.next_label);
204 self.next_label += 1;
205 let (has_otherwise, end_otherwise_label) = {
207 match _input.otherwise.is_some() {
208 true => {
209 let end_otherwise_label =
211 { format!("end_otherwise_{}_{}", self.conditional_depth, self.next_label) };
212 self.next_label += 1;
213 (true, end_otherwise_label)
214 }
215 false => (false, String::new()),
216 }
217 };
218
219 self.conditional_depth += 1;
221
222 let (condition, mut instructions) = self.visit_expression(&_input.condition);
224 let condition = condition.expect("Trying to branch on an empty expression");
225 instructions.push(AleoStmt::BranchEq(condition, AleoExpr::Bool(false), end_then_label.clone()));
226
227 instructions.extend(self.visit_block(&_input.then));
229 if has_otherwise {
231 instructions.push(AleoStmt::BranchEq(
232 AleoExpr::Bool(true),
233 AleoExpr::Bool(true),
234 end_otherwise_label.clone(),
235 ));
236 }
237
238 instructions.push(AleoStmt::Position(end_then_label));
240
241 if let Some(else_block) = &_input.otherwise {
243 instructions.extend(self.visit_statement(else_block));
245 instructions.push(AleoStmt::Position(end_otherwise_label));
247 }
248
249 self.conditional_depth -= 1;
251
252 instructions
253 }
254 }
255
256 fn visit_iteration(&mut self, _input: &IterationStatement) -> AleoStmt {
257 panic!("`IterationStatement`s should not be in the AST at this phase of compilation.");
258 }
259
260 pub(crate) fn visit_block(&mut self, input: &Block) -> Vec<AleoStmt> {
261 input.statements.iter().flat_map(|stmt| self.visit_statement(stmt)).collect()
263 }
264}