leo_passes/static_single_assignment/
statement.rs1use super::SsaFormingVisitor;
18use crate::RenameTable;
19
20use leo_ast::{
21 AssertStatement,
22 AssertVariant,
23 AssignStatement,
24 Block,
25 ConditionalStatement,
26 ConstDeclaration,
27 DefinitionPlace,
28 DefinitionStatement,
29 Expression,
30 ExpressionConsumer,
31 ExpressionStatement,
32 Identifier,
33 IterationStatement,
34 Node,
35 ReturnStatement,
36 Statement,
37 StatementConsumer,
38 TernaryExpression,
39};
40use leo_span::Symbol;
41
42use indexmap::IndexSet;
43
44impl StatementConsumer for SsaFormingVisitor<'_> {
45 type Output = Vec<Statement>;
46
47 fn consume_assert(&mut self, input: AssertStatement) -> Self::Output {
49 let (variant, mut statements) = match input.variant {
50 AssertVariant::Assert(expr) => {
51 let (expr, statements) = self.consume_expression_and_define(expr);
52 (AssertVariant::Assert(expr), statements)
53 }
54 AssertVariant::AssertEq(left, right) => {
55 let (left, mut statements) = self.consume_expression_and_define(left);
57 let (right, right_statements) = self.consume_expression_and_define(right);
59 statements.extend(right_statements);
61
62 (AssertVariant::AssertEq(left, right), statements)
63 }
64 AssertVariant::AssertNeq(left, right) => {
65 let (left, mut statements) = self.consume_expression_and_define(left);
67 let (right, right_statements) = self.consume_expression_and_define(right);
69 statements.extend(right_statements);
71
72 (AssertVariant::AssertNeq(left, right), statements)
73 }
74 };
75
76 statements.push(AssertStatement { variant, ..input }.into());
78
79 statements
80 }
81
82 fn consume_assign(&mut self, mut assign: AssignStatement) -> Self::Output {
84 let (value, mut statements) = self.consume_expression(assign.value);
85 if let Expression::Path(path) = assign.place {
86 let new_place = self.rename_identifier(path.identifier());
90
91 statements.push(self.simple_definition(new_place, value));
92 statements
93 } else {
94 let mut place = &mut assign.place;
99 loop {
100 match place {
101 Expression::ArrayAccess(array_access) => place = &mut array_access.array,
102 Expression::MemberAccess(member_access) => place = &mut member_access.inner,
103 Expression::TupleAccess(tuple_access) => place = &mut tuple_access.tuple,
104 expr @ Expression::Path(..) => {
105 let (new_expr, statements2) = self.consume_expression(std::mem::take(expr));
106 *expr = new_expr;
107 statements.extend(statements2);
108 assign.value = value;
109 statements.push(assign.into());
110 return statements;
111 }
112 _ => panic!("Type checking should have ensured this is not possible."),
113 }
114 }
115 }
116 }
117
118 fn consume_block(&mut self, block: Block) -> Self::Output {
120 block.statements.into_iter().flat_map(|statement| self.consume_statement(statement)).collect()
121 }
122
123 fn consume_conditional(&mut self, conditional: ConditionalStatement) -> Self::Output {
131 let (condition, mut statements) = self.consume_expression_and_define(conditional.condition);
133
134 self.push();
136
137 let then = Block {
139 span: conditional.then.span,
140 id: conditional.then.id,
141 statements: self.consume_block(conditional.then),
142 };
143
144 let if_table = self.pop();
146
147 self.push();
149
150 let otherwise = conditional.otherwise.map(|otherwise| Box::new(Statement::Block(match *otherwise {
152 Statement::Block(block) => Block {
153 span: block.span,
154 id: block.id,
155 statements: self.consume_block(block),
156 },
157 Statement::Conditional(conditional) => Block {
158 span: conditional.span,
159 id: conditional.id,
160 statements: self.consume_conditional(conditional),
161 },
162 _ => panic!("Type checking guarantees that the otherwise-block of a conditional statement is a block or another conditional statement."),
163 })));
164
165 let else_table = self.pop();
167
168 statements.push(ConditionalStatement { condition: condition.clone(), then, otherwise, ..conditional }.into());
170
171 let if_write_set: IndexSet<&Symbol> = IndexSet::from_iter(if_table.local_names());
173 let else_write_set: IndexSet<&Symbol> = IndexSet::from_iter(else_table.local_names());
174 let write_set = if_write_set.union(&else_write_set);
175
176 for symbol in write_set {
178 if self.rename_table.lookup(**symbol).is_some() {
180 let create_phi_argument = |table: &RenameTable, symbol: Symbol| -> Expression {
182 let name =
183 *table.lookup(symbol).unwrap_or_else(|| panic!("Symbol {symbol} should exist in the program."));
184 let id = *table
185 .lookup_id(&name)
186 .unwrap_or_else(|| panic!("Symbol {name} should exist in the rename table."));
187 Identifier { name, span: Default::default(), id }.into()
188 };
189
190 let new_name = self.state.assigner.unique_symbol(symbol, "$");
192
193 let if_true = create_phi_argument(&if_table, **symbol);
195 let if_false = create_phi_argument(&else_table, **symbol);
196
197 let id = self.state.node_builder.next_id();
199 let Some(type_) = self.state.type_table.get(&if_true.id()) else {
201 panic!("Type checking guarantees that all expressions have a type.");
202 };
203 self.state.type_table.insert(id, type_);
204
205 let (value, stmts) = self.consume_ternary(TernaryExpression {
207 condition: condition.clone(),
208 if_true,
209 if_false,
210 span: Default::default(),
211 id,
212 });
213
214 statements.extend(stmts);
215
216 let id = *self.rename_table.lookup_id(symbol).unwrap_or_else(|| {
218 panic!("The ID for the symbol `{symbol}` should already exist in the rename table.")
219 });
220
221 self.rename_table.update(**symbol, new_name, id);
223
224 let identifier = Identifier { name: new_name, span: Default::default(), id };
226 let assignment = self.simple_definition(identifier, value);
227
228 statements.push(assignment);
230 }
231 }
232
233 statements
234 }
235
236 fn consume_const(&mut self, _: ConstDeclaration) -> Self::Output {
237 Default::default()
239 }
240
241 fn consume_definition(&mut self, definition: DefinitionStatement) -> Self::Output {
243 match definition.place {
244 DefinitionPlace::Single(identifier) => {
245 let (value, mut rhs_statements) = self.consume_expression(definition.value);
247
248 let new_identifier = if self.rename_defs { self.rename_identifier(identifier) } else { identifier };
250
251 rhs_statements.push(self.simple_definition(new_identifier, value));
253 rhs_statements
254 }
255 DefinitionPlace::Multiple(identifiers) => {
256 let new_identifiers = if self.rename_defs {
258 identifiers.into_iter().map(|id| self.rename_identifier(id)).collect()
259 } else {
260 identifiers
261 };
262
263 let place = DefinitionPlace::Multiple(new_identifiers);
268
269 let (value, mut rhs_statements) = self.consume_expression(definition.value);
270
271 let def_stmt = DefinitionStatement { place, type_: None, value, ..definition }.into();
273 rhs_statements.push(def_stmt);
274 rhs_statements
275 }
276 }
277 }
278
279 fn consume_expression_statement(&mut self, mut input: ExpressionStatement) -> Self::Output {
281 let (expr, mut statements) = self.consume_expression(input.expression);
282 input.expression = expr;
283 statements.push(input.into());
284 statements
285 }
286
287 fn consume_iteration(&mut self, _input: IterationStatement) -> Self::Output {
288 panic!("`IterationStatement`s should not be in the AST at this phase of compilation.");
289 }
290
291 fn consume_return(&mut self, mut input: ReturnStatement) -> Self::Output {
294 if let Expression::Tuple(tuple_expr) = &mut input.expression {
295 let mut statements = Vec::new();
297 for element in tuple_expr.elements.iter_mut() {
298 let (new_element, new_statements) = self.consume_expression_and_define(std::mem::take(element));
299 *element = new_element;
300 statements.extend(new_statements);
301 }
302 statements.push(input.into());
303 statements
304 } else {
305 let (expression, mut statements) = self.consume_expression_and_define(input.expression);
306 input.expression = expression;
307 statements.push(input.into());
308 statements
309 }
310 }
311}