leo_passes/code_generation/
program.rs1use super::*;
18
19use leo_ast::{
20 Composite,
21 Constructor,
22 Function,
23 Location,
24 Mapping,
25 Member,
26 Mode,
27 NetworkName,
28 Program,
29 ProgramScope,
30 Type,
31 UpgradeVariant,
32 Variant,
33 snarkvm_admin_constructor,
34 snarkvm_checksum_constructor,
35 snarkvm_noupgrade_constructor,
36};
37use leo_span::{Symbol, sym};
38
39use indexmap::IndexMap;
40use itertools::Itertools;
41use snarkvm::prelude::{CanaryV0, MainnetV0, TestnetV0};
42use std::fmt::Write as _;
43
44const EXPECT_STR: &str = "Failed to write code";
45
46impl<'a> CodeGeneratingVisitor<'a> {
47 pub fn visit_program(&mut self, input: &'a Program) -> String {
48 let mut program_string = String::new();
50
51 input.stubs.iter().for_each(|(program_name, _)| {
53 writeln!(program_string, "import {program_name}.aleo;").expect(EXPECT_STR);
54 });
55
56 let program_scope: &ProgramScope = input.program_scopes.values().next().unwrap();
59
60 self.program_id = Some(program_scope.program_id);
61
62 writeln!(program_string, "program {};", program_scope.program_id).expect(EXPECT_STR);
64
65 let order = self.state.struct_graph.post_order().unwrap();
68
69 let this_program = self.program_id.unwrap().name.name;
70
71 let lookup = |name: &[Symbol]| {
72 self.state
73 .symbol_table
74 .lookup_struct(name)
75 .or_else(|| self.state.symbol_table.lookup_record(&Location::new(this_program, name.to_vec())))
76 };
77
78 for name in order.into_iter() {
80 if let Some(struct_) = lookup(&name) {
81 program_string.push_str(&self.visit_struct_or_record(struct_, &name));
82 }
83 }
84
85 for (_symbol, mapping) in program_scope.mappings.iter() {
87 program_string.push_str(&self.visit_mapping(mapping));
88 }
89
90 for (_symbol, function) in program_scope.functions.iter() {
94 if function.variant != Variant::AsyncFunction {
95 let mut function_string = self.visit_function(function);
96
97 if function.variant == Variant::AsyncTransition {
99 self.finalize_caller = Some(function.identifier.name);
101 let finalize = &self
103 .state
104 .symbol_table
105 .lookup_function(&Location::new(
106 self.program_id.unwrap().name.name,
107 vec![function.identifier.name], ))
109 .unwrap()
110 .clone()
111 .finalizer
112 .unwrap();
113 function_string.push_str(
115 &self.visit_function_with(
116 &program_scope
117 .functions
118 .iter()
119 .find(|(name, _f)| vec![*name] == finalize.location.path)
120 .unwrap()
121 .1,
122 &finalize.future_inputs,
123 ),
124 );
125 }
126
127 program_string.push_str(&function_string);
128 }
129 }
130
131 if let Some(constructor) = program_scope.constructor.as_ref() {
133 program_string.push_str(&self.visit_constructor(constructor));
135 }
136
137 program_string
138 }
139
140 fn visit_struct_or_record(&mut self, struct_: &'a Composite, absolute_path: &[Symbol]) -> String {
141 if struct_.is_record {
142 self.visit_record(struct_, absolute_path)
143 } else {
144 self.visit_struct(struct_, absolute_path)
145 }
146 }
147
148 fn visit_struct(&mut self, struct_: &'a Composite, absolute_path: &[Symbol]) -> String {
149 self.composite_mapping.insert(absolute_path.to_vec(), (false, String::from("private"))); let mut output_string = format!(
153 "\nstruct {}:\n",
154 Self::legalize_path(absolute_path).unwrap_or_else(|| panic!(
155 "path format cannot be legalized at this point: {}",
156 absolute_path.iter().join("::")
157 ))
158 ); for var in struct_.members.iter() {
162 writeln!(output_string, " {} as {};", var.identifier, Self::visit_type(&var.type_),).expect(EXPECT_STR);
163 }
164
165 output_string
166 }
167
168 fn visit_record(&mut self, record: &'a Composite, absolute_path: &[Symbol]) -> String {
169 self.composite_mapping.insert(absolute_path.to_vec(), (true, "record".into()));
171
172 let mut output_string = format!("\nrecord {}:\n", record.identifier); let mut members = Vec::with_capacity(record.members.len());
175 let mut member_map: IndexMap<Symbol, Member> =
176 record.members.clone().into_iter().map(|member| (member.identifier.name, member)).collect();
177
178 members.push(member_map.shift_remove(&sym::owner).unwrap());
181
182 members.extend(member_map.into_iter().map(|(_, member)| member));
184
185 for var in members.iter() {
187 let mode = match var.mode {
188 Mode::Constant => "constant",
189 Mode::Public => "public",
190 Mode::None | Mode::Private => "private",
191 };
192 writeln!(
193 output_string,
194 " {} as {}.{mode};", var.identifier,
196 Self::visit_type(&var.type_)
197 )
198 .expect(EXPECT_STR);
199 }
200
201 output_string
202 }
203
204 fn visit_function_with(&mut self, function: &'a Function, futures: &[Location]) -> String {
205 self.next_register = 0;
207 self.variable_mapping = IndexMap::new();
208 self.variant = Some(function.variant);
209 self.variable_mapping.insert(sym::SelfLower, "self".to_string());
211 self.variable_mapping.insert(sym::block, "block".to_string());
212 self.variable_mapping.insert(sym::network, "network".to_string());
213 self.current_function = Some(function);
214
215 let mut function_string = match function.variant {
220 Variant::Transition | Variant::AsyncTransition => format!("\nfunction {}:\n", function.identifier),
221 Variant::Function => format!("\nclosure {}:\n", function.identifier),
222 Variant::AsyncFunction => format!("\nfinalize {}:\n", self.finalize_caller.unwrap()),
223 Variant::Inline => return String::new(),
224 Variant::Script => panic!("script should not appear in native code"),
225 };
226
227 let mut futures = futures.iter();
228
229 self.internal_record_inputs.clear();
230
231 for input in function.input.iter() {
233 let register_string = self.next_register();
234
235 if let Type::Composite(comp) = &input.type_ {
237 let program = comp.program.unwrap_or(self.program_id.unwrap().name.name);
238 if let Some(record) =
239 self.state.symbol_table.lookup_record(&Location::new(program, comp.path.absolute_path().to_vec()))
240 {
241 if record.external.is_none() || record.external == self.program_id.map(|id| id.name.name) {
242 self.internal_record_inputs.insert(register_string.clone());
243 }
244 }
245 }
246
247 let type_string = {
248 self.variable_mapping.insert(input.identifier.name, register_string.clone());
249 let visibility = match (self.variant.unwrap(), input.mode) {
251 (Variant::AsyncTransition, Mode::None) | (Variant::Transition, Mode::None) => Mode::Private,
252 (Variant::AsyncFunction, Mode::None) => Mode::Public,
253 _ => input.mode,
254 };
255 if matches!(input.type_, Type::Future(_)) {
257 let location = futures
258 .next()
259 .expect("Type checking guarantees we have future locations for each future input");
260 let [future_name] = location.path.as_slice() else {
261 panic!("All futures must have a single segment paths since they don't belong to submodules.")
262 };
263 format!("{}.aleo/{}.future", location.program, future_name)
264 } else {
265 self.visit_type_with_visibility(&input.type_, visibility)
266 }
267 };
268
269 writeln!(function_string, " input {register_string} as {type_string};",).expect(EXPECT_STR);
270 }
271
272 let block_string = self.visit_block(&function.block);
274 if matches!(self.variant.unwrap(), Variant::Function | Variant::AsyncFunction)
275 && block_string.lines().all(|line| line.starts_with(" output "))
276 {
277 function_string.push_str(" assert.eq true true;\n");
280 }
281
282 function_string.push_str(&block_string);
283
284 function_string
285 }
286
287 fn visit_function(&mut self, function: &'a Function) -> String {
288 self.visit_function_with(function, &[])
289 }
290
291 fn visit_constructor(&mut self, constructor: &'a Constructor) -> String {
292 self.next_register = 0;
294 self.variable_mapping = IndexMap::new();
295 self.variant = Some(Variant::AsyncFunction);
296 self.variable_mapping.insert(sym::SelfLower, "self".to_string());
298 self.variable_mapping.insert(sym::block, "block".to_string());
299 self.variable_mapping.insert(sym::network, "network".to_string());
300
301 let upgrade_variant = constructor
303 .get_upgrade_variant_with_network(self.state.network)
304 .expect("Type checking should have validated the upgrade variant");
305
306 let constructor = match &upgrade_variant {
309 UpgradeVariant::Admin { address } => snarkvm_admin_constructor(address),
310 UpgradeVariant::Checksum { mapping, key, .. } => {
311 if mapping.program
312 == self.program_id.expect("Program ID should be set before traversing the program").name.name
313 {
314 let [mapping_name] = &mapping.path[..] else {
315 panic!("Mappings are only allowed in the top level program at this stage");
316 };
317 snarkvm_checksum_constructor(mapping_name, key)
318 } else {
319 snarkvm_checksum_constructor(mapping, key)
320 }
321 }
322 UpgradeVariant::Custom => format!("\nconstructor:\n{}\n", self.visit_block(&constructor.block)),
323 UpgradeVariant::NoUpgrade => snarkvm_noupgrade_constructor(),
324 };
325
326 if let Err(e) = match self.state.network {
328 NetworkName::MainnetV0 => check_snarkvm_constructor::<MainnetV0>(&constructor),
329 NetworkName::TestnetV0 => check_snarkvm_constructor::<TestnetV0>(&constructor),
330 NetworkName::CanaryV0 => check_snarkvm_constructor::<CanaryV0>(&constructor),
331 } {
332 panic!("Compilation produced an invalid constructor: {e}");
333 };
334
335 constructor
337 }
338
339 fn visit_mapping(&mut self, mapping: &'a Mapping) -> String {
340 let mut mapping_string = format!("\nmapping {}:\n", mapping.identifier);
342
343 let create_type = |type_: &Type| {
345 match type_ {
346 Type::Mapping(_) | Type::Tuple(_) => panic!("Mappings cannot contain mappings or tuples."),
347 Type::Identifier(identifier) => {
348 let (is_record, _) = self.composite_mapping.get(&vec![identifier.name]).unwrap();
351 assert!(!is_record, "Type checking guarantees that mappings cannot contain records.");
352 self.visit_type_with_visibility(type_, Mode::Public)
353 }
354 type_ => self.visit_type_with_visibility(type_, Mode::Public),
355 }
356 };
357
358 writeln!(mapping_string, " key as {};", create_type(&mapping.key_type)).expect(EXPECT_STR);
360
361 writeln!(mapping_string, " value as {};", create_type(&mapping.value_type)).expect(EXPECT_STR);
363
364 self.global_mapping.insert(mapping.identifier.name, mapping.identifier.to_string());
366
367 mapping_string
368 }
369}