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