statement.rs
1 // Copyright (C) 2019-2025 ADnet Contributors 2 // This file is part of the ADL library. 3 4 // The ADL library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 9 // The ADL library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 14 // You should have received a copy of the GNU General Public License 15 // along with the ADL library. If not, see <https://www.gnu.org/licenses/>. 16 17 use super::*; 18 19 use adl_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 37 use indexmap::IndexMap; 38 39 impl 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 // Concatenate the instructions. 72 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 // Concatenate the instructions. 85 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 // Now tuples only appear in return position, so let's handle this 100 // ourselves. 101 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 // We can't output an internal record we received as input. 110 // We also can't output the same value twice. 111 // Either way, clone it. 112 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 // Not a tuple - only one output. 122 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 // We can't output an internal record we received as input. 128 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 // Transitions outputs with no mode are private. 141 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 suffix: self.state.target.suffix().to_string(), 152 }, 153 None, 154 )); 155 } else if output.type_.is_empty() { 156 // do nothing 157 } else { 158 let (output_type, output_viz) = self.visit_type_with_visibility(&output.type_, visibility); 159 instructions.push(AleoStmt::Output(operand.clone(), output_type, output_viz)); 160 } 161 } 162 163 instructions 164 } 165 166 fn visit_definition(&mut self, input: &DefinitionStatement) -> Vec<AleoStmt> { 167 match (&input.place, &input.value) { 168 (DefinitionPlace::Single(identifier), _) => { 169 let (operand, expression_instructions) = self.visit_expression(&input.value); 170 if let Some(operand) = operand { 171 self.variable_mapping.insert(identifier.name, operand); 172 } 173 expression_instructions 174 } 175 (DefinitionPlace::Multiple(identifiers), Expression::Call(_)) => { 176 let (operand, expression_instructions) = self.visit_expression(&input.value); 177 let Some(AleoExpr::Tuple(elems)) = operand else { 178 panic!("Definition with multiple identifiers should yield a tuple") 179 }; 180 // Add the destinations to the variable mapping. 181 for (identifier, operand) in identifiers.iter().zip_eq(elems.iter()) { 182 self.variable_mapping.insert(identifier.name, operand.clone()); 183 } 184 expression_instructions 185 } 186 _ => panic!("Previous passes should have ensured that a definition with multiple identifiers is a `Call`."), 187 } 188 } 189 190 fn visit_expression_statement(&mut self, input: &ExpressionStatement) -> Vec<AleoStmt> { 191 self.visit_expression(&input.expression).1 192 } 193 194 fn visit_assign(&mut self, _input: &AssignStatement) -> AleoStmt { 195 panic!("AssignStatement's should not exist in SSA form.") 196 } 197 198 fn visit_conditional(&mut self, _input: &ConditionalStatement) -> Vec<AleoStmt> { 199 // Note that this unwrap is safe because we set the variant before traversing the function. 200 if !self.variant.unwrap().is_async_function() { 201 panic!("`ConditionalStatement`s should not be in the AST at this phase of compilation.") 202 } else { 203 // Construct a label for the end of the `then` block. 204 let end_then_label = format!("end_then_{}_{}", self.conditional_depth, self.next_label); 205 self.next_label += 1; 206 // Construct a label for the end of the `otherwise` block if it exists. 207 let (has_otherwise, end_otherwise_label) = { 208 match _input.otherwise.is_some() { 209 true => { 210 // Construct a label for the end of the `otherwise` block. 211 let end_otherwise_label = 212 { format!("end_otherwise_{}_{}", self.conditional_depth, self.next_label) }; 213 self.next_label += 1; 214 (true, end_otherwise_label) 215 } 216 false => (false, String::new()), 217 } 218 }; 219 220 // Increment the conditional depth. 221 self.conditional_depth += 1; 222 223 // Create a `branch` instruction. 224 let (condition, mut instructions) = self.visit_expression(&_input.condition); 225 let condition = condition.expect("Trying to branch on an empty expression"); 226 instructions.push(AleoStmt::BranchEq(condition, AleoExpr::Bool(false), end_then_label.clone())); 227 228 // Visit the `then` block. 229 instructions.extend(self.visit_block(&_input.then)); 230 // If the `otherwise` block is present, add a branch instruction to jump to the end of the `otherwise` block. 231 if has_otherwise { 232 instructions.push(AleoStmt::BranchEq( 233 AleoExpr::Bool(true), 234 AleoExpr::Bool(true), 235 end_otherwise_label.clone(), 236 )); 237 } 238 239 // Add a label for the end of the `then` block. 240 instructions.push(AleoStmt::Position(end_then_label)); 241 242 // Visit the `otherwise` block. 243 if let Some(else_block) = &_input.otherwise { 244 // Visit the `otherwise` block. 245 instructions.extend(self.visit_statement(else_block)); 246 // Add a label for the end of the `otherwise` block. 247 instructions.push(AleoStmt::Position(end_otherwise_label)); 248 } 249 250 // Decrement the conditional depth. 251 self.conditional_depth -= 1; 252 253 instructions 254 } 255 } 256 257 fn visit_iteration(&mut self, _input: &IterationStatement) -> AleoStmt { 258 panic!("`IterationStatement`s should not be in the AST at this phase of compilation."); 259 } 260 261 pub(crate) fn visit_block(&mut self, input: &Block) -> Vec<AleoStmt> { 262 // For each statement in the block, visit it and add its instructions to the list. 263 input.statements.iter().flat_map(|stmt| self.visit_statement(stmt)).collect() 264 } 265 }