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};
34use leo_span::{Symbol, sym};
35
36use indexmap::IndexMap;
37use itertools::Itertools;
38use snarkvm::prelude::{CanaryV0, MainnetV0, TestnetV0};
39
40impl<'a> CodeGeneratingVisitor<'a> {
41 pub fn visit_program(&mut self, input: &'a Program) -> AleoProgram {
42 let imports = input.stubs.iter().map(|(program_name, _)| program_name.to_string()).collect();
45
46 let program_scope: &ProgramScope = input.program_scopes.values().next().unwrap();
49
50 let program_id = program_scope.program_id;
51 self.program_id = Some(program_id);
52
53 let order = self.state.struct_graph.post_order().unwrap();
56
57 let this_program = self.program_id.unwrap().name.name;
58
59 let lookup = |name: &[Symbol]| {
60 self.state
61 .symbol_table
62 .lookup_struct(name)
63 .or_else(|| self.state.symbol_table.lookup_record(&Location::new(this_program, name.to_vec())))
64 };
65
66 let data_types = order
68 .into_iter()
69 .filter_map(|name| lookup(&name).map(|struct_| self.visit_struct_or_record(struct_, &name)))
70 .collect();
71
72 let mappings = program_scope.mappings.iter().map(|(_symbol, mapping)| self.visit_mapping(mapping)).collect();
74
75 let functions = program_scope
79 .functions
80 .iter()
81 .filter_map(|(_symbol, function)| {
82 if function.variant != Variant::AsyncFunction {
83 let mut aleo_function = self.visit_function(function);
84
85 if function.variant == Variant::AsyncTransition {
87 self.finalize_caller = Some(function.identifier.name);
89 let finalize = &self
91 .state
92 .symbol_table
93 .lookup_function(&Location::new(
94 self.program_id.unwrap().name.name,
95 vec![function.identifier.name], ))
97 .unwrap()
98 .clone()
99 .finalizer
100 .unwrap();
101 if let Some(caller) = &mut aleo_function {
103 caller.as_function_ref_mut().finalize = Some(
104 self.visit_function_with(
105 &program_scope
106 .functions
107 .iter()
108 .find(|(name, _f)| vec![*name] == finalize.location.path)
109 .unwrap()
110 .1,
111 &finalize.future_inputs,
112 )
113 .unwrap()
114 .as_finalize(),
115 );
116 }
117 }
118 aleo_function
119 } else {
120 None
121 }
122 })
123 .collect();
124
125 let constructor = program_scope.constructor.as_ref().map(|c| self.visit_constructor(c));
127
128 AleoProgram { imports, program_id, data_types, mappings, functions, constructor }
129 }
130
131 fn visit_struct_or_record(&mut self, struct_: &'a Composite, absolute_path: &[Symbol]) -> AleoDatatype {
132 if struct_.is_record {
133 AleoDatatype::Record(self.visit_record(struct_, absolute_path))
134 } else {
135 AleoDatatype::Struct(self.visit_struct(struct_, absolute_path))
136 }
137 }
138
139 fn visit_struct(&mut self, struct_: &'a Composite, absolute_path: &[Symbol]) -> AleoStruct {
140 self.composite_mapping.insert(absolute_path.to_vec(), false); let name = Self::legalize_path(absolute_path).unwrap_or_else(|| {
145 panic!("path format cannot be legalized at this point: {}", absolute_path.iter().join("::"))
146 });
147
148 let fields = struct_
150 .members
151 .iter()
152 .filter_map(|var| {
153 if var.type_.is_empty() {
154 None
155 } else {
156 Some((var.identifier.to_string(), Self::visit_type(&var.type_)))
157 }
158 })
159 .collect();
160
161 AleoStruct { name, fields }
162 }
163
164 fn visit_record(&mut self, record: &'a Composite, absolute_path: &[Symbol]) -> AleoRecord {
165 self.composite_mapping.insert(absolute_path.to_vec(), true);
167
168 let name = record.identifier.to_string(); let mut members = Vec::with_capacity(record.members.len());
171 let mut member_map: IndexMap<Symbol, Member> =
172 record.members.clone().into_iter().map(|member| (member.identifier.name, member)).collect();
173
174 members.push(member_map.shift_remove(&sym::owner).unwrap());
177
178 members.extend(member_map.into_iter().map(|(_, member)| member));
180
181 let fields = members
183 .iter()
184 .filter_map(|var| {
185 if var.type_.is_empty() {
186 None
187 } else {
188 Some((var.identifier.to_string(), Self::visit_type(&var.type_), match var.mode {
189 Mode::Constant => AleoVisibility::Constant,
190 Mode::Public => AleoVisibility::Public,
191 Mode::None | Mode::Private => AleoVisibility::Private,
192 }))
193 }
194 })
195 .collect();
196
197 AleoRecord { name, fields }
198 }
199
200 fn visit_function_with(&mut self, function: &'a Function, futures: &[Location]) -> Option<AleoFunctional> {
201 self.next_register = 0;
203 self.variable_mapping = IndexMap::new();
204 self.variant = Some(function.variant);
205 self.variable_mapping.insert(sym::SelfLower, AleoExpr::Reg(AleoReg::Self_));
207 self.variable_mapping.insert(sym::block, AleoExpr::Reg(AleoReg::Block));
208 self.variable_mapping.insert(sym::network, AleoExpr::Reg(AleoReg::Network));
209 self.current_function = Some(function);
210
211 let function_name = match function.variant {
216 Variant::Inline => return None,
217 Variant::Script => panic!("script should not appear in native code"),
218 Variant::Transition | Variant::AsyncTransition => function.identifier.to_string(),
219 Variant::Function => function.identifier.to_string(),
220 Variant::AsyncFunction => self.finalize_caller.unwrap().to_string(),
221 };
222
223 let mut futures = futures.iter();
224
225 self.internal_record_inputs.clear();
226
227 let inputs: Vec<AleoInput> = function
229 .input
230 .iter()
231 .filter_map(|input| {
232 if input.type_.is_empty() {
233 return None;
234 }
235 let register_num = self.next_register();
236
237 if let Type::Composite(comp) = &input.type_ {
239 let program = comp.program.unwrap_or(self.program_id.unwrap().name.name);
240 if let Some(record) = self
241 .state
242 .symbol_table
243 .lookup_record(&Location::new(program, comp.path.absolute_path().to_vec()))
244 && (record.external.is_none() || record.external == self.program_id.map(|id| id.name.name))
245 {
246 self.internal_record_inputs.insert(AleoExpr::Reg(register_num.clone()));
247 }
248 }
249
250 let (input_type, input_visibility) = {
251 self.variable_mapping.insert(input.identifier.name, AleoExpr::Reg(register_num.clone()));
252 let visibility = match (self.variant.unwrap(), input.mode) {
254 (Variant::AsyncTransition, Mode::None) | (Variant::Transition, Mode::None) => {
255 Some(AleoVisibility::Private)
256 }
257 (Variant::AsyncFunction, Mode::None) => Some(AleoVisibility::Public),
258 (_, mode) => AleoVisibility::maybe_from(mode),
259 };
260 if matches!(input.type_, Type::Future(_)) {
262 let location = futures
263 .next()
264 .expect("Type checking guarantees we have future locations for each future input");
265 let [future_name] = location.path.as_slice() else {
266 panic!(
267 "All futures must have a single segment paths since they don't belong to submodules."
268 )
269 };
270 (
271 AleoType::Future { name: future_name.to_string(), program: location.program.to_string() },
272 None,
273 )
274 } else {
275 self.visit_type_with_visibility(&input.type_, visibility)
276 }
277 };
278
279 Some(AleoInput { register: register_num, type_: input_type, visibility: input_visibility })
280 })
281 .collect();
282
283 let mut statements = self.visit_block(&function.block);
285 if matches!(self.variant.unwrap(), Variant::Function | Variant::AsyncFunction)
286 && statements.iter().all(|stm| matches!(stm, AleoStmt::Output(..)))
287 {
288 statements.insert(0, AleoStmt::AssertEq(AleoExpr::Bool(true), AleoExpr::Bool(true)));
291 }
292
293 match function.variant {
294 Variant::Inline | Variant::Script => None,
295 Variant::Transition | Variant::AsyncTransition => {
296 Some(AleoFunctional::Function(AleoFunction { name: function_name, inputs, statements, finalize: None })) }
298 Variant::Function => Some(AleoFunctional::Closure(AleoClosure { name: function_name, inputs, statements })),
299 Variant::AsyncFunction => {
300 Some(AleoFunctional::Finalize(AleoFinalize { caller_name: function_name, inputs, statements }))
301 }
302 }
303 }
304
305 fn visit_function(&mut self, function: &'a Function) -> Option<AleoFunctional> {
306 self.visit_function_with(function, &[])
307 }
308
309 fn visit_constructor(&mut self, constructor: &'a Constructor) -> AleoConstructor {
310 self.next_register = 0;
312 self.variable_mapping = IndexMap::new();
313 self.variant = Some(Variant::AsyncFunction);
314 self.variable_mapping.insert(sym::SelfLower, AleoExpr::Reg(AleoReg::Self_));
316 self.variable_mapping.insert(sym::block, AleoExpr::Reg(AleoReg::Block));
317 self.variable_mapping.insert(sym::network, AleoExpr::Reg(AleoReg::Network));
318
319 let upgrade_variant = constructor
321 .get_upgrade_variant_with_network(self.state.network)
322 .expect("Type checking should have validated the upgrade variant");
323
324 let constructor = match &upgrade_variant {
327 UpgradeVariant::Admin { address } => AleoConstructor {
329 is_custom: false,
330 statements: vec![AleoStmt::AssertEq(
331 AleoExpr::RawName("program_owner".to_string()),
332 AleoExpr::RawName(address.to_string()),
333 )],
334 },
335
336 UpgradeVariant::Checksum { mapping, key, .. } => {
337 let map_name = if mapping.program
338 == self.program_id.expect("Program ID should be set before traversing the program").name.name
339 {
340 let [mapping_name] = &mapping.path[..] else {
341 panic!("Mappings are only allowed in the top level program at this stage");
342 };
343 mapping_name.to_string()
344 } else {
345 mapping.to_string()
346 };
347 AleoConstructor {
350 is_custom: false,
351 statements: vec![
352 AleoStmt::BranchEq(
353 AleoExpr::RawName("edition".to_string()),
354 AleoExpr::U16(0),
355 "end".to_string(),
356 ),
357 AleoStmt::Get(AleoExpr::RawName(map_name), AleoExpr::RawName(key.to_string()), AleoReg::R(0)),
358 AleoStmt::AssertEq(AleoExpr::RawName("checksum".to_string()), AleoExpr::Reg(AleoReg::R(0))),
359 AleoStmt::Position("end".to_string()),
360 ],
361 }
362 }
363 UpgradeVariant::Custom => {
364 AleoConstructor { statements: self.visit_block(&constructor.block), is_custom: true }
365 }
366 UpgradeVariant::NoUpgrade => {
367 AleoConstructor {
369 is_custom: false,
370 statements: vec![AleoStmt::AssertEq(AleoExpr::RawName("edition".to_string()), AleoExpr::U16(0))],
371 }
372 }
373 };
374
375 if let Err(e) = match self.state.network {
377 NetworkName::MainnetV0 => check_snarkvm_constructor::<MainnetV0>(&constructor),
378 NetworkName::TestnetV0 => check_snarkvm_constructor::<TestnetV0>(&constructor),
379 NetworkName::CanaryV0 => check_snarkvm_constructor::<CanaryV0>(&constructor),
380 } {
381 panic!("Compilation produced an invalid constructor: {e}");
382 };
383
384 constructor
386 }
387
388 fn visit_mapping(&mut self, mapping: &'a Mapping) -> AleoMapping {
389 let legalized_mapping_name = Self::legalize_path(&[mapping.identifier.name]);
390 let name = legalized_mapping_name
392 .clone()
393 .unwrap_or_else(|| panic!("path format cannot be legalized at this point: {}", mapping.identifier));
394
395 let create_type = |type_: &Type| {
397 match type_ {
398 Type::Mapping(_) | Type::Tuple(_) => panic!("Mappings cannot contain mappings or tuples."),
399 Type::Identifier(identifier) => {
400 let is_record = self.composite_mapping.get(&vec![identifier.name]).unwrap();
403 assert!(!is_record, "Type checking guarantees that mappings cannot contain records.");
404 self.visit_type_with_visibility(type_, Some(AleoVisibility::Public))
405 }
406 type_ => self.visit_type_with_visibility(type_, Some(AleoVisibility::Public)),
407 }
408 };
409
410 let (key_type, key_visibility) = create_type(&mapping.key_type);
412
413 let (value_type, value_visibility) = create_type(&mapping.value_type);
415
416 self.global_mapping.insert(
418 mapping.identifier.name,
419 AleoExpr::RawName(
420 legalized_mapping_name
421 .unwrap_or_else(|| panic!("path format cannot be legalized at this point: {}", mapping.identifier)),
422 ),
423 );
424
425 AleoMapping { name, key_type, value_type, key_visibility, value_visibility }
426 }
427}