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