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