leo_passes/code_generation/
expression.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo 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 Leo 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 Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::*;
18
19use leo_ast::{
20    ArrayAccess,
21    ArrayExpression,
22    AssociatedConstantExpression,
23    AssociatedFunctionExpression,
24    AsyncExpression,
25    BinaryExpression,
26    BinaryOperation,
27    CallExpression,
28    CastExpression,
29    ErrExpression,
30    Expression,
31    Identifier,
32    Literal,
33    LiteralVariant,
34    Location,
35    LocatorExpression,
36    MemberAccess,
37    Node,
38    Path,
39    ProgramId,
40    RepeatExpression,
41    StructExpression,
42    TernaryExpression,
43    TupleExpression,
44    Type,
45    UnaryExpression,
46    UnaryOperation,
47    UnitExpression,
48    Variant,
49};
50use leo_span::sym;
51
52use std::{borrow::Borrow, fmt::Write as _};
53
54/// Implement the necessary methods to visit nodes in the AST.
55impl CodeGeneratingVisitor<'_> {
56    pub fn visit_expression(&mut self, input: &Expression) -> (String, String) {
57        match input {
58            Expression::Array(expr) => self.visit_array(expr),
59            Expression::ArrayAccess(expr) => self.visit_array_access(expr),
60            Expression::AssociatedConstant(expr) => self.visit_associated_constant(expr),
61            Expression::AssociatedFunction(expr) => self.visit_associated_function(expr),
62            Expression::Async(expr) => self.visit_async(expr),
63            Expression::Binary(expr) => self.visit_binary(expr),
64            Expression::Call(expr) => self.visit_call(expr),
65            Expression::Cast(expr) => self.visit_cast(expr),
66            Expression::Struct(expr) => self.visit_struct_init(expr),
67            Expression::Err(expr) => self.visit_err(expr),
68            Expression::Path(expr) => self.visit_path(expr),
69            Expression::Literal(expr) => self.visit_value(expr),
70            Expression::Locator(expr) => self.visit_locator(expr),
71            Expression::MemberAccess(expr) => self.visit_member_access(expr),
72            Expression::Repeat(expr) => self.visit_repeat(expr),
73            Expression::Ternary(expr) => self.visit_ternary(expr),
74            Expression::Tuple(expr) => self.visit_tuple(expr),
75            Expression::TupleAccess(_) => panic!("Tuple accesses should not appear in the AST at this point."),
76            Expression::Unary(expr) => self.visit_unary(expr),
77            Expression::Unit(expr) => self.visit_unit(expr),
78        }
79    }
80
81    fn visit_path(&mut self, input: &Path) -> (String, String) {
82        // The only relevant paths here are paths to local variable or to mappings, so we really only care about their
83        // names since mappings are only allowed in the top level program scope
84        let var_name = input.identifier().name;
85        (
86            self.variable_mapping.get(&var_name).or_else(|| self.global_mapping.get(&var_name)).unwrap().clone(),
87            String::new(),
88        )
89    }
90
91    fn visit_err(&mut self, _input: &ErrExpression) -> (String, String) {
92        panic!("`ErrExpression`s should not be in the AST at this phase of compilation.")
93    }
94
95    fn visit_value(&mut self, input: &Literal) -> (String, String) {
96        // AVM can only parse decimal numbers.
97        let literal = if let LiteralVariant::Unsuffixed(value) = &input.variant {
98            // For unsuffixed lierals, consult the `type_table` for their types. The type checker
99            // ensures that their type can only be `Integer`, `Field`, `Group`, or `Scalar`.
100            match self.state.type_table.get(&input.id) {
101                Some(Type::Integer(int_ty)) => Literal {
102                    variant: LiteralVariant::Integer(int_ty, value.clone()),
103                    id: self.state.node_builder.next_id(),
104                    span: input.span,
105                },
106                Some(Type::Field) => Literal {
107                    variant: LiteralVariant::Field(value.clone()),
108                    id: self.state.node_builder.next_id(),
109                    span: input.span,
110                },
111                Some(Type::Group) => Literal {
112                    variant: LiteralVariant::Group(value.clone()),
113                    id: self.state.node_builder.next_id(),
114                    span: input.span,
115                },
116                Some(Type::Scalar) => Literal {
117                    variant: LiteralVariant::Scalar(value.clone()),
118                    id: self.state.node_builder.next_id(),
119                    span: input.span,
120                },
121                _ => panic!(
122                    "Unexpected type for unsuffixed integer literal. This should have been caught by the type checker"
123                ),
124            }
125        } else {
126            input.clone()
127        };
128        (format!("{}", literal.display_decimal()), String::new())
129    }
130
131    fn visit_locator(&mut self, input: &LocatorExpression) -> (String, String) {
132        if input.program.name.name == self.program_id.expect("Locators only appear within programs.").name.name {
133            // This locator refers to the current program, so we only output the name, not the program.
134            (format!("{}", input.name), String::new())
135        } else {
136            (format!("{input}"), String::new())
137        }
138    }
139
140    fn visit_binary(&mut self, input: &BinaryExpression) -> (String, String) {
141        let (left_operand, left_instructions) = self.visit_expression(&input.left);
142        let (right_operand, right_instructions) = self.visit_expression(&input.right);
143
144        let opcode = match input.op {
145            BinaryOperation::Add => String::from("add"),
146            BinaryOperation::AddWrapped => String::from("add.w"),
147            BinaryOperation::And => String::from("and"),
148            BinaryOperation::BitwiseAnd => String::from("and"),
149            BinaryOperation::Div => String::from("div"),
150            BinaryOperation::DivWrapped => String::from("div.w"),
151            BinaryOperation::Eq => String::from("is.eq"),
152            BinaryOperation::Gte => String::from("gte"),
153            BinaryOperation::Gt => String::from("gt"),
154            BinaryOperation::Lte => String::from("lte"),
155            BinaryOperation::Lt => String::from("lt"),
156            BinaryOperation::Mod => String::from("mod"),
157            BinaryOperation::Mul => String::from("mul"),
158            BinaryOperation::MulWrapped => String::from("mul.w"),
159            BinaryOperation::Nand => String::from("nand"),
160            BinaryOperation::Neq => String::from("is.neq"),
161            BinaryOperation::Nor => String::from("nor"),
162            BinaryOperation::Or => String::from("or"),
163            BinaryOperation::BitwiseOr => String::from("or"),
164            BinaryOperation::Pow => String::from("pow"),
165            BinaryOperation::PowWrapped => String::from("pow.w"),
166            BinaryOperation::Rem => String::from("rem"),
167            BinaryOperation::RemWrapped => String::from("rem.w"),
168            BinaryOperation::Shl => String::from("shl"),
169            BinaryOperation::ShlWrapped => String::from("shl.w"),
170            BinaryOperation::Shr => String::from("shr"),
171            BinaryOperation::ShrWrapped => String::from("shr.w"),
172            BinaryOperation::Sub => String::from("sub"),
173            BinaryOperation::SubWrapped => String::from("sub.w"),
174            BinaryOperation::Xor => String::from("xor"),
175        };
176
177        let destination_register = self.next_register();
178        let binary_instruction = format!("    {opcode} {left_operand} {right_operand} into {destination_register};\n",);
179
180        // Concatenate the instructions.
181        let mut instructions = left_instructions;
182        instructions.push_str(&right_instructions);
183        instructions.push_str(&binary_instruction);
184
185        (destination_register, instructions)
186    }
187
188    fn visit_cast(&mut self, input: &CastExpression) -> (String, String) {
189        let (expression_operand, mut instructions) = self.visit_expression(&input.expression);
190
191        // Construct the destination register.
192        let destination_register = self.next_register();
193
194        let cast_instruction = format!(
195            "    cast {expression_operand} into {destination_register} as {};\n",
196            Self::visit_type(&input.type_)
197        );
198
199        // Concatenate the instructions.
200        instructions.push_str(&cast_instruction);
201
202        (destination_register, instructions)
203    }
204
205    fn visit_array(&mut self, input: &ArrayExpression) -> (String, String) {
206        let mut expression_operands = String::new();
207        let mut instructions = String::new();
208        for (operand, operand_instructions) in input.elements.iter().map(|expr| self.visit_expression(expr)) {
209            let space = if expression_operands.is_empty() { "" } else { " " };
210            write!(&mut expression_operands, "{space}{operand}").unwrap();
211            instructions.push_str(&operand_instructions);
212        }
213
214        // Construct the destination register.
215        let destination_register = self.next_register();
216
217        // Get the array type.
218        let Some(array_type @ Type::Array(..)) = self.state.type_table.get(&input.id) else {
219            panic!("All types should be known at this phase of compilation");
220        };
221        let array_type: String = Self::visit_type(&array_type);
222
223        let array_instruction =
224            format!("    cast {expression_operands} into {destination_register} as {array_type};\n");
225
226        // Concatenate the instructions.
227        instructions.push_str(&array_instruction);
228
229        (destination_register, instructions)
230    }
231
232    fn visit_unary(&mut self, input: &UnaryExpression) -> (String, String) {
233        let (expression_operand, expression_instructions) = self.visit_expression(&input.receiver);
234
235        // Note that non-empty suffixes must be preceded by a space.
236        let (opcode, suffix) = match input.op {
237            UnaryOperation::Abs => ("abs", ""),
238            UnaryOperation::AbsWrapped => ("abs.w", ""),
239            UnaryOperation::Double => ("double", ""),
240            UnaryOperation::Inverse => ("inv", ""),
241            UnaryOperation::Not => ("not", ""),
242            UnaryOperation::Negate => ("neg", ""),
243            UnaryOperation::Square => ("square", ""),
244            UnaryOperation::SquareRoot => ("sqrt", ""),
245            UnaryOperation::ToXCoordinate => ("cast", " as group.x"),
246            UnaryOperation::ToYCoordinate => ("cast", " as group.y"),
247        };
248
249        let destination_register = self.next_register();
250        let unary_instruction = format!("    {opcode} {expression_operand} into {destination_register}{suffix};\n");
251
252        // Concatenate the instructions.
253        let mut instructions = expression_instructions;
254        instructions.push_str(&unary_instruction);
255
256        (destination_register, instructions)
257    }
258
259    fn visit_ternary(&mut self, input: &TernaryExpression) -> (String, String) {
260        let (condition_operand, condition_instructions) = self.visit_expression(&input.condition);
261        let (if_true_operand, if_true_instructions) = self.visit_expression(&input.if_true);
262        let (if_false_operand, if_false_instructions) = self.visit_expression(&input.if_false);
263
264        let destination_register = self.next_register();
265        let ternary_instruction = format!(
266            "    ternary {condition_operand} {if_true_operand} {if_false_operand} into {destination_register};\n",
267        );
268
269        // Concatenate the instructions.
270        let mut instructions = condition_instructions;
271        instructions.push_str(&if_true_instructions);
272        instructions.push_str(&if_false_instructions);
273        instructions.push_str(&ternary_instruction);
274
275        (destination_register, instructions)
276    }
277
278    fn visit_struct_init(&mut self, input: &StructExpression) -> (String, String) {
279        // Lookup struct or record.
280        let name = if let Some((is_record, type_)) = self.composite_mapping.get(&input.path.absolute_path()) {
281            if *is_record {
282                // record.private;
283                let [record_name] = &input.path.absolute_path()[..] else {
284                    panic!("Absolute paths to records can only have a single segment at this stage.")
285                };
286                format!("{record_name}.{type_}")
287            } else {
288                // foo; // no visibility for structs
289                Self::legalize_path(&input.path.absolute_path()).expect("path format cannot be legalized at this point")
290            }
291        } else {
292            panic!("All composite types should be known at this phase of compilation")
293        };
294
295        // Initialize instruction builder strings.
296        let mut instructions = String::new();
297        let mut struct_init_instruction = String::from("    cast ");
298
299        // Visit each struct member and accumulate instructions from expressions.
300        for member in input.members.iter() {
301            let operand = if let Some(expr) = member.expression.as_ref() {
302                // Visit variable expression.
303                let (variable_operand, variable_instructions) = self.visit_expression(expr);
304                instructions.push_str(&variable_instructions);
305
306                variable_operand
307            } else {
308                // Push operand identifier.
309                let (ident_operand, ident_instructions) =
310                    self.visit_path(&Path::from(member.identifier).into_absolute());
311                instructions.push_str(&ident_instructions);
312
313                ident_operand
314            };
315
316            // Push operand name to struct init instruction.
317            write!(struct_init_instruction, "{operand} ").expect("failed to write to string");
318        }
319
320        // Push destination register to struct init instruction.
321        let destination_register = self.next_register();
322        writeln!(struct_init_instruction, "into {destination_register} as {name};",)
323            .expect("failed to write to string");
324
325        instructions.push_str(&struct_init_instruction);
326
327        (destination_register, instructions)
328    }
329
330    fn visit_array_access(&mut self, input: &ArrayAccess) -> (String, String) {
331        let (array_operand, _) = self.visit_expression(&input.array);
332
333        assert!(
334            matches!(self.state.type_table.get(&input.index.id()), Some(Type::Integer(_))),
335            "unexpected type for for array index. This should have been caught by the type checker."
336        );
337
338        let index_operand = match &input.index {
339            Expression::Literal(Literal {
340                variant: LiteralVariant::Integer(_, s) | LiteralVariant::Unsuffixed(s),
341                ..
342            }) => format!("{s}u32"),
343            _ => panic!("Array indices must be integer literals"),
344        };
345
346        (format!("{array_operand}[{index_operand}]"), String::new())
347    }
348
349    fn visit_member_access(&mut self, input: &MemberAccess) -> (String, String) {
350        // Handle `self.address`, `self.caller`, `self.checksum`, `self.edition`, `self.id`, `self.program_owner`, `self.signer`.
351        if let Expression::Path(path) = input.inner.borrow()
352            && matches!(path.try_absolute_path().as_deref(), Some([sym::SelfLower]))
353        {
354            // Get the current program ID.
355            let program_id = self.program_id.expect("Program ID should be set before traversing the program");
356
357            match input.name.name {
358                // Return the program ID directly.
359                sym::address | sym::id => {
360                    return (program_id.to_string(), String::new());
361                }
362                // Return the appropriate snarkVM operand.
363                name @ (sym::checksum | sym::edition | sym::program_owner) => {
364                    return (name.to_string(), String::new());
365                }
366                _ => {} // Do nothing as `self.signer` and `self.caller` are handled below.
367            }
368        }
369
370        let (inner_expr, _) = self.visit_expression(&input.inner);
371        let member_access = format!("{}.{}", inner_expr, input.name);
372
373        (member_access, String::new())
374    }
375
376    fn visit_repeat(&mut self, input: &RepeatExpression) -> (String, String) {
377        let (operand, mut operand_instructions) = self.visit_expression(&input.expr);
378        let count = input.count.as_u32().expect("repeat count should be known at this point");
379
380        let expression_operands = std::iter::repeat_n(operand, count as usize).collect::<Vec<_>>().join(" ");
381
382        // Construct the destination register.
383        let destination_register = self.next_register();
384
385        // Get the array type.
386        let Some(array_type @ Type::Array(..)) = self.state.type_table.get(&input.id) else {
387            panic!("All types should be known at this phase of compilation");
388        };
389        let array_type: String = Self::visit_type(&array_type);
390
391        let array_instruction =
392            format!("    cast {expression_operands} into {destination_register} as {array_type};\n");
393
394        // Concatenate the instructions.
395        operand_instructions.push_str(&array_instruction);
396
397        (destination_register, operand_instructions)
398    }
399
400    // group::GEN -> group::GEN
401    fn visit_associated_constant(&mut self, input: &AssociatedConstantExpression) -> (String, String) {
402        (format!("{input}"), String::new())
403    }
404
405    // Pedersen64::hash() -> hash.ped64
406    fn visit_associated_function(&mut self, input: &AssociatedFunctionExpression) -> (String, String) {
407        let mut instructions = String::new();
408
409        // Visit each function argument and accumulate instructions from expressions.
410        let arguments = input
411            .arguments
412            .iter()
413            .map(|argument| {
414                let (arg_string, arg_instructions) = self.visit_expression(argument);
415                instructions.push_str(&arg_instructions);
416                arg_string
417            })
418            .collect::<Vec<_>>();
419
420        // Helper function to construct the instruction associated with a simple function call.
421        // This assumes that the function call has one output.
422        let mut construct_simple_function_call = |function: &Identifier, variant: &str, arguments: Vec<String>| {
423            // Split function into [opcode, return type] e.g. hash_to_field -> [hash, field]
424            let function_name = function.name.to_string();
425            let mut names = function_name.split("_to_");
426            let opcode = names.next().expect("failed to get opcode");
427            let return_type = names.next().expect("failed to get type");
428
429            let mut instruction = format!("    {opcode}.{variant}");
430            for argument in arguments {
431                write!(instruction, " {argument}").expect("failed to write to string");
432            }
433            let destination_register = self.next_register();
434            writeln!(instruction, " into {destination_register} as {return_type};").expect("failed to write to string");
435            (destination_register, instruction)
436        };
437
438        // Construct the instruction.
439        let (destination, instruction) = match input.variant.name {
440            sym::BHP256 => construct_simple_function_call(&input.name, "bhp256", arguments),
441            sym::BHP512 => construct_simple_function_call(&input.name, "bhp512", arguments),
442            sym::BHP768 => construct_simple_function_call(&input.name, "bhp768", arguments),
443            sym::BHP1024 => construct_simple_function_call(&input.name, "bhp1024", arguments),
444            sym::Keccak256 => construct_simple_function_call(&input.name, "keccak256", arguments),
445            sym::Keccak384 => construct_simple_function_call(&input.name, "keccak384", arguments),
446            sym::Keccak512 => construct_simple_function_call(&input.name, "keccak512", arguments),
447            sym::Pedersen64 => construct_simple_function_call(&input.name, "ped64", arguments),
448            sym::Pedersen128 => construct_simple_function_call(&input.name, "ped128", arguments),
449            sym::Poseidon2 => construct_simple_function_call(&input.name, "psd2", arguments),
450            sym::Poseidon4 => construct_simple_function_call(&input.name, "psd4", arguments),
451            sym::Poseidon8 => construct_simple_function_call(&input.name, "psd8", arguments),
452            sym::SHA3_256 => construct_simple_function_call(&input.name, "sha3_256", arguments),
453            sym::SHA3_384 => construct_simple_function_call(&input.name, "sha3_384", arguments),
454            sym::SHA3_512 => construct_simple_function_call(&input.name, "sha3_512", arguments),
455            sym::Mapping => match input.name.name {
456                sym::get => {
457                    let mut instruction = "    get".to_string();
458                    let destination_register = self.next_register();
459                    // Write the mapping name and the key.
460                    writeln!(instruction, " {}[{}] into {destination_register};", arguments[0], arguments[1])
461                        .expect("failed to write to string");
462                    (destination_register, instruction)
463                }
464                sym::get_or_use => {
465                    let mut instruction = "    get.or_use".to_string();
466                    let destination_register = self.next_register();
467                    // Write the mapping name, the key, and the default value.
468                    writeln!(
469                        instruction,
470                        " {}[{}] {} into {destination_register};",
471                        arguments[0], arguments[1], arguments[2]
472                    )
473                    .expect("failed to write to string");
474                    (destination_register, instruction)
475                }
476                sym::set => {
477                    let mut instruction = "    set".to_string();
478                    // Write the value, mapping name, and the key.
479                    writeln!(instruction, " {} into {}[{}];", arguments[2], arguments[0], arguments[1])
480                        .expect("failed to write to string");
481                    (String::new(), instruction)
482                }
483                sym::remove => {
484                    let mut instruction = "    remove".to_string();
485                    // Write the mapping name and the key.
486                    writeln!(instruction, " {}[{}];", arguments[0], arguments[1]).expect("failed to write to string");
487                    (String::new(), instruction)
488                }
489                sym::contains => {
490                    let mut instruction = "    contains".to_string();
491                    let destination_register = self.next_register();
492                    // Write the mapping name and the key.
493                    writeln!(instruction, " {}[{}] into {destination_register};", arguments[0], arguments[1])
494                        .expect("failed to write to string");
495                    (destination_register, instruction)
496                }
497                _ => panic!("The only variants of Mapping are get, get_or, and set"),
498            },
499            sym::Optional => panic!("Functions for optional types should have been lowered by now."),
500            sym::group => {
501                match input.name {
502                    Identifier { name: sym::to_x_coordinate, .. } => {
503                        let mut instruction = "    cast".to_string();
504                        let destination_register = self.next_register();
505                        // Write the argument and the destination register.
506                        writeln!(instruction, " {} into {destination_register} as group.x;", arguments[0],)
507                            .expect("failed to write to string");
508                        (destination_register, instruction)
509                    }
510                    Identifier { name: sym::to_y_coordinate, .. } => {
511                        let mut instruction = "    cast".to_string();
512                        let destination_register = self.next_register();
513                        // Write the argument and the destination register.
514                        writeln!(instruction, " {} into {destination_register} as group.y;", arguments[0],)
515                            .expect("failed to write to string");
516                        (destination_register, instruction)
517                    }
518                    _ => panic!("The only associated methods of `group` are `to_x_coordinate` and `to_y_coordinate`"),
519                }
520            }
521            sym::ChaCha => {
522                // Get the destination register.
523                let destination_register = self.next_register();
524                // Construct the instruction template.
525                let mut instruction = format!("    rand.chacha into {destination_register} as ");
526                // Write the return type.
527                match input.name {
528                    Identifier { name: sym::rand_address, .. } => writeln!(instruction, "address;"),
529                    Identifier { name: sym::rand_bool, .. } => writeln!(instruction, "boolean;"),
530                    Identifier { name: sym::rand_field, .. } => writeln!(instruction, "field;"),
531                    Identifier { name: sym::rand_group, .. } => writeln!(instruction, "group;"),
532                    Identifier { name: sym::rand_i8, .. } => writeln!(instruction, "i8;"),
533                    Identifier { name: sym::rand_i16, .. } => writeln!(instruction, "i16;"),
534                    Identifier { name: sym::rand_i32, .. } => writeln!(instruction, "i32;"),
535                    Identifier { name: sym::rand_i64, .. } => writeln!(instruction, "i64;"),
536                    Identifier { name: sym::rand_i128, .. } => writeln!(instruction, "i128;"),
537                    Identifier { name: sym::rand_scalar, .. } => writeln!(instruction, "scalar;"),
538                    Identifier { name: sym::rand_u8, .. } => writeln!(instruction, "u8;"),
539                    Identifier { name: sym::rand_u16, .. } => writeln!(instruction, "u16;"),
540                    Identifier { name: sym::rand_u32, .. } => writeln!(instruction, "u32;"),
541                    Identifier { name: sym::rand_u64, .. } => writeln!(instruction, "u64;"),
542                    Identifier { name: sym::rand_u128, .. } => writeln!(instruction, "u128;"),
543                    _ => panic!("The only associated methods of ChaCha are `rand_*`"),
544                }
545                .expect("failed to write to string");
546                (destination_register, instruction)
547            }
548            sym::signature => {
549                let mut instruction = "    sign.verify".to_string();
550                let destination_register = self.next_register();
551                // Write the arguments and the destination register.
552                writeln!(
553                    instruction,
554                    " {} {} {} into {destination_register};",
555                    arguments[0], arguments[1], arguments[2]
556                )
557                .expect("failed to write to string");
558                (destination_register, instruction)
559            }
560            sym::Future => {
561                let mut instruction = "    await".to_string();
562                writeln!(instruction, " {};", arguments[0]).expect("failed to write to string");
563                (String::new(), instruction)
564            }
565            sym::ProgramCore => {
566                match input.name.name {
567                    // Generate code for `Program::checksum`, `Program::edition`, and `Program::program_owner`
568                    name @ (sym::checksum | sym::edition | sym::program_owner) => {
569                        // Get the program ID from the first argument.
570                        let program_id =
571                            ProgramId::from_str_with_network(&arguments[0].replace("\"", ""), self.state.network)
572                                .expect("Type checking guarantees that the program name is valid");
573                        // If the program name matches the current program ID, then use the operand directly, otherwise fully qualify the operand.
574                        let operand = match program_id.to_string()
575                            == self.program_id.expect("The program ID is set before traversing the program").to_string()
576                        {
577                            true => name.to_string(),
578                            false => format!("{program_id}/{name}"),
579                        };
580                        (operand, String::new())
581                    }
582                    // No other variants are allowed.
583                    _ => panic!(
584                        "The only associated methods of `Program` are `checksum`, `edition`, and `program_owner`"
585                    ),
586                }
587            }
588            sym::CheatCode => {
589                (String::new(), String::new())
590                // Do nothing. Cheat codes do not generate instructions.
591            }
592            _ => {
593                panic!("All core functions should be known at this phase of compilation")
594            }
595        };
596        // Add the instruction to the list of instructions.
597        instructions.push_str(&instruction);
598
599        (destination, instructions)
600    }
601
602    fn visit_async(&mut self, _input: &AsyncExpression) -> (String, String) {
603        panic!("`AsyncExpression`s should not be in the AST at this phase of compilation.")
604    }
605
606    fn visit_call(&mut self, input: &CallExpression) -> (String, String) {
607        let caller_program = self.program_id.expect("Calls only appear within programs.").name.name;
608        let callee_program = input.program.unwrap_or(caller_program);
609        let func_symbol = self
610            .state
611            .symbol_table
612            .lookup_function(&Location::new(callee_program, input.function.absolute_path().to_vec()))
613            .expect("Type checking guarantees functions exist");
614
615        // Need to determine the program the function originated from as well as if the function has a finalize block.
616        let mut call_instruction = if caller_program != callee_program {
617            // All external functions must be defined as stubs.
618            assert!(
619                self.program.stubs.get(&callee_program).is_some(),
620                "Type checking guarantees that imported and stub programs are present."
621            );
622            format!("    call {}.aleo/{}", callee_program, input.function)
623        } else if func_symbol.function.variant.is_async() {
624            format!("    async {}", self.current_function.unwrap().identifier)
625        } else {
626            format!("    call {}", input.function)
627        };
628
629        let mut instructions = String::new();
630
631        for argument in input.arguments.iter() {
632            let (argument, argument_instructions) = self.visit_expression(argument);
633            write!(call_instruction, " {argument}").expect("failed to write to string");
634            instructions.push_str(&argument_instructions);
635        }
636
637        // Initialize storage for the destination registers.
638        let mut destinations = Vec::new();
639
640        // Create operands for the output registers.
641        match func_symbol.function.output_type.clone() {
642            Type::Unit => {} // Do nothing
643            Type::Tuple(tuple) => match tuple.length() {
644                0 | 1 => panic!("Parsing guarantees that a tuple type has at least two elements"),
645                len => {
646                    for _ in 0..len {
647                        destinations.push(self.next_register());
648                    }
649                }
650            },
651            _ => {
652                destinations.push(self.next_register());
653            }
654        }
655
656        // Add a register for async functions to represent the future created.
657        if func_symbol.function.variant == Variant::AsyncFunction {
658            destinations.push(self.next_register());
659        }
660
661        // Construct the output operands. These are the destination registers **without** the future.
662        let output_operands = destinations.join(" ");
663
664        // If destination registers were created, write them to the call instruction.
665        if !destinations.is_empty() {
666            write!(call_instruction, " into").expect("failed to write to string");
667            for destination in &destinations {
668                write!(call_instruction, " {destination}").expect("failed to write to string");
669            }
670        }
671
672        // Write the closing semicolon.
673        writeln!(call_instruction, ";").expect("failed to write to string");
674
675        // Push the call instruction to the list of instructions.
676        instructions.push_str(&call_instruction);
677
678        // Return the output operands and the instructions.
679        (output_operands, instructions)
680    }
681
682    fn visit_tuple(&mut self, input: &TupleExpression) -> (String, String) {
683        // Need to return a single string here so we will join the tuple elements with ' '
684        // and split them after this method is called.
685        let mut tuple_elements = Vec::with_capacity(input.elements.len());
686        let mut instructions = String::new();
687
688        // Visit each tuple element and accumulate instructions from expressions.
689        for element in input.elements.iter() {
690            let (element, element_instructions) = self.visit_expression(element);
691            tuple_elements.push(element);
692            instructions.push_str(&element_instructions);
693        }
694
695        // CAUTION: does not return the destination_register.
696        (tuple_elements.join(" "), instructions)
697    }
698
699    fn visit_unit(&mut self, _input: &UnitExpression) -> (String, String) {
700        panic!("`UnitExpression`s should not be visited during code generation.")
701    }
702
703    pub fn clone_register(&mut self, register: &str, typ: &Type) -> (String, String) {
704        let new_reg = self.next_register();
705        match typ {
706            Type::Address
707            | Type::Boolean
708            | Type::Field
709            | Type::Group
710            | Type::Scalar
711            | Type::Signature
712            | Type::Integer(_) => {
713                // These types can be cloned just by casting them to themselves.
714                let instruction = format!("    cast {register} into {new_reg} as {typ};\n");
715                (new_reg, instruction)
716            }
717
718            Type::Array(array_type) => {
719                // We need to cast the old array's members into the new array.
720                let mut instruction = "    cast ".to_string();
721                for i in 0..array_type.length.as_u32().expect("length should be known at this point") as usize {
722                    write!(&mut instruction, "{register}[{i}u32] ").unwrap();
723                }
724                writeln!(&mut instruction, "into {new_reg} as {};", Self::visit_type(typ)).unwrap();
725                (new_reg, instruction)
726            }
727
728            Type::Composite(comp_ty) => {
729                // We need to cast the old struct or record's members into the new one.
730                let program = comp_ty.program.unwrap_or(self.program_id.unwrap().name.name);
731                let location = Location::new(program, comp_ty.path.absolute_path().to_vec());
732                let comp = self
733                    .state
734                    .symbol_table
735                    .lookup_record(&location)
736                    .or_else(|| self.state.symbol_table.lookup_struct(&comp_ty.path.absolute_path()))
737                    .unwrap();
738                let mut instruction = "    cast ".to_string();
739                for member in &comp.members {
740                    write!(&mut instruction, "{register}.{} ", member.identifier.name).unwrap();
741                }
742                writeln!(
743                    &mut instruction,
744                    "into {new_reg} as {};",
745                    // We call `..with_visibility` just so we get the `.record` appended if it's a record.
746                    self.visit_type_with_visibility(typ, leo_ast::Mode::None)
747                )
748                .unwrap();
749                (new_reg, instruction)
750            }
751
752            Type::Optional(_) => panic!("All optional types should have been lowered by now."),
753
754            Type::Mapping(..)
755            | Type::Future(..)
756            | Type::Tuple(..)
757            | Type::Identifier(..)
758            | Type::String
759            | Type::Unit
760            | Type::Numeric
761            | Type::Err => panic!("Objects of type {typ} cannot be cloned."),
762        }
763    }
764}