1use 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
57impl 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 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 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 let literal = if let LiteralVariant::Unsuffixed(value) = &input.variant {
108 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 fn prepare_literal(s: &str) -> String {
143 let (neg, rest) = s.strip_prefix("-").map(|rest| ("-", rest)).unwrap_or(("", s));
145 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 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 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 let dest_reg = self.next_register();
244
245 let cast_instruction = AleoStmt::Cast(operand, dest_reg.clone(), Self::visit_type(&input.type_));
246
247 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 let destination_register = self.next_register();
267
268 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 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 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 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 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 let struct_type = if let Some(is_record) = self.composite_mapping.get(&input.path.absolute_path()) {
332 if *is_record {
333 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 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 let mut instructions = vec![];
351
352 let operands: Vec<AleoExpr> = input
354 .members
355 .iter()
356 .filter_map(|member| {
357 if let Some(expr) = member.expression.as_ref() {
358 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 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 let dest_reg = self.next_register();
416
417 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 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 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 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 let mut destinations = Vec::new();
477
478 match func_symbol.function.output_type.clone() {
480 t if t.is_empty() => {} 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 if func_symbol.function.variant == Variant::AsyncFunction {
496 destinations.push(self.next_register());
497 }
498
499 let call_instruction = if caller_program != callee_program {
501 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 instructions.push(call_instruction);
516
517 (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 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 (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 let generate_program_core = |program: &str, name: &str| {
551 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 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 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 }
679 Some(Intrinsic::Serialize(variant)) => {
680 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 let is_raw = matches!(variant, SerializeVariant::ToBitsRaw);
686 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 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 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 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 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}