leo_passes/type_checking/
program.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::TypeCheckingVisitor;
18use crate::{VariableSymbol, VariableType};
19
20use leo_ast::{DiGraphError, Type, *};
21use leo_errors::TypeCheckerError;
22use leo_span::{Symbol, sym};
23
24use itertools::Itertools;
25use snarkvm::prelude::{CanaryV0, MainnetV0, TestnetV0};
26use std::collections::{BTreeMap, HashSet};
27
28impl ProgramVisitor for TypeCheckingVisitor<'_> {
29    fn visit_program(&mut self, input: &Program) {
30        // Typecheck the program's stubs.
31        input.stubs.iter().for_each(|(symbol, stub)| {
32            // Check that naming and ordering is consistent.
33            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        // Typecheck the modules.
45        input.modules.values().for_each(|module| self.visit_module(module));
46
47        // Typecheck the program scopes.
48        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        // Ensure that the program name is legal, i.e., it does not contain the keyword `aleo`
55        check_name(&self.state.handler, program_name, "program");
56
57        // Set the current program name.
58        self.scope_state.program_name = Some(program_name.name);
59
60        // Collect a map from record names to their spans
61        let record_info: BTreeMap<String, leo_span::Span> = input
62            .structs
63            .iter()
64            .filter(|(_, c)| c.is_record)
65            .map(|(_, r)| (r.name().to_string(), r.identifier.span))
66            .collect();
67
68        // Check if any record name is a prefix for another record name. We don't really collect all possible prefixes
69        // here but only adjacent ones. That is, if we have records `Foo`, `FooBar`, and `FooBarBaz`, we only emit
70        // errors for `Foo/FooBar` and for `FooBar/FooBarBaz` but not for `Foo/FooBarBaz`.
71        for ((prev_name, _), (curr_name, curr_span)) in record_info.iter().tuple_windows() {
72            if curr_name.starts_with(prev_name) {
73                self.state
74                    .handler
75                    .emit_err(TypeCheckerError::record_prefixed_by_other_record(curr_name, prev_name, *curr_span));
76            }
77        }
78
79        // Typecheck each const definition, and append to symbol table.
80        input.consts.iter().for_each(|(_, c)| self.visit_const(c));
81
82        // Typecheck each struct definition.
83        input.structs.iter().for_each(|(_, function)| self.visit_struct(function));
84
85        // Check that the struct dependency graph does not have any cycles.
86        if let Err(DiGraphError::CycleDetected(path)) = self.state.struct_graph.post_order() {
87            self.emit_err(TypeCheckerError::cyclic_struct_dependency(
88                path.iter().map(|p| p.iter().format("::")).collect(),
89            ));
90        }
91
92        // Typecheck each mapping definition.
93        let mut mapping_count = 0;
94        for (_, mapping) in input.mappings.iter() {
95            self.visit_mapping(mapping);
96            mapping_count += 1;
97        }
98
99        // Check that the number of mappings does not exceed the maximum.
100        if mapping_count > self.limits.max_mappings {
101            self.emit_err(TypeCheckerError::too_many_mappings(
102                self.limits.max_mappings,
103                input.program_id.name.span + input.program_id.network.span,
104            ));
105        }
106
107        // Typecheck each function definitions.
108        let mut transition_count = 0;
109        for (_, function) in input.functions.iter() {
110            self.visit_function(function);
111            if function.variant.is_transition() {
112                transition_count += 1;
113            }
114        }
115
116        // Typecheck the constructor.
117        // Note: Constructors are required for all **new** programs once they are supported in the AVM.
118        //  However, we do not require them to exist to ensure backwards compatibility with existing programs.
119        if let Some(constructor) = &input.constructor {
120            self.visit_constructor(constructor);
121        }
122
123        // Check that the call graph does not have any cycles.
124        if let Err(DiGraphError::CycleDetected(path)) = self.state.call_graph.post_order() {
125            self.emit_err(TypeCheckerError::cyclic_function_dependency(path));
126        }
127
128        // TODO: Need similar checks for structs (all in separate PR)
129        // Check that the number of transitions does not exceed the maximum.
130        if transition_count > self.limits.max_functions {
131            self.emit_err(TypeCheckerError::too_many_transitions(
132                self.limits.max_functions,
133                input.program_id.name.span + input.program_id.network.span,
134            ));
135        }
136        // Check that each program has at least one transition function.
137        // This is a snarkvm requirement.
138        else if transition_count == 0 {
139            self.emit_err(TypeCheckerError::no_transitions(input.program_id.name.span + input.program_id.network.span));
140        }
141    }
142
143    fn visit_module(&mut self, input: &Module) {
144        let parent_module = self.scope_state.module_name.clone();
145        // Set the current program name.
146        self.scope_state.program_name = Some(input.program_name);
147        self.scope_state.module_name = input.path.clone();
148
149        // Typecheck each const definition, and append to symbol table.
150        input.consts.iter().for_each(|(_, c)| self.visit_const(c));
151
152        // Typecheck each struct definition.
153        input.structs.iter().for_each(|(_, function)| self.visit_struct(function));
154
155        for (_, function) in input.functions.iter() {
156            self.visit_function(function);
157        }
158
159        self.scope_state.module_name = parent_module;
160    }
161
162    fn visit_stub(&mut self, input: &Stub) {
163        // Set the scope state.
164        self.scope_state.program_name = Some(input.stub_id.name.name);
165        self.scope_state.is_stub = true;
166
167        // Cannot have constant declarations in stubs.
168        if !input.consts.is_empty() {
169            self.emit_err(TypeCheckerError::stubs_cannot_have_const_declarations(input.consts.first().unwrap().1.span));
170        }
171
172        // Typecheck the program's structs.
173        input.structs.iter().for_each(|(_, function)| self.visit_struct_stub(function));
174
175        // Typecheck the program's functions.
176        input.functions.iter().for_each(|(_, function)| self.visit_function_stub(function));
177    }
178
179    fn visit_struct(&mut self, input: &Composite) {
180        self.in_conditional_scope(|slf| {
181            slf.in_scope(input.id, |slf| {
182                if input.is_record && !input.const_parameters.is_empty() {
183                    slf.emit_err(TypeCheckerError::unexpected_record_const_parameters(input.span));
184                } else {
185                    input
186                        .const_parameters
187                        .iter()
188                        .for_each(|const_param| slf.insert_symbol_conditional_scope(const_param.identifier.name));
189
190                    for const_param in &input.const_parameters {
191                        slf.visit_type(const_param.type_());
192
193                        // Restrictions for const parameters
194                        if !matches!(
195                            const_param.type_(),
196                            Type::Boolean | Type::Integer(_) | Type::Address | Type::Scalar | Type::Group | Type::Field
197                        ) {
198                            slf.emit_err(TypeCheckerError::bad_const_generic_type(
199                                const_param.type_(),
200                                const_param.span(),
201                            ));
202                        }
203
204                        // Add the input to the symbol table.
205                        if let Err(err) = slf.state.symbol_table.insert_variable(
206                            slf.scope_state.program_name.unwrap(),
207                            &[const_param.identifier().name],
208                            VariableSymbol {
209                                type_: const_param.type_().clone(),
210                                span: const_param.identifier.span(),
211                                declaration: VariableType::ConstParameter,
212                            },
213                        ) {
214                            slf.state.handler.emit_err(err);
215                        }
216
217                        // Add the input to the type table.
218                        slf.state.type_table.insert(const_param.identifier().id(), const_param.type_().clone());
219                    }
220                }
221
222                input.members.iter().for_each(|member| slf.visit_type(&member.type_));
223            })
224        });
225
226        // Check for conflicting struct/record member names.
227        let mut used = HashSet::new();
228        for Member { identifier, type_, span, .. } in &input.members {
229            // Check that the member types are defined.
230            self.assert_type_is_valid(type_, *span);
231
232            if !used.insert(identifier.name) {
233                self.emit_err(if input.is_record {
234                    TypeCheckerError::duplicate_record_variable(input.name(), *span)
235                } else {
236                    TypeCheckerError::duplicate_struct_member(input.name(), *span)
237                });
238            }
239        }
240
241        // For records, enforce presence of the `owner: Address` member.
242        if input.is_record {
243            // Ensure that the record name is legal, i.e., it does not contain the keyword `aleo`
244            check_name(&self.state.handler, input.identifier, "record");
245
246            // Ensure that the names of the record entries are all legal, i.e., they do not contain the
247            // keyword `aleo`
248            input.members.iter().for_each(|member| {
249                check_name(&self.state.handler, member.identifier, "record entry");
250            });
251
252            let check_has_field =
253                |need, expected_ty: Type| match input.members.iter().find_map(|Member { identifier, type_, .. }| {
254                    (identifier.name == need).then_some((identifier, type_))
255                }) {
256                    Some((_, actual_ty)) if expected_ty.eq_flat_relaxed(actual_ty) => {} // All good, found + right type!
257                    Some((field, _)) => {
258                        self.emit_err(TypeCheckerError::record_var_wrong_type(field, expected_ty, input.span()));
259                    }
260                    None => {
261                        self.emit_err(TypeCheckerError::required_record_variable(need, expected_ty, input.span()));
262                    }
263                };
264            check_has_field(sym::owner, Type::Address);
265
266            for Member { identifier, type_, span, .. } in input.members.iter() {
267                if self.contains_optional_type(type_) {
268                    self.emit_err(TypeCheckerError::record_field_cannot_be_optional(identifier, type_, *span));
269                }
270            }
271        }
272        // For structs, check that there is at least one member.
273        else if input.members.is_empty() {
274            self.emit_err(TypeCheckerError::empty_struct(input.span()));
275        }
276
277        if !(input.is_record && self.scope_state.is_stub) {
278            for Member { mode, identifier, type_, span, .. } in input.members.iter() {
279                // Check that the member type is not a tuple.
280                if matches!(type_, Type::Tuple(_)) {
281                    self.emit_err(TypeCheckerError::composite_data_type_cannot_contain_tuple(
282                        if input.is_record { "record" } else { "struct" },
283                        identifier.span,
284                    ));
285                } else if matches!(type_, Type::Future(..)) {
286                    self.emit_err(TypeCheckerError::composite_data_type_cannot_contain_future(
287                        if input.is_record { "record" } else { "struct" },
288                        identifier.span,
289                    ));
290                }
291
292                // Ensure that there are no record members.
293                self.assert_member_is_not_record(identifier.span, input.identifier.name, type_);
294                // If the member is a struct, add it to the struct dependency graph.
295                // Note that we have already checked that each member is defined and valid.
296                let composite_path = self
297                    .scope_state
298                    .module_name
299                    .iter()
300                    .cloned()
301                    .chain(std::iter::once(input.identifier.name))
302                    .collect::<Vec<Symbol>>();
303                if let Type::Composite(struct_member_type) = type_ {
304                    // Note that since there are no cycles in the program dependency graph, there are no cycles in the struct dependency graph caused by external structs.
305                    self.state.struct_graph.add_edge(composite_path, struct_member_type.path.absolute_path().to_vec());
306                } else if let Type::Array(array_type) = type_ {
307                    // Get the base element type.
308                    let base_element_type = array_type.base_element_type();
309                    // If the base element type is a struct, then add it to the struct dependency graph.
310                    if let Type::Composite(member_type) = base_element_type {
311                        self.state.struct_graph.add_edge(composite_path, member_type.path.absolute_path().to_vec());
312                    }
313                }
314
315                // If the input is a struct, then check that the member does not have a mode.
316                if !input.is_record && !matches!(mode, Mode::None) {
317                    self.emit_err(TypeCheckerError::struct_cannot_have_member_mode(*span));
318                }
319            }
320        }
321    }
322
323    fn visit_mapping(&mut self, input: &Mapping) {
324        self.visit_type(&input.key_type);
325        self.visit_type(&input.value_type);
326
327        // Check that a mapping's key type is valid.
328        self.assert_type_is_valid(&input.key_type, input.span);
329        // Check that a mapping's key type is not a future, tuple, record, or mapping.
330        match input.key_type.clone() {
331            Type::Future(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "future", input.span)),
332            Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "tuple", input.span)),
333            Type::Composite(struct_type) => {
334                if let Some(comp) = self.lookup_struct(
335                    struct_type.program.or(self.scope_state.program_name),
336                    &struct_type.path.absolute_path(),
337                ) {
338                    if comp.is_record {
339                        self.emit_err(TypeCheckerError::invalid_mapping_type("key", "record", input.span));
340                    }
341                } else {
342                    self.emit_err(TypeCheckerError::undefined_type(&input.key_type, input.span));
343                }
344            }
345            // Note that this is not possible since the parser does not currently accept mapping types.
346            Type::Mapping(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "mapping", input.span)),
347            _ => {}
348        }
349
350        if self.contains_optional_type(&input.key_type) {
351            self.emit_err(TypeCheckerError::optional_type_not_allowed_in_mapping(
352                input.key_type.clone(),
353                "key",
354                input.span,
355            ))
356        }
357
358        // Check that a mapping's value type is valid.
359        self.assert_type_is_valid(&input.value_type, input.span);
360        // Check that a mapping's value type is not a future, tuple, record or mapping.
361        match input.value_type.clone() {
362            Type::Future(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "future", input.span)),
363            Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "tuple", input.span)),
364            Type::Composite(struct_type) => {
365                if let Some(comp) = self.lookup_struct(
366                    struct_type.program.or(self.scope_state.program_name),
367                    &struct_type.path.absolute_path(),
368                ) {
369                    if comp.is_record {
370                        self.emit_err(TypeCheckerError::invalid_mapping_type("value", "record", input.span));
371                    }
372                } else {
373                    self.emit_err(TypeCheckerError::undefined_type(&input.value_type, input.span));
374                }
375            }
376            // Note that this is not possible since the parser does not currently accept mapping types.
377            Type::Mapping(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "mapping", input.span)),
378            _ => {}
379        }
380
381        if self.contains_optional_type(&input.value_type) {
382            self.emit_err(TypeCheckerError::optional_type_not_allowed_in_mapping(
383                input.value_type.clone(),
384                "value",
385                input.span,
386            ))
387        }
388    }
389
390    fn visit_function(&mut self, function: &Function) {
391        // Reset the scope state.
392        self.scope_state.reset();
393
394        // Set the scope state before traversing the function.
395        self.scope_state.variant = Some(function.variant);
396
397        // Check that the function's annotations are valid.
398        for annotation in function.annotations.iter() {
399            if !matches!(annotation.identifier.name, sym::test | sym::should_fail) {
400                self.emit_err(TypeCheckerError::unknown_annotation(annotation, annotation.span))
401            }
402        }
403
404        let get = |symbol: Symbol| -> &Annotation {
405            function.annotations.iter().find(|ann| ann.identifier.name == symbol).unwrap()
406        };
407
408        let check_annotation = |symbol: Symbol, allowed_keys: &[Symbol]| -> bool {
409            let count = function.annotations.iter().filter(|ann| ann.identifier.name == symbol).count();
410            if count > 0 {
411                let annotation = get(symbol);
412                for key in annotation.map.keys() {
413                    if !allowed_keys.contains(key) {
414                        self.emit_err(TypeCheckerError::annotation_error(
415                            format_args!("Invalid key `{key}` for annotation @{symbol}"),
416                            annotation.span,
417                        ));
418                    }
419                }
420                if count > 1 {
421                    self.emit_err(TypeCheckerError::annotation_error(
422                        format_args!("Duplicate annotation @{symbol}"),
423                        annotation.span,
424                    ));
425                }
426            }
427            count > 0
428        };
429
430        let has_test = check_annotation(sym::test, &[sym::private_key]);
431        let has_should_fail = check_annotation(sym::should_fail, &[]);
432
433        if has_test && !self.state.is_test {
434            self.emit_err(TypeCheckerError::annotation_error(
435                format_args!("Test annotation @test appears outside of tests"),
436                get(sym::test).span,
437            ));
438        }
439
440        if has_should_fail && !self.state.is_test {
441            self.emit_err(TypeCheckerError::annotation_error(
442                format_args!("Test annotation @should_fail appears outside of tests"),
443                get(sym::should_fail).span,
444            ));
445        }
446
447        if has_should_fail && !has_test {
448            self.emit_err(TypeCheckerError::annotation_error(
449                format_args!("Annotation @should_fail appears without @test"),
450                get(sym::should_fail).span,
451            ));
452        }
453
454        if has_test
455            && !self.scope_state.variant.unwrap().is_script()
456            && !self.scope_state.variant.unwrap().is_transition()
457        {
458            self.emit_err(TypeCheckerError::annotation_error(
459                format_args!("Annotation @test may appear only on scripts and transitions"),
460                get(sym::test).span,
461            ));
462        }
463
464        if (has_test) && !function.input.is_empty() {
465            self.emit_err(TypeCheckerError::annotation_error(
466                "A test procedure cannot have inputs",
467                function.input[0].span,
468            ));
469        }
470
471        self.in_conditional_scope(|slf| {
472            slf.in_scope(function.id, |slf| {
473                function
474                    .const_parameters
475                    .iter()
476                    .for_each(|const_param| slf.insert_symbol_conditional_scope(const_param.identifier.name));
477
478                function.input.iter().for_each(|input| slf.insert_symbol_conditional_scope(input.identifier.name));
479
480                // Store the name of the function.
481                slf.scope_state.function = Some(function.name());
482
483                // Query helper function to type check function parameters and outputs.
484                slf.check_function_signature(function, false);
485
486                if function.variant == Variant::Function && function.input.is_empty() {
487                    slf.emit_err(TypeCheckerError::empty_function_arglist(function.span));
488                }
489
490                slf.visit_block(&function.block);
491
492                // If the function has a return type, then check that it has a return.
493                if function.output_type != Type::Unit && !slf.scope_state.has_return {
494                    slf.emit_err(TypeCheckerError::missing_return(function.span));
495                }
496            })
497        });
498
499        // Make sure that async transitions call finalize.
500        if self.scope_state.variant == Some(Variant::AsyncTransition)
501            && !self.scope_state.has_called_finalize
502            && !self.scope_state.already_contains_an_async_block
503        {
504            self.emit_err(TypeCheckerError::missing_async_operation_in_async_transition(function.span));
505        }
506
507        self.scope_state.reset();
508    }
509
510    fn visit_constructor(&mut self, constructor: &Constructor) {
511        // Reset the scope state.
512        self.scope_state.reset();
513        // Set the scope state before traversing the constructor.
514        self.scope_state.function = Some(sym::constructor);
515        // Note: We set the variant to `AsyncFunction` since constructors have similar semantics.
516        self.scope_state.variant = Some(Variant::AsyncFunction);
517        self.scope_state.is_constructor = true;
518
519        // Get the upgrade variant.
520        // Note, `get_upgrade_variant` will return an error if the constructor is not well-formed.
521        let result = match self.state.network {
522            NetworkName::CanaryV0 => constructor.get_upgrade_variant::<CanaryV0>(),
523            NetworkName::TestnetV0 => constructor.get_upgrade_variant::<TestnetV0>(),
524            NetworkName::MainnetV0 => constructor.get_upgrade_variant::<MainnetV0>(),
525        };
526        let upgrade_variant = match result {
527            Ok(upgrade_variant) => upgrade_variant,
528            Err(e) => {
529                self.emit_err(TypeCheckerError::custom(e, constructor.span));
530                return;
531            }
532        };
533
534        // Validate the number of statements.
535        match (&upgrade_variant, constructor.block.statements.is_empty()) {
536            (UpgradeVariant::Custom, true) => {
537                self.emit_err(TypeCheckerError::custom("A 'custom' constructor cannot be empty", constructor.span));
538            }
539            (UpgradeVariant::NoUpgrade | UpgradeVariant::Admin { .. } | UpgradeVariant::Checksum { .. }, false) => {
540                self.emit_err(TypeCheckerError::custom("A 'noupgrade', 'admin', or 'checksum' constructor must be empty. The Leo compiler will insert the appropriate code.", constructor.span));
541            }
542            _ => {}
543        }
544
545        // For the checksum variant, check that the mapping exists and that the type matches.
546        if let UpgradeVariant::Checksum { mapping, key, key_type } = &upgrade_variant {
547            // Look up the mapping type.
548            let Some(VariableSymbol { type_: Type::Mapping(mapping_type), .. }) =
549                self.state.symbol_table.lookup_global(mapping)
550            else {
551                self.emit_err(TypeCheckerError::custom(
552                    format!("The mapping '{mapping}' does not exist. Please ensure that it is imported or defined in your program."),
553                    constructor.annotations[0].span,
554                ));
555                return;
556            };
557            // Check that the mapping key type matches the expected key type.
558            if *mapping_type.key != *key_type {
559                self.emit_err(TypeCheckerError::custom(
560                    format!(
561                        "The mapping '{}' key type '{}' does not match the key '{}' in the `@checksum` annotation",
562                        mapping, mapping_type.key, key
563                    ),
564                    constructor.annotations[0].span,
565                ));
566            }
567            // Check that the value type is a `[u8; 32]`.
568            let check_value_type = |type_: &Type| -> bool {
569                if let Type::Array(array_type) = type_ {
570                    if !matches!(array_type.element_type.as_ref(), &Type::Integer(_)) {
571                        return false;
572                    }
573                    if let Some(length) = array_type.length.as_u32() {
574                        return length == 32;
575                    }
576                    return false;
577                }
578                false
579            };
580            if !check_value_type(&mapping_type.value) {
581                self.emit_err(TypeCheckerError::custom(
582                    format!("The mapping '{}' value type '{}' must be a '[u8; 32]'", mapping, mapping_type.value),
583                    constructor.annotations[0].span,
584                ));
585            }
586        }
587
588        // Traverse the constructor.
589        self.in_conditional_scope(|slf| {
590            slf.in_scope(constructor.id, |slf| {
591                slf.visit_block(&constructor.block);
592            })
593        });
594
595        // Check that the constructor does not call `finalize`.
596        if self.scope_state.has_called_finalize {
597            self.emit_err(TypeCheckerError::custom("The constructor cannot call `finalize`.", constructor.span));
598        }
599
600        // Check that the constructor does not have an `async` block.
601        if self.scope_state.already_contains_an_async_block {
602            self.emit_err(TypeCheckerError::custom("The constructor cannot have an `async` block.", constructor.span));
603        }
604
605        self.scope_state.reset();
606    }
607
608    fn visit_function_stub(&mut self, input: &FunctionStub) {
609        // Must not be an inline function
610        if input.variant == Variant::Inline {
611            self.emit_err(TypeCheckerError::stub_functions_must_not_be_inlines(input.span));
612        }
613
614        // Create future stubs.
615        if input.variant == Variant::AsyncFunction {
616            let finalize_input_map = &mut self.async_function_input_types;
617            let resolved_inputs: Vec<Type> = input
618                .input
619                .iter()
620                .map(|input| {
621                    match &input.type_ {
622                        Type::Future(f) => {
623                            // Since we traverse stubs in post-order, we can assume that the corresponding finalize stub has already been traversed.
624                            Type::Future(FutureType::new(
625                                finalize_input_map.get(f.location.as_ref().unwrap()).unwrap().clone(),
626                                f.location.clone(),
627                                true,
628                            ))
629                        }
630                        _ => input.clone().type_,
631                    }
632                })
633                .collect();
634
635            finalize_input_map.insert(
636                Location::new(self.scope_state.program_name.unwrap(), vec![input.identifier.name]),
637                resolved_inputs,
638            );
639        }
640
641        // Query helper function to type check function parameters and outputs.
642        self.check_function_signature(&Function::from(input.clone()), /* is_stub */ true);
643    }
644
645    fn visit_struct_stub(&mut self, input: &Composite) {
646        self.visit_struct(input);
647    }
648}
649
650fn check_name(handler: &leo_errors::Handler, name: Identifier, item_type: &str) {
651    if name.to_string().contains(&sym::aleo.to_string()) {
652        handler.emit_err(TypeCheckerError::illegal_name(name, item_type, sym::aleo, name.span));
653    }
654}