1use super::TypeCheckingVisitor;
18use crate::{VariableSymbol, VariableType};
19
20use leo_ast::{DiGraphError, Type, *};
21use leo_errors::{Label, TypeCheckerError};
22use leo_span::{Symbol, sym};
23
24use itertools::Itertools;
25use snarkvm::prelude::{CanaryV0, MainnetV0, TestnetV0};
26use std::collections::{BTreeMap, HashMap};
27
28impl ProgramVisitor for TypeCheckingVisitor<'_> {
29 fn visit_program(&mut self, input: &Program) {
30 input.stubs.iter().for_each(|(symbol, stub)| {
32 if symbol != &stub.stub_id.name.name {
34 self.emit_err(TypeCheckerError::stub_name_mismatch(
35 symbol,
36 stub.stub_id.name,
37 stub.stub_id.network.span,
38 ));
39 }
40 self.visit_stub(stub)
41 });
42 self.scope_state.is_stub = false;
43
44 input.modules.values().for_each(|module| self.visit_module(module));
46
47 input.program_scopes.values().for_each(|scope| self.visit_program_scope(scope));
49 }
50
51 fn visit_program_scope(&mut self, input: &ProgramScope) {
52 let program_name = input.program_id.name;
53
54 self.scope_state.program_name = Some(program_name.name);
56
57 let record_info: BTreeMap<String, leo_span::Span> = input
59 .structs
60 .iter()
61 .filter(|(_, c)| c.is_record)
62 .map(|(_, r)| (r.name().to_string(), r.identifier.span))
63 .collect();
64
65 for ((prev_name, _), (curr_name, curr_span)) in record_info.iter().tuple_windows() {
69 if curr_name.starts_with(prev_name) {
70 self.state
71 .handler
72 .emit_err(TypeCheckerError::record_prefixed_by_other_record(curr_name, prev_name, *curr_span));
73 }
74 }
75
76 input.consts.iter().for_each(|(_, c)| self.visit_const(c));
78
79 input.structs.iter().for_each(|(_, function)| self.visit_struct(function));
81
82 if let Err(DiGraphError::CycleDetected(path)) = self.state.struct_graph.post_order() {
84 self.emit_err(TypeCheckerError::cyclic_struct_dependency(
85 path.iter().map(|p| p.iter().format("::")).collect(),
86 ));
87 }
88
89 let mut mapping_count = 0;
91 for (_, mapping) in input.mappings.iter() {
92 self.visit_mapping(mapping);
93 mapping_count += 1;
94 }
95
96 for (_, storage_variable) in input.storage_variables.iter() {
98 self.visit_storage_variable(storage_variable);
99 }
100
101 if mapping_count > self.limits.max_mappings {
103 self.emit_err(TypeCheckerError::too_many_mappings(
104 self.limits.max_mappings,
105 input.program_id.name.span + input.program_id.network.span,
106 ));
107 }
108
109 let mut transition_count = 0;
111 for (_, function) in input.functions.iter() {
112 self.visit_function(function);
113 if function.variant.is_transition() {
114 transition_count += 1;
115 }
116 }
117
118 if let Some(constructor) = &input.constructor {
122 self.visit_constructor(constructor);
123 }
124
125 if let Err(DiGraphError::CycleDetected(path)) = self.state.call_graph.post_order() {
127 self.emit_err(TypeCheckerError::cyclic_function_dependency(path));
128 }
129
130 if transition_count > self.limits.max_functions {
133 self.emit_err(TypeCheckerError::too_many_transitions(
134 self.limits.max_functions,
135 input.program_id.name.span + input.program_id.network.span,
136 ));
137 }
138 else if transition_count == 0 {
141 self.emit_err(TypeCheckerError::no_transitions(input.program_id.name.span + input.program_id.network.span));
142 }
143 }
144
145 fn visit_module(&mut self, input: &Module) {
146 let parent_module = self.scope_state.module_name.clone();
147 self.scope_state.program_name = Some(input.program_name);
149 self.scope_state.module_name = input.path.clone();
150
151 input.consts.iter().for_each(|(_, c)| self.visit_const(c));
153
154 input.structs.iter().for_each(|(_, function)| self.visit_struct(function));
156
157 for (_, function) in input.functions.iter() {
158 self.visit_function(function);
159 }
160
161 self.scope_state.module_name = parent_module;
162 }
163
164 fn visit_stub(&mut self, input: &Stub) {
165 self.scope_state.program_name = Some(input.stub_id.name.name);
167 self.scope_state.is_stub = true;
168
169 if !input.consts.is_empty() {
171 self.emit_err(TypeCheckerError::stubs_cannot_have_const_declarations(input.consts.first().unwrap().1.span));
172 }
173
174 input.structs.iter().for_each(|(_, function)| self.visit_struct_stub(function));
176
177 input.functions.iter().for_each(|(_, function)| self.visit_function_stub(function));
179 }
180
181 fn visit_struct(&mut self, input: &Composite) {
182 self.in_conditional_scope(|slf| {
183 slf.in_scope(input.id, |slf| {
184 if input.is_record && !input.const_parameters.is_empty() {
185 slf.emit_err(TypeCheckerError::unexpected_record_const_parameters(input.span));
186 } else {
187 input
188 .const_parameters
189 .iter()
190 .for_each(|const_param| slf.insert_symbol_conditional_scope(const_param.identifier.name));
191
192 for const_param in &input.const_parameters {
193 slf.visit_type(const_param.type_());
194
195 if !matches!(
197 const_param.type_(),
198 Type::Boolean | Type::Integer(_) | Type::Address | Type::Scalar | Type::Group | Type::Field
199 ) {
200 slf.emit_err(TypeCheckerError::bad_const_generic_type(
201 const_param.type_(),
202 const_param.span(),
203 ));
204 }
205
206 if let Err(err) = slf.state.symbol_table.insert_variable(
208 slf.scope_state.program_name.unwrap(),
209 &[const_param.identifier().name],
210 VariableSymbol {
211 type_: const_param.type_().clone(),
212 span: const_param.identifier.span(),
213 declaration: VariableType::ConstParameter,
214 },
215 ) {
216 slf.state.handler.emit_err(err);
217 }
218
219 slf.state.type_table.insert(const_param.identifier().id(), const_param.type_().clone());
221 }
222 }
223
224 input.members.iter().for_each(|member| slf.visit_type(&member.type_));
225 })
226 });
227
228 let mut used = HashMap::new();
230 for Member { identifier, type_, span, .. } in &input.members {
231 self.assert_type_is_valid(type_, *span);
233
234 if let Some(first_span) = used.get(&identifier.name) {
235 self.emit_err(if input.is_record {
236 TypeCheckerError::duplicate_record_variable(identifier.name, *span).with_labels(vec![
237 Label::new(format!("`{}` first declared here", identifier.name), *first_span)
238 .with_color(leo_errors::Color::Blue),
239 Label::new("record variable already declared", *span),
240 ])
241 } else {
242 TypeCheckerError::duplicate_struct_member(identifier.name, *span).with_labels(vec![
243 Label::new(format!("`{}` first declared here", identifier.name), *first_span)
244 .with_color(leo_errors::Color::Blue),
245 Label::new("struct field already declared", *span),
246 ])
247 });
248 } else {
249 used.insert(identifier.name, *span);
250 }
251 }
252
253 if input.is_record {
255 let check_has_field =
256 |need, expected_ty: Type| match input.members.iter().find_map(|Member { identifier, type_, .. }| {
257 (identifier.name == need).then_some((identifier, type_))
258 }) {
259 Some((_, actual_ty)) if expected_ty.eq_flat_relaxed(actual_ty) => {} Some((field, _)) => {
261 self.emit_err(TypeCheckerError::record_var_wrong_type(field, expected_ty, input.span()));
262 }
263 None => {
264 self.emit_err(TypeCheckerError::required_record_variable(need, expected_ty, input.span()));
265 }
266 };
267 check_has_field(sym::owner, Type::Address);
268
269 for Member { identifier, type_, span, .. } in input.members.iter() {
270 if self.contains_optional_type(type_) {
271 self.emit_err(TypeCheckerError::record_field_cannot_be_optional(identifier, type_, *span));
272 }
273 }
274 }
275 else if input.members.is_empty() {
277 self.emit_err(TypeCheckerError::empty_struct(input.span()));
278 } else if input.members.iter().all(|m| m.type_.is_empty()) {
279 self.emit_err(TypeCheckerError::zero_size_struct(input.span()));
280 }
281
282 if !(input.is_record && self.scope_state.is_stub) {
283 for Member { mode, identifier, type_, span, .. } in input.members.iter() {
284 if matches!(type_, Type::Tuple(_)) {
286 self.emit_err(TypeCheckerError::composite_data_type_cannot_contain_tuple(
287 if input.is_record { "record" } else { "struct" },
288 identifier.span,
289 ));
290 } else if matches!(type_, Type::Future(..)) {
291 self.emit_err(TypeCheckerError::composite_data_type_cannot_contain_future(
292 if input.is_record { "record" } else { "struct" },
293 identifier.span,
294 ));
295 }
296
297 self.assert_member_is_not_record(identifier.span, input.identifier.name, type_);
299 let composite_path = self
302 .scope_state
303 .module_name
304 .iter()
305 .cloned()
306 .chain(std::iter::once(input.identifier.name))
307 .collect::<Vec<Symbol>>();
308 if let Type::Composite(struct_member_type) = type_ {
309 self.state.struct_graph.add_edge(composite_path, struct_member_type.path.absolute_path().to_vec());
311 } else if let Type::Array(array_type) = type_ {
312 let base_element_type = array_type.base_element_type();
314 if let Type::Composite(member_type) = base_element_type {
316 self.state.struct_graph.add_edge(composite_path, member_type.path.absolute_path().to_vec());
317 }
318 }
319
320 if !input.is_record && !matches!(mode, Mode::None) {
322 self.emit_err(TypeCheckerError::struct_cannot_have_member_mode(*span));
323 }
324 }
325 }
326 }
327
328 fn visit_mapping(&mut self, input: &Mapping) {
329 self.visit_type(&input.key_type);
330 self.visit_type(&input.value_type);
331
332 self.assert_type_is_valid(&input.key_type, input.span);
334 match input.key_type.clone() {
336 Type::Future(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "future", input.span)),
337 Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "tuple", input.span)),
338 Type::Composite(struct_type) => {
339 if let Some(comp) = self.lookup_struct(
340 struct_type.program.or(self.scope_state.program_name),
341 &struct_type.path.absolute_path(),
342 ) {
343 if comp.is_record {
344 self.emit_err(TypeCheckerError::invalid_mapping_type("key", "record", input.span));
345 }
346 } else {
347 self.emit_err(TypeCheckerError::undefined_type(&input.key_type, input.span));
348 }
349 }
350 Type::Mapping(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "mapping", input.span)),
352 _ => {}
353 }
354
355 if input.key_type.is_empty() {
356 self.emit_err(TypeCheckerError::invalid_mapping_type("key", "zero sized type", input.span));
357 }
358
359 if self.contains_optional_type(&input.key_type) {
360 self.emit_err(TypeCheckerError::optional_type_not_allowed_in_mapping(
361 input.key_type.clone(),
362 "key",
363 input.span,
364 ))
365 }
366
367 self.assert_type_is_valid(&input.value_type, input.span);
369 match input.value_type.clone() {
371 Type::Future(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "future", input.span)),
372 Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "tuple", input.span)),
373 Type::Composite(struct_type) => {
374 if let Some(comp) = self.lookup_struct(
375 struct_type.program.or(self.scope_state.program_name),
376 &struct_type.path.absolute_path(),
377 ) {
378 if comp.is_record {
379 self.emit_err(TypeCheckerError::invalid_mapping_type("value", "record", input.span));
380 }
381 } else {
382 self.emit_err(TypeCheckerError::undefined_type(&input.value_type, input.span));
383 }
384 }
385 Type::Mapping(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "mapping", input.span)),
387 _ => {}
388 }
389
390 if input.value_type.is_empty() {
391 self.emit_err(TypeCheckerError::invalid_mapping_type("value", "zero sized type", input.span));
392 }
393
394 if self.contains_optional_type(&input.value_type) {
395 self.emit_err(TypeCheckerError::optional_type_not_allowed_in_mapping(
396 input.value_type.clone(),
397 "value",
398 input.span,
399 ))
400 }
401 }
402
403 fn visit_storage_variable(&mut self, input: &StorageVariable) {
404 self.visit_type(&input.type_);
405
406 let storage_type = if let Type::Vector(VectorType { element_type }) = &input.type_ {
407 *element_type.clone()
408 } else {
409 input.type_.clone()
410 };
411
412 self.assert_storage_type_is_valid(&storage_type, input.span);
413 }
414
415 fn visit_function(&mut self, function: &Function) {
416 self.scope_state.reset();
418
419 self.scope_state.variant = Some(function.variant);
421
422 for annotation in function.annotations.iter() {
424 if !matches!(annotation.identifier.name, sym::test | sym::should_fail) {
425 self.emit_err(TypeCheckerError::unknown_annotation(annotation, annotation.span))
426 }
427 }
428
429 let get = |symbol: Symbol| -> &Annotation {
430 function.annotations.iter().find(|ann| ann.identifier.name == symbol).unwrap()
431 };
432
433 let check_annotation = |symbol: Symbol, allowed_keys: &[Symbol]| -> bool {
434 let count = function.annotations.iter().filter(|ann| ann.identifier.name == symbol).count();
435 if count > 0 {
436 let annotation = get(symbol);
437 for key in annotation.map.keys() {
438 if !allowed_keys.contains(key) {
439 self.emit_err(TypeCheckerError::annotation_error(
440 format_args!("Invalid key `{key}` for annotation @{symbol}"),
441 annotation.span,
442 ));
443 }
444 }
445 if count > 1 {
446 self.emit_err(TypeCheckerError::annotation_error(
447 format_args!("Duplicate annotation @{symbol}"),
448 annotation.span,
449 ));
450 }
451 }
452 count > 0
453 };
454
455 let has_test = check_annotation(sym::test, &[sym::private_key]);
456 let has_should_fail = check_annotation(sym::should_fail, &[]);
457
458 if has_test && !self.state.is_test {
459 self.emit_err(TypeCheckerError::annotation_error(
460 format_args!("Test annotation @test appears outside of tests"),
461 get(sym::test).span,
462 ));
463 }
464
465 if has_should_fail && !self.state.is_test {
466 self.emit_err(TypeCheckerError::annotation_error(
467 format_args!("Test annotation @should_fail appears outside of tests"),
468 get(sym::should_fail).span,
469 ));
470 }
471
472 if has_should_fail && !has_test {
473 self.emit_err(TypeCheckerError::annotation_error(
474 format_args!("Annotation @should_fail appears without @test"),
475 get(sym::should_fail).span,
476 ));
477 }
478
479 if has_test
480 && !self.scope_state.variant.unwrap().is_script()
481 && !self.scope_state.variant.unwrap().is_transition()
482 {
483 self.emit_err(TypeCheckerError::annotation_error(
484 format_args!("Annotation @test may appear only on scripts and transitions"),
485 get(sym::test).span,
486 ));
487 }
488
489 if (has_test) && !function.input.is_empty() {
490 self.emit_err(TypeCheckerError::annotation_error(
491 "A test procedure cannot have inputs",
492 function.input[0].span,
493 ));
494 }
495
496 self.in_conditional_scope(|slf| {
497 slf.in_scope(function.id, |slf| {
498 function
499 .const_parameters
500 .iter()
501 .for_each(|const_param| slf.insert_symbol_conditional_scope(const_param.identifier.name));
502
503 function.input.iter().for_each(|input| slf.insert_symbol_conditional_scope(input.identifier.name));
504
505 slf.scope_state.function = Some(function.name());
507
508 slf.check_function_signature(function, false);
510
511 if function.variant == Variant::Function && function.input.is_empty() {
512 slf.emit_err(TypeCheckerError::empty_function_arglist(function.span));
513 } else if function.variant == Variant::Function && function.input.iter().all(|i| i.type_.is_empty()) {
514 slf.emit_err(TypeCheckerError::empty_function_args(function.span));
515 }
516
517 slf.visit_block(&function.block);
518
519 if function.output_type != Type::Unit && !slf.scope_state.has_return {
521 slf.emit_err(TypeCheckerError::missing_return(function.span));
522 }
523 })
524 });
525
526 if self.scope_state.variant == Some(Variant::AsyncTransition)
528 && !self.scope_state.has_called_finalize
529 && !self.scope_state.already_contains_an_async_block
530 {
531 self.emit_err(TypeCheckerError::missing_async_operation_in_async_transition(function.span));
532 }
533
534 self.scope_state.reset();
535 }
536
537 fn visit_constructor(&mut self, constructor: &Constructor) {
538 self.scope_state.reset();
540 self.scope_state.function = Some(sym::constructor);
542 self.scope_state.variant = Some(Variant::AsyncFunction);
544 self.scope_state.is_constructor = true;
545
546 let result = match self.state.network {
549 NetworkName::CanaryV0 => constructor.get_upgrade_variant::<CanaryV0>(),
550 NetworkName::TestnetV0 => constructor.get_upgrade_variant::<TestnetV0>(),
551 NetworkName::MainnetV0 => constructor.get_upgrade_variant::<MainnetV0>(),
552 };
553 let upgrade_variant = match result {
554 Ok(upgrade_variant) => upgrade_variant,
555 Err(e) => {
556 self.emit_err(TypeCheckerError::custom(e, constructor.span));
557 return;
558 }
559 };
560
561 match (&upgrade_variant, constructor.block.statements.is_empty()) {
563 (UpgradeVariant::Custom, true) => {
564 self.emit_err(TypeCheckerError::custom("A 'custom' constructor cannot be empty", constructor.span));
565 }
566 (UpgradeVariant::NoUpgrade | UpgradeVariant::Admin { .. } | UpgradeVariant::Checksum { .. }, false) => {
567 self.emit_err(TypeCheckerError::custom("A 'noupgrade', 'admin', or 'checksum' constructor must be empty. The Leo compiler will insert the appropriate code.", constructor.span));
568 }
569 _ => {}
570 }
571
572 if let UpgradeVariant::Checksum { mapping, key, key_type } = &upgrade_variant {
574 let Some(VariableSymbol { type_: Type::Mapping(mapping_type), .. }) =
576 self.state.symbol_table.lookup_global(mapping)
577 else {
578 self.emit_err(TypeCheckerError::custom(
579 format!("The mapping '{mapping}' does not exist. Please ensure that it is imported or defined in your program."),
580 constructor.annotations[0].span,
581 ));
582 return;
583 };
584 if *mapping_type.key != *key_type {
586 self.emit_err(TypeCheckerError::custom(
587 format!(
588 "The mapping '{}' key type '{}' does not match the key '{}' in the `@checksum` annotation",
589 mapping, mapping_type.key, key
590 ),
591 constructor.annotations[0].span,
592 ));
593 }
594 let check_value_type = |type_: &Type| -> bool {
596 if let Type::Array(array_type) = type_ {
597 if !matches!(array_type.element_type.as_ref(), &Type::Integer(_)) {
598 return false;
599 }
600 if let Some(length) = array_type.length.as_u32() {
601 return length == 32;
602 }
603 return false;
604 }
605 false
606 };
607 if !check_value_type(&mapping_type.value) {
608 self.emit_err(TypeCheckerError::custom(
609 format!("The mapping '{}' value type '{}' must be a '[u8; 32]'", mapping, mapping_type.value),
610 constructor.annotations[0].span,
611 ));
612 }
613 }
614
615 self.in_conditional_scope(|slf| {
617 slf.in_scope(constructor.id, |slf| {
618 slf.visit_block(&constructor.block);
619 })
620 });
621
622 if self.scope_state.has_called_finalize {
624 self.emit_err(TypeCheckerError::custom("The constructor cannot call `finalize`.", constructor.span));
625 }
626
627 if self.scope_state.already_contains_an_async_block {
629 self.emit_err(TypeCheckerError::custom("The constructor cannot have an `async` block.", constructor.span));
630 }
631
632 self.scope_state.reset();
633 }
634
635 fn visit_function_stub(&mut self, input: &FunctionStub) {
636 if input.variant == Variant::Inline {
638 self.emit_err(TypeCheckerError::stub_functions_must_not_be_inlines(input.span));
639 }
640
641 if input.variant == Variant::AsyncFunction {
643 let finalize_input_map = &mut self.async_function_input_types;
644 let resolved_inputs: Vec<Type> = input
645 .input
646 .iter()
647 .map(|input| {
648 match &input.type_ {
649 Type::Future(f) => {
650 Type::Future(FutureType::new(
652 finalize_input_map.get(f.location.as_ref().unwrap()).unwrap().clone(),
653 f.location.clone(),
654 true,
655 ))
656 }
657 _ => input.clone().type_,
658 }
659 })
660 .collect();
661
662 finalize_input_map.insert(
663 Location::new(self.scope_state.program_name.unwrap(), vec![input.identifier.name]),
664 resolved_inputs,
665 );
666 }
667
668 self.check_function_signature(&Function::from(input.clone()), true);
670 }
671
672 fn visit_struct_stub(&mut self, input: &Composite) {
673 self.visit_struct(input);
674 }
675}