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