leo_passes/type_checking/
ast.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::*;
18use crate::{VariableSymbol, VariableType};
19
20use leo_ast::{
21    Type::{Future, Tuple},
22    *,
23};
24use leo_errors::{TypeCheckerError, TypeCheckerWarning};
25use leo_span::{Span, Symbol, sym};
26
27use itertools::Itertools as _;
28
29impl TypeCheckingVisitor<'_> {
30    // Returns the type of the RHS of an assign statement. Also returns whether the RHS is a storage location.
31    // For now, the only possible storage location to assign to is a `Path` to a storage variable name.
32    pub fn visit_expression_assign(&mut self, input: &Expression) -> (Type, bool) {
33        let (ty, is_storage) = match input {
34            Expression::ArrayAccess(array_access) => {
35                (self.visit_array_access_general(array_access, true, &None), false)
36            }
37            Expression::Path(path) if path.qualifier().is_empty() => self.visit_path_assign(path),
38            Expression::MemberAccess(member_access) => {
39                (self.visit_member_access_general(member_access, true, &None), false)
40            }
41            Expression::TupleAccess(tuple_access) => {
42                (self.visit_tuple_access_general(tuple_access, true, &None), false)
43            }
44            _ => {
45                self.emit_err(TypeCheckerError::invalid_assignment_target(input, input.span()));
46                (Type::Err, false)
47            }
48        };
49
50        // Prohibit assignment to an external record in a narrower conditional scope.
51        let external_record = self.is_external_record(&ty);
52        let external_record_tuple =
53            matches!(&ty, Type::Tuple(tuple) if tuple.elements().iter().any(|ty| self.is_external_record(ty)));
54
55        if external_record || external_record_tuple {
56            let Expression::Path(path) = input else {
57                // This is not valid Leo and will have triggered an error elsewhere.
58                return (Type::Err, false);
59            };
60
61            if !self.symbol_in_conditional_scope(path.identifier().name) {
62                if external_record {
63                    self.emit_err(TypeCheckerError::assignment_to_external_record_cond(&ty, input.span()));
64                } else {
65                    // Note that this will cover both assigning to a tuple variable and assigning to a member of a tuple.
66                    self.emit_err(TypeCheckerError::assignment_to_external_record_tuple_cond(&ty, input.span()));
67                }
68            }
69        }
70
71        // Prohibit reassignment of futures.
72        if let Type::Future(..) = ty {
73            self.emit_err(TypeCheckerError::cannot_reassign_future_variable(input, input.span()));
74        }
75
76        // Prohibit reassignment of mappings.
77        if let Type::Mapping(_) = ty {
78            self.emit_err(TypeCheckerError::cannot_reassign_mapping(input, input.span()));
79        }
80
81        // Add the expression and its associated type to the type table.
82        self.state.type_table.insert(input.id(), ty.clone());
83
84        (ty, is_storage)
85    }
86
87    pub fn visit_array_access_general(&mut self, input: &ArrayAccess, assign: bool, expected: &Option<Type>) -> Type {
88        // Check that the expression is an array.
89        let this_type = if assign {
90            self.visit_expression_assign(&input.array).0
91        } else {
92            self.visit_expression(&input.array, &None)
93        };
94        self.assert_array_type(&this_type, input.array.span());
95
96        // Check that the index is an integer type.
97        let mut index_type = self.visit_expression(&input.index, &None);
98
99        if index_type == Type::Numeric {
100            // If the index has type `Numeric`, then it's an unsuffixed literal. Just infer its type to be `u32` and
101            // then check it's validity as a `u32`.
102            index_type = Type::Integer(IntegerType::U32);
103            if let Expression::Literal(literal) = &input.index {
104                self.check_numeric_literal(literal, &index_type);
105            }
106        }
107
108        self.assert_int_type(&index_type, input.index.span());
109
110        // Keep track of the type of the index in the type table.
111        // This is important for when the index is an unsuffixed literal.
112        self.state.type_table.insert(input.index.id(), index_type.clone());
113
114        // Get the element type of the array.
115        let Type::Array(array_type) = this_type else {
116            // We must have already reported an error above, in our type assertion.
117            return Type::Err;
118        };
119
120        let element_type = array_type.element_type();
121
122        // If the expected type is known, then check that the element type is the same as the expected type.
123        self.maybe_assert_type(element_type, expected, input.span());
124
125        // Return the element type of the array.
126        element_type.clone()
127    }
128
129    pub fn visit_member_access_general(&mut self, input: &MemberAccess, assign: bool, expected: &Option<Type>) -> Type {
130        // Handler member access expressions that correspond to valid operands in AVM code.
131        if !assign {
132            match &input.inner {
133                // If the access expression is of the form `self.<name>`, then check the <name> is valid.
134                Expression::Path(path) if path.identifier().name == sym::SelfLower => {
135                    match input.name.name {
136                        sym::address => {
137                            let ty = Type::Address;
138                            self.maybe_assert_type(&ty, expected, input.span());
139                            return ty;
140                        }
141                        sym::caller => {
142                            // Check that the operation is not invoked in a `finalize` block.
143                            self.check_access_allowed("self.caller", false, input.name.span());
144
145                            let ty = Type::Address;
146                            self.maybe_assert_type(&ty, expected, input.span());
147                            return ty;
148                        }
149                        sym::checksum => {
150                            let ty = Type::Array(ArrayType::new(
151                                Type::Integer(IntegerType::U8),
152                                Expression::Literal(Literal::integer(
153                                    IntegerType::U8,
154                                    "32".to_string(),
155                                    Default::default(),
156                                    Default::default(),
157                                )),
158                            ));
159                            self.maybe_assert_type(&ty, expected, input.span());
160                            return ty;
161                        }
162                        sym::edition => {
163                            let ty = Type::Integer(IntegerType::U16);
164                            self.maybe_assert_type(&ty, expected, input.span());
165                            return ty;
166                        }
167                        sym::id => {
168                            let ty = Type::Address;
169                            self.maybe_assert_type(&ty, expected, input.span());
170                            return ty;
171                        }
172                        sym::program_owner => {
173                            // Check that the operation is only invoked in a `finalize` block.
174                            self.check_access_allowed("self.program_owner", true, input.name.span());
175
176                            let ty = Type::Address;
177                            self.maybe_assert_type(&ty, expected, input.span());
178                            return ty;
179                        }
180                        sym::signer => {
181                            // Check that operation is not invoked in a `finalize` block.
182                            self.check_access_allowed("self.signer", false, input.name.span());
183
184                            let ty = Type::Address;
185                            self.maybe_assert_type(&ty, expected, input.span());
186                            return ty;
187                        }
188                        _ => {
189                            self.emit_err(TypeCheckerError::invalid_self_access(input.name.span()));
190                            return Type::Err;
191                        }
192                    }
193                }
194                // If the access expression is of the form `block.<name>`, then check the <name> is valid.
195                Expression::Path(path) if path.identifier().name == sym::block => match input.name.name {
196                    sym::height => {
197                        // Check that the operation is invoked in a `finalize` block.
198                        self.check_access_allowed("block.height", true, input.name.span());
199                        let ty = Type::Integer(IntegerType::U32);
200                        self.maybe_assert_type(&ty, expected, input.span());
201                        return ty;
202                    }
203                    _ => {
204                        self.emit_err(TypeCheckerError::invalid_block_access(input.name.span()));
205                        return Type::Err;
206                    }
207                },
208                // If the access expression is of the form `network.<name>`, then check that the <name> is valid.
209                Expression::Path(path) if path.identifier().name == sym::network => match input.name.name {
210                    sym::id => {
211                        // Check that the operation is not invoked outside a `finalize` block.
212                        self.check_access_allowed("network.id", true, input.name.span());
213                        let ty = Type::Integer(IntegerType::U16);
214                        self.maybe_assert_type(&ty, expected, input.span());
215                        return ty;
216                    }
217                    _ => {
218                        self.emit_err(TypeCheckerError::invalid_block_access(input.name.span()));
219                        return Type::Err;
220                    }
221                },
222                _ => {}
223            }
224        }
225
226        let ty = if assign {
227            self.visit_expression_assign(&input.inner).0
228        } else {
229            self.visit_expression(&input.inner, &None)
230        };
231
232        // Make sure we're not assigning to a member of an external record.
233        if assign && self.is_external_record(&ty) {
234            self.emit_err(TypeCheckerError::assignment_to_external_record_member(&ty, input.span));
235        }
236
237        // Check that the type of `inner` in `inner.name` is a struct.
238        match ty {
239            Type::Err => Type::Err,
240            Type::Composite(ref struct_) => {
241                // Retrieve the struct definition associated with `identifier`.
242                let Some(struct_) = self
243                    .lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
244                else {
245                    self.emit_err(TypeCheckerError::undefined_type(ty, input.inner.span()));
246                    return Type::Err;
247                };
248                // Check that `input.name` is a member of the struct.
249                match struct_.members.iter().find(|member| member.name() == input.name.name) {
250                    // Case where `input.name` is a member of the struct.
251                    Some(Member { type_, .. }) => {
252                        // Check that the type of `input.name` is the same as `expected`.
253                        self.maybe_assert_type(type_, expected, input.span());
254                        type_.clone()
255                    }
256                    // Case where `input.name` is not a member of the struct.
257                    None => {
258                        self.emit_err(TypeCheckerError::invalid_struct_variable(
259                            input.name,
260                            &struct_,
261                            input.name.span(),
262                        ));
263                        Type::Err
264                    }
265                }
266            }
267            type_ => {
268                self.emit_err(TypeCheckerError::type_should_be2(type_, "a struct or record", input.inner.span()));
269                Type::Err
270            }
271        }
272    }
273
274    pub fn visit_tuple_access_general(&mut self, input: &TupleAccess, assign: bool, expected: &Option<Type>) -> Type {
275        let this_type = if assign {
276            self.visit_expression_assign(&input.tuple).0
277        } else {
278            self.visit_expression(&input.tuple, &None)
279        };
280        match this_type {
281            Type::Err => Type::Err,
282            Type::Tuple(tuple) => {
283                // Check out of range input.
284                let index = input.index.value();
285                let Some(actual) = tuple.elements().get(index) else {
286                    self.emit_err(TypeCheckerError::tuple_out_of_range(index, tuple.length(), input.span()));
287                    return Type::Err;
288                };
289
290                self.maybe_assert_type(actual, expected, input.span());
291
292                actual.clone()
293            }
294            Type::Future(_) => {
295                // Get the fully inferred type.
296                let Some(Type::Future(inferred_f)) = self.state.type_table.get(&input.tuple.id()) else {
297                    // If a future type was not inferred, we will have already reported an error.
298                    return Type::Err;
299                };
300
301                if inferred_f.location.is_none() {
302                    // This generally means that the `Future` is produced by an `async` block expression and not an
303                    // `async function` function call.
304                    self.emit_err(TypeCheckerError::invalid_async_block_future_access(input.span()));
305                    return Type::Err;
306                }
307
308                let Some(actual) = inferred_f.inputs().get(input.index.value()) else {
309                    self.emit_err(TypeCheckerError::invalid_future_access(
310                        input.index.value(),
311                        inferred_f.inputs().len(),
312                        input.span(),
313                    ));
314                    return Type::Err;
315                };
316
317                // If all inferred types weren't the same, the member will be of type `Type::Err`.
318                if let Type::Err = actual {
319                    self.emit_err(TypeCheckerError::future_error_member(input.index.value(), input.span()));
320                    return Type::Err;
321                }
322
323                self.maybe_assert_type(actual, expected, input.span());
324
325                actual.clone()
326            }
327            type_ => {
328                self.emit_err(TypeCheckerError::type_should_be2(type_, "a tuple or future", input.span()));
329                Type::Err
330            }
331        }
332    }
333
334    // Returns the type of the RHS of an assign statement if it's a `Path`.
335    // Also returns whether the RHS is a storage location.
336    pub fn visit_path_assign(&mut self, input: &Path) -> (Type, bool) {
337        // Lookup the variable in the symbol table and retrieve its type.
338        let Some(var) =
339            self.state.symbol_table.lookup_path(self.scope_state.program_name.unwrap(), &input.absolute_path())
340        else {
341            self.emit_err(TypeCheckerError::unknown_sym("variable", input, input.span));
342            return (Type::Err, false);
343        };
344
345        // Cannot assign to storage vectors
346        if var.type_.is_vector() {
347            self.emit_err(TypeCheckerError::invalid_assignment_target(input, input.span()));
348            return (Type::Err, false);
349        }
350
351        // If the variable exists, then check that it is not a constant.
352        match &var.declaration {
353            VariableType::Const => self.emit_err(TypeCheckerError::cannot_assign_to_const_var(input, var.span)),
354            VariableType::ConstParameter => {
355                self.emit_err(TypeCheckerError::cannot_assign_to_generic_const_function_parameter(input, input.span))
356            }
357            VariableType::Input(Mode::Constant) => {
358                self.emit_err(TypeCheckerError::cannot_assign_to_const_input(input, var.span))
359            }
360            VariableType::Storage => return (var.type_.clone(), true),
361            VariableType::Mut | VariableType::Input(_) => {}
362        }
363
364        // If the variable exists and it's in an async function, then check that it is in the current conditional scope.
365        if self.scope_state.variant.unwrap().is_async_function()
366            && !self.symbol_in_conditional_scope(input.identifier().name)
367        {
368            self.emit_err(TypeCheckerError::async_cannot_assign_outside_conditional(input, "function", var.span));
369        }
370
371        // Similarly, if the variable exists and it's in an async block, then check that it is in the current conditional scope.
372        if self.async_block_id.is_some() && !self.symbol_in_conditional_scope(input.identifier().name) {
373            self.emit_err(TypeCheckerError::async_cannot_assign_outside_conditional(input, "block", var.span));
374        }
375
376        if let Some(async_block_id) = self.async_block_id
377            && !self.state.symbol_table.is_defined_in_scope_or_ancestor_until(async_block_id, input.identifier().name)
378        {
379            // If we're inside an async block (i.e. in the scope of its block or one if its child scopes) and if
380            // we're trying to assign to a variable that is not local to the block (or its child scopes), then we
381            // should error out.
382            self.emit_err(TypeCheckerError::cannot_assign_to_vars_outside_async_block(
383                input.identifier().name,
384                input.span,
385            ));
386        }
387
388        (var.type_.clone(), false)
389    }
390
391    /// Infers the type of an expression, but returns Type::Err and emits an error if the result is Type::Numeric.
392    /// Used to disallow numeric types in specific contexts where they are not valid or expected.
393    pub(crate) fn visit_expression_reject_numeric(&mut self, expr: &Expression, expected: &Option<Type>) -> Type {
394        let mut inferred = self.visit_expression(expr, expected);
395        match inferred {
396            Type::Numeric => {
397                self.emit_inference_failure_error(&mut inferred, expr);
398                Type::Err
399            }
400            _ => inferred,
401        }
402    }
403
404    /// Infers the type of an expression, and if it is `Type::Numeric`, coerces it to `U32`, validates it, and
405    /// records it in the type table.
406    pub(crate) fn visit_expression_infer_default_u32(&mut self, expr: &Expression) -> Type {
407        let mut inferred = self.visit_expression(expr, &None);
408
409        if inferred == Type::Numeric {
410            inferred = Type::Integer(IntegerType::U32);
411
412            if let Expression::Literal(literal) = expr
413                && !self.check_numeric_literal(literal, &inferred)
414            {
415                inferred = Type::Err;
416            }
417
418            self.state.type_table.insert(expr.id(), inferred.clone());
419        }
420
421        inferred
422    }
423}
424
425impl AstVisitor for TypeCheckingVisitor<'_> {
426    type AdditionalInput = Option<Type>;
427    type Output = Type;
428
429    /* Types */
430    fn visit_array_type(&mut self, input: &ArrayType) {
431        self.visit_type(&input.element_type);
432        self.visit_expression_infer_default_u32(&input.length);
433    }
434
435    fn visit_composite_type(&mut self, input: &CompositeType) {
436        let struct_ = self.lookup_struct(self.scope_state.program_name, &input.path.absolute_path()).clone();
437
438        if let Some(struct_) = struct_ {
439            // Check the number of const arguments against the number of the struct's const parameters
440            if struct_.const_parameters.len() != input.const_arguments.len() {
441                self.emit_err(TypeCheckerError::incorrect_num_const_args(
442                    "Struct type",
443                    struct_.const_parameters.len(),
444                    input.const_arguments.len(),
445                    input.path.span,
446                ));
447            }
448
449            // Check the types of const arguments against the types of the struct's const parameters
450            for (expected, argument) in struct_.const_parameters.iter().zip(input.const_arguments.iter()) {
451                self.visit_expression(argument, &Some(expected.type_().clone()));
452            }
453        } else if !input.const_arguments.is_empty() {
454            self.emit_err(TypeCheckerError::unexpected_const_args(input, input.path.span));
455        }
456    }
457
458    /* Expressions */
459    fn visit_expression(&mut self, input: &Expression, additional: &Self::AdditionalInput) -> Self::Output {
460        let output = match input {
461            Expression::Array(array) => self.visit_array(array, additional),
462            Expression::ArrayAccess(access) => self.visit_array_access_general(access, false, additional),
463            Expression::AssociatedConstant(constant) => self.visit_associated_constant(constant, additional),
464            Expression::AssociatedFunction(function) => self.visit_associated_function(function, additional),
465            Expression::Async(async_) => self.visit_async(async_, additional),
466            Expression::Binary(binary) => self.visit_binary(binary, additional),
467            Expression::Call(call) => self.visit_call(call, additional),
468            Expression::Cast(cast) => self.visit_cast(cast, additional),
469            Expression::Struct(struct_) => self.visit_struct_init(struct_, additional),
470            Expression::Err(err) => self.visit_err(err, additional),
471            Expression::Path(path) => self.visit_path(path, additional),
472            Expression::Literal(literal) => self.visit_literal(literal, additional),
473            Expression::Locator(locator) => self.visit_locator(locator, additional),
474            Expression::MemberAccess(access) => self.visit_member_access_general(access, false, additional),
475            Expression::Repeat(repeat) => self.visit_repeat(repeat, additional),
476            Expression::Ternary(ternary) => self.visit_ternary(ternary, additional),
477            Expression::Tuple(tuple) => self.visit_tuple(tuple, additional),
478            Expression::TupleAccess(access) => self.visit_tuple_access_general(access, false, additional),
479            Expression::Unary(unary) => self.visit_unary(unary, additional),
480            Expression::Unit(unit) => self.visit_unit(unit, additional),
481        };
482
483        // Add the expression and its associated type to the symbol table.
484        self.state.type_table.insert(input.id(), output.clone());
485        output
486    }
487
488    fn visit_array_access(&mut self, _input: &ArrayAccess, _additional: &Self::AdditionalInput) -> Self::Output {
489        panic!("Should not be called.");
490    }
491
492    fn visit_member_access(&mut self, _input: &MemberAccess, _additional: &Self::AdditionalInput) -> Self::Output {
493        panic!("Should not be called.");
494    }
495
496    fn visit_tuple_access(&mut self, _input: &TupleAccess, _additional: &Self::AdditionalInput) -> Self::Output {
497        panic!("Should not be called.");
498    }
499
500    fn visit_array(&mut self, input: &ArrayExpression, additional: &Self::AdditionalInput) -> Self::Output {
501        if input.elements.is_empty() {
502            self.emit_err(TypeCheckerError::array_empty(input.span()));
503            return Type::Err;
504        }
505
506        // Grab the element type from the expected type if the expected type is an array or if it's
507        // an optional array
508        let element_type = match additional {
509            Some(Type::Array(array_ty)) => Some(array_ty.element_type().clone()),
510            Some(Type::Optional(opt)) => match &*opt.inner {
511                Type::Array(array_ty) => Some(array_ty.element_type().clone()),
512                _ => None,
513            },
514            _ => None,
515        };
516
517        let inferred_type = self.visit_expression_reject_numeric(&input.elements[0], &element_type);
518
519        if input.elements.len() > self.limits.max_array_elements {
520            self.emit_err(TypeCheckerError::array_too_large(
521                input.elements.len(),
522                self.limits.max_array_elements,
523                input.span(),
524            ));
525        }
526
527        for expression in input.elements[1..].iter() {
528            let next_type = self.visit_expression_reject_numeric(expression, &element_type);
529
530            if next_type == Type::Err {
531                return Type::Err;
532            }
533
534            if let Some(ref element_type) = element_type {
535                self.assert_type(&next_type, element_type, expression.span());
536            } else {
537                self.assert_type(&next_type, &inferred_type, expression.span());
538            }
539        }
540
541        if inferred_type == Type::Err {
542            return Type::Err;
543        }
544
545        let type_ = Type::Array(ArrayType::new(
546            inferred_type,
547            Expression::Literal(Literal {
548                // The default type for array length is `U32`.
549                variant: LiteralVariant::Integer(IntegerType::U32, input.elements.len().to_string()),
550                id: self.state.node_builder.next_id(),
551                span: Span::default(),
552            }),
553        ));
554
555        self.maybe_assert_type(&type_, additional, input.span());
556
557        type_
558    }
559
560    fn visit_repeat(&mut self, input: &RepeatExpression, additional: &Self::AdditionalInput) -> Self::Output {
561        // Grab the element type from the expected type if the expected type is an array or if it's
562        // an optional array
563        let expected_element_type = match additional {
564            Some(Type::Array(array_ty)) => Some(array_ty.element_type().clone()),
565            Some(Type::Optional(opt)) => match &*opt.inner {
566                Type::Array(array_ty) => Some(array_ty.element_type().clone()),
567                _ => None,
568            },
569            _ => None,
570        };
571
572        let inferred_element_type = self.visit_expression_reject_numeric(&input.expr, &expected_element_type);
573
574        // Now infer the type of `count`. If it's an unsuffixed literal (i.e. has `Type::Numeric`), then infer it to be
575        // a `U32` as the default type.
576        self.visit_expression_infer_default_u32(&input.count);
577
578        // If we can already evaluate the repeat count as a `u32`, then make sure it's not 0 or  greater than the array
579        // size limit.
580        if let Some(count) = input.count.as_u32() {
581            if count == 0 {
582                self.emit_err(TypeCheckerError::array_empty(input.span()));
583                return Type::Err;
584            }
585
586            if count > self.limits.max_array_elements as u32 {
587                self.emit_err(TypeCheckerError::array_too_large(count, self.limits.max_array_elements, input.span()));
588            }
589        }
590
591        let type_ = Type::Array(ArrayType::new(inferred_element_type, input.count.clone()));
592
593        self.maybe_assert_type(&type_, additional, input.span());
594        type_
595    }
596
597    fn visit_associated_constant(
598        &mut self,
599        input: &AssociatedConstantExpression,
600        expected: &Self::AdditionalInput,
601    ) -> Self::Output {
602        // Check associated constant type and constant name
603        let Some(core_constant) = self.get_core_constant(&input.ty, &input.name) else {
604            self.emit_err(TypeCheckerError::invalid_associated_constant(input, input.span));
605            return Type::Err;
606        };
607        let type_ = core_constant.to_type();
608        self.maybe_assert_type(&type_, expected, input.span());
609        type_
610    }
611
612    fn visit_associated_function(
613        &mut self,
614        input: &AssociatedFunctionExpression,
615        expected: &Self::AdditionalInput,
616    ) -> Self::Output {
617        // Check core struct name and function.
618        let Some(core_instruction) = self.get_core_function_call(input) else {
619            self.emit_err(TypeCheckerError::invalid_core_function_call(input, input.span()));
620            return Type::Err;
621        };
622        // Check that operation is not restricted to finalize blocks.
623        if !matches!(self.scope_state.variant, Some(Variant::AsyncFunction) | Some(Variant::Script))
624            && self.async_block_id.is_none()
625            && core_instruction.is_finalize_command()
626        {
627            self.emit_err(TypeCheckerError::operation_must_be_in_async_block_or_function(input.span()));
628        }
629
630        let return_type =
631            self.check_core_function_call(core_instruction.clone(), &input.arguments, expected, input.span());
632
633        // Check return type if the expected type is known.
634        self.maybe_assert_type(&return_type, expected, input.span());
635
636        // Await futures here so that can use the argument variable names to lookup.
637        if core_instruction == CoreFunction::FutureAwait && input.arguments.len() != 1 {
638            self.emit_err(TypeCheckerError::can_only_await_one_future_at_a_time(input.span));
639        }
640
641        return_type
642    }
643
644    fn visit_async(&mut self, input: &AsyncExpression, _additional: &Self::AdditionalInput) -> Self::Output {
645        // Step into an async block
646        self.async_block_id = Some(input.block.id);
647
648        // A few restrictions
649        if self.scope_state.is_conditional {
650            self.emit_err(TypeCheckerError::async_block_in_conditional(input.span));
651        }
652
653        if !matches!(self.scope_state.variant, Some(Variant::AsyncTransition) | Some(Variant::Script)) {
654            self.emit_err(TypeCheckerError::illegal_async_block_location(input.span));
655        }
656
657        if self.scope_state.already_contains_an_async_block {
658            self.emit_err(TypeCheckerError::multiple_async_blocks_not_allowed(input.span));
659        }
660
661        if self.scope_state.has_called_finalize {
662            self.emit_err(TypeCheckerError::conflicting_async_call_and_block(input.span));
663        }
664
665        self.visit_block(&input.block);
666
667        // This scope now already has an async block
668        self.scope_state.already_contains_an_async_block = true;
669
670        // Step out of the async block
671        self.async_block_id = None;
672
673        // The type of the async block is just a `Future` with no `Location` (i.e. not produced by an explicit `async
674        // function`) and no inputs since we're not allowed to access inputs of a `Future` produced by an `async block.
675        Type::Future(FutureType::new(Vec::new(), None, false))
676    }
677
678    fn visit_binary(&mut self, input: &BinaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
679        let assert_same_type = |slf: &Self, t1: &Type, t2: &Type| -> Type {
680            if t1 == &Type::Err || t2 == &Type::Err {
681                Type::Err
682            } else if !t1.eq_user(t2) {
683                slf.emit_err(TypeCheckerError::operation_types_mismatch(input.op, t1, t2, input.span()));
684                Type::Err
685            } else {
686                t1.clone()
687            }
688        };
689
690        // This closure attempts to resolve numeric type inference between two operands.
691        // It handles the following cases:
692        // - If both types are unknown numeric placeholders (`Numeric`), emit errors for both.
693        // - If one type is `Numeric` and the other is an error (`Err`), propagate the error.
694        // - If one type is a known numeric type and the other is `Numeric`, infer the unknown type.
695        // - If one type is `Numeric` but the other is not a valid numeric type, emit an error.
696        // - Otherwise, do nothing (types are already resolved or not subject to inference).
697        let infer_numeric_types = |slf: &Self, left_type: &mut Type, right_type: &mut Type| {
698            use Type::*;
699
700            match (&*left_type, &*right_type) {
701                // Case: Both types are unknown numeric types – cannot infer either side
702                (Numeric, Numeric) => {
703                    slf.emit_inference_failure_error(left_type, &input.left);
704                    slf.emit_inference_failure_error(right_type, &input.right);
705                }
706
707                // Case: Left is unknown numeric, right is erroneous – propagate error to left
708                (Numeric, Err) => slf.emit_inference_failure_error(left_type, &input.left),
709
710                // Case: Right is unknown numeric, left is erroneous – propagate error to right
711                (Err, Numeric) => slf.emit_inference_failure_error(right_type, &input.right),
712
713                // Case: Right type is unknown numeric, infer it from known left type
714                (Integer(_) | Field | Group | Scalar, Numeric) => {
715                    *right_type = left_type.clone();
716                    slf.state.type_table.insert(input.right.id(), right_type.clone());
717                    if let Expression::Literal(literal) = &input.right {
718                        slf.check_numeric_literal(literal, right_type);
719                    }
720                }
721
722                // Case: Left type is unknown numeric, infer it from known right type
723                (Numeric, Integer(_) | Field | Group | Scalar) => {
724                    *left_type = right_type.clone();
725                    slf.state.type_table.insert(input.left.id(), left_type.clone());
726                    if let Expression::Literal(literal) = &input.left {
727                        slf.check_numeric_literal(literal, left_type);
728                    }
729                }
730
731                // Case: Left type is numeric but right is invalid for numeric inference – error on left
732                (Numeric, _) => slf.emit_inference_failure_error(left_type, &input.left),
733
734                // Case: Right type is numeric but left is invalid for numeric inference – error on right
735                (_, Numeric) => slf.emit_inference_failure_error(right_type, &input.right),
736
737                // No inference or error needed. Rely on further operator-specific checks.
738                _ => {}
739            }
740        };
741
742        match input.op {
743            BinaryOperation::And | BinaryOperation::Or | BinaryOperation::Nand | BinaryOperation::Nor => {
744                self.maybe_assert_type(&Type::Boolean, destination, input.span());
745                self.visit_expression(&input.left, &Some(Type::Boolean));
746                self.visit_expression(&input.right, &Some(Type::Boolean));
747                Type::Boolean
748            }
749            BinaryOperation::BitwiseAnd | BinaryOperation::BitwiseOr | BinaryOperation::Xor => {
750                let operand_expected = self.unwrap_optional_type(destination);
751
752                // The expected type for both `left` and `right` is the unwrapped type
753                let mut t1 = self.visit_expression(&input.left, &operand_expected);
754                let mut t2 = self.visit_expression(&input.right, &operand_expected);
755
756                // Infer `Numeric` types if possible
757                infer_numeric_types(self, &mut t1, &mut t2);
758
759                // Now sanity check everything
760                self.assert_bool_int_type(&t1, input.left.span());
761                self.assert_bool_int_type(&t2, input.right.span());
762
763                let result_t = assert_same_type(self, &t1, &t2);
764                self.maybe_assert_type(&result_t, destination, input.span());
765
766                self.wrap_if_optional(result_t, destination)
767            }
768            BinaryOperation::Add => {
769                let operand_expected = self.unwrap_optional_type(destination);
770
771                // The expected type for both `left` and `right` is the unwrapped type
772                let mut t1 = self.visit_expression(&input.left, &operand_expected);
773                let mut t2 = self.visit_expression(&input.right, &operand_expected);
774
775                // Infer `Numeric` types if possible
776                infer_numeric_types(self, &mut t1, &mut t2);
777
778                // Now sanity check everything
779                let assert_add_type = |type_: &Type, span: Span| {
780                    if !matches!(type_, Type::Err | Type::Field | Type::Group | Type::Scalar | Type::Integer(_)) {
781                        self.emit_err(TypeCheckerError::type_should_be2(
782                            type_,
783                            "a field, group, scalar, or integer",
784                            span,
785                        ));
786                    }
787                };
788
789                assert_add_type(&t1, input.left.span());
790                assert_add_type(&t2, input.right.span());
791
792                let result_t = assert_same_type(self, &t1, &t2);
793
794                self.maybe_assert_type(&result_t, destination, input.span());
795
796                self.wrap_if_optional(result_t, destination)
797            }
798            BinaryOperation::Sub => {
799                let operand_expected = self.unwrap_optional_type(destination);
800
801                // The expected type for both `left` and `right` is the unwrapped type
802                let mut t1 = self.visit_expression(&input.left, &operand_expected);
803                let mut t2 = self.visit_expression(&input.right, &operand_expected);
804
805                // Infer `Numeric` types if possible
806                infer_numeric_types(self, &mut t1, &mut t2);
807
808                // Now sanity check everything
809                self.assert_field_group_int_type(&t1, input.left.span());
810                self.assert_field_group_int_type(&t2, input.right.span());
811
812                let result_t = assert_same_type(self, &t1, &t2);
813
814                self.maybe_assert_type(&result_t, destination, input.span());
815
816                self.wrap_if_optional(result_t, destination)
817            }
818            BinaryOperation::Mul => {
819                let unwrapped_dest = self.unwrap_optional_type(destination);
820
821                // The expected type for both `left` and `right` is the same as unwrapped destination except when it is
822                // a `Type::Group`. In that case, the two operands should be a `Type::Group` and `Type::Scalar` but we can't
823                // known which one is which.
824                let expected = if matches!(unwrapped_dest, Some(Type::Group)) { &None } else { &unwrapped_dest };
825                let mut t1 = self.visit_expression(&input.left, expected);
826                let mut t2 = self.visit_expression(&input.right, expected);
827
828                // - If one side is `Group` and the other is an unresolved `Numeric`, infer the `Numeric` as a `Scalar`,
829                //   since `Group * Scalar = Group`.
830                // - Similarly, if one side is `Scalar` and the other is `Numeric`, infer the `Numeric` as `Group`.
831                //
832                // If no special case applies, default to inferring types between `t1` and `t2` as-is.
833                match (&t1, &t2) {
834                    (Type::Group, Type::Numeric) => infer_numeric_types(self, &mut Type::Scalar, &mut t2),
835                    (Type::Numeric, Type::Group) => infer_numeric_types(self, &mut t1, &mut Type::Scalar),
836                    (Type::Scalar, Type::Numeric) => infer_numeric_types(self, &mut Type::Group, &mut t2),
837                    (Type::Numeric, Type::Scalar) => infer_numeric_types(self, &mut t1, &mut Type::Group),
838                    (_, _) => infer_numeric_types(self, &mut t1, &mut t2),
839                }
840
841                // Final sanity checks
842                let result_t = match (&t1, &t2) {
843                    (Type::Err, _) | (_, Type::Err) => Type::Err,
844                    (Type::Group, Type::Scalar) | (Type::Scalar, Type::Group) => Type::Group,
845                    (Type::Field, Type::Field) => Type::Field,
846                    (Type::Integer(integer_type1), Type::Integer(integer_type2)) if integer_type1 == integer_type2 => {
847                        t1.clone()
848                    }
849                    _ => {
850                        self.emit_err(TypeCheckerError::mul_types_mismatch(t1, t2, input.span()));
851                        Type::Err
852                    }
853                };
854
855                self.maybe_assert_type(&result_t, destination, input.span());
856
857                self.wrap_if_optional(result_t, destination)
858            }
859            BinaryOperation::Div => {
860                let operand_expected = self.unwrap_optional_type(destination);
861
862                // The expected type for both `left` and `right` is the unwrapped type
863                let mut t1 = self.visit_expression(&input.left, &operand_expected);
864                let mut t2 = self.visit_expression(&input.right, &operand_expected);
865
866                // Infer `Numeric` types if possible
867                infer_numeric_types(self, &mut t1, &mut t2);
868
869                // Now sanity check everything
870                self.assert_field_int_type(&t1, input.left.span());
871                self.assert_field_int_type(&t2, input.right.span());
872
873                let result_t = assert_same_type(self, &t1, &t2);
874
875                self.maybe_assert_type(&result_t, destination, input.span());
876
877                self.wrap_if_optional(result_t, destination)
878            }
879            BinaryOperation::Rem | BinaryOperation::RemWrapped => {
880                let operand_expected = self.unwrap_optional_type(destination);
881
882                // The expected type for both `left` and `right` is the unwrapped type
883                let mut t1 = self.visit_expression(&input.left, &operand_expected);
884                let mut t2 = self.visit_expression(&input.right, &operand_expected);
885
886                // Infer `Numeric` types if possible
887                infer_numeric_types(self, &mut t1, &mut t2);
888
889                // Now sanity check everything
890                self.assert_int_type(&t1, input.left.span());
891                self.assert_int_type(&t2, input.right.span());
892
893                let result_t = assert_same_type(self, &t1, &t2);
894
895                self.maybe_assert_type(&result_t, destination, input.span());
896
897                self.wrap_if_optional(result_t, destination)
898            }
899            BinaryOperation::Mod => {
900                let operand_expected = self.unwrap_optional_type(destination);
901
902                // The expected type for both `left` and `right` is the unwrapped type
903                let mut t1 = self.visit_expression(&input.left, &operand_expected);
904                let mut t2 = self.visit_expression(&input.right, &operand_expected);
905
906                // Infer `Numeric` types if possible
907                infer_numeric_types(self, &mut t1, &mut t2);
908
909                // Now sanity check everything
910                self.assert_unsigned_type(&t1, input.left.span());
911                self.assert_unsigned_type(&t2, input.right.span());
912
913                let result_t = assert_same_type(self, &t1, &t2);
914
915                self.maybe_assert_type(&result_t, destination, input.span());
916
917                self.wrap_if_optional(result_t, destination)
918            }
919            BinaryOperation::Pow => {
920                let operand_expected = self.unwrap_optional_type(destination);
921
922                // The expected type of `left` is the unwrapped destination
923                let mut t1 = self.visit_expression(&input.left, &operand_expected);
924
925                // The expected type of `right` is `field`, `u8`, `u16`, or `u32` so leave it as `None` for now.
926                let mut t2 = self.visit_expression(&input.right, &None);
927
928                // If one side is a `Field` and the other is a `Numeric`, infer the `Numeric` as a `Field.
929                // Otherwise, error out for each `Numeric`.
930                if matches!((&t1, &t2), (Type::Field, Type::Numeric) | (Type::Numeric, Type::Field)) {
931                    infer_numeric_types(self, &mut t1, &mut t2);
932                } else {
933                    if matches!(t1, Type::Numeric) {
934                        self.emit_inference_failure_error(&mut t1, &input.left);
935                    }
936                    if matches!(t2, Type::Numeric) {
937                        self.emit_inference_failure_error(&mut t2, &input.right);
938                    }
939                }
940
941                // Now sanity check everything
942                let ty = match (&t1, &t2) {
943                    (Type::Err, _) | (_, Type::Err) => Type::Err,
944                    (Type::Field, Type::Field) => Type::Field,
945                    (base @ Type::Integer(_), t2) => {
946                        if !matches!(
947                            t2,
948                            Type::Integer(IntegerType::U8)
949                                | Type::Integer(IntegerType::U16)
950                                | Type::Integer(IntegerType::U32)
951                        ) {
952                            self.emit_err(TypeCheckerError::pow_types_mismatch(base, t2, input.span()));
953                        }
954                        base.clone()
955                    }
956                    _ => {
957                        self.emit_err(TypeCheckerError::pow_types_mismatch(t1, t2, input.span()));
958                        Type::Err
959                    }
960                };
961
962                self.maybe_assert_type(&ty, destination, input.span());
963
964                self.wrap_if_optional(ty, destination)
965            }
966            BinaryOperation::Eq | BinaryOperation::Neq => {
967                // Handle type inference for `None` as a special case.
968                //
969                // If either side of the binary expression is the literal `None`, we first type check the other side
970                // without any expected type to infer its type. Then we type check the `None` side using that inferred type
971                // as context, in hopes of resolving it to a more specific optional type.
972                //
973                // This helps with cases like `x == None`, allowing us to infer the type of `x` and apply it to `None`.
974                // However, this is **not sufficient for the general case**. For instance, in something like `[None] == [x]`,
975                // we won't be able to infer the type of `None`.
976                let (mut t1, mut t2) =
977                    if let Expression::Literal(Literal { variant: LiteralVariant::None, .. }) = input.right {
978                        let t1 = self.visit_expression(&input.left, &None);
979                        (t1.clone(), self.visit_expression(&input.right, &Some(t1.clone())))
980                    } else if let Expression::Literal(Literal { variant: LiteralVariant::None, .. }) = input.left {
981                        let t2 = self.visit_expression(&input.right, &None);
982                        (self.visit_expression(&input.left, &Some(t2.clone())), t2)
983                    } else {
984                        (self.visit_expression(&input.left, &None), self.visit_expression(&input.right, &None))
985                    };
986
987                // Infer `Numeric` types if possible
988                infer_numeric_types(self, &mut t1, &mut t2);
989
990                // Now sanity check everything
991                let _ = assert_same_type(self, &t1, &t2);
992
993                self.maybe_assert_type(&Type::Boolean, destination, input.span());
994
995                Type::Boolean
996            }
997            BinaryOperation::Lt | BinaryOperation::Gt | BinaryOperation::Lte | BinaryOperation::Gte => {
998                // Assert left and right are equal field, scalar, or integer types.
999                let mut t1 = self.visit_expression(&input.left, &None);
1000                let mut t2 = self.visit_expression(&input.right, &None);
1001
1002                // Infer `Numeric` types if possible
1003                infer_numeric_types(self, &mut t1, &mut t2);
1004
1005                // Now sanity check everything
1006                let assert_compare_type = |type_: &Type, span: Span| {
1007                    if !matches!(type_, Type::Err | Type::Field | Type::Scalar | Type::Integer(_)) {
1008                        self.emit_err(TypeCheckerError::type_should_be2(type_, "a field, scalar, or integer", span));
1009                    }
1010                };
1011
1012                assert_compare_type(&t1, input.left.span());
1013                assert_compare_type(&t2, input.right.span());
1014
1015                let _ = assert_same_type(self, &t1, &t2);
1016
1017                self.maybe_assert_type(&Type::Boolean, destination, input.span());
1018
1019                Type::Boolean
1020            }
1021            BinaryOperation::AddWrapped
1022            | BinaryOperation::SubWrapped
1023            | BinaryOperation::DivWrapped
1024            | BinaryOperation::MulWrapped => {
1025                let operand_expected = self.unwrap_optional_type(destination);
1026
1027                // The expected type for both `left` and `right` is the unwrapped type
1028                let mut t1 = self.visit_expression(&input.left, &operand_expected);
1029                let mut t2 = self.visit_expression(&input.right, &operand_expected);
1030
1031                // Infer `Numeric` types if possible
1032                infer_numeric_types(self, &mut t1, &mut t2);
1033
1034                // Now sanity check everything
1035                self.assert_int_type(&t1, input.left.span());
1036                self.assert_int_type(&t2, input.right.span());
1037
1038                let result_t = assert_same_type(self, &t1, &t2);
1039
1040                self.maybe_assert_type(&result_t, destination, input.span());
1041
1042                self.wrap_if_optional(result_t, destination)
1043            }
1044            BinaryOperation::Shl
1045            | BinaryOperation::ShlWrapped
1046            | BinaryOperation::Shr
1047            | BinaryOperation::ShrWrapped
1048            | BinaryOperation::PowWrapped => {
1049                let operand_expected = self.unwrap_optional_type(destination);
1050
1051                // The expected type of `left` is the unwrapped `destination`
1052                let t1 = self.visit_expression_reject_numeric(&input.left, &operand_expected);
1053
1054                // The expected type of `right` is `field`, `u8`, `u16`, or `u32` so leave it as `None` for now.
1055                let t2 = self.visit_expression_reject_numeric(&input.right, &None);
1056
1057                self.assert_int_type(&t1, input.left.span());
1058
1059                if !matches!(
1060                    &t2,
1061                    Type::Err
1062                        | Type::Integer(IntegerType::U8)
1063                        | Type::Integer(IntegerType::U16)
1064                        | Type::Integer(IntegerType::U32)
1065                ) {
1066                    self.emit_err(TypeCheckerError::shift_type_magnitude(input.op, t2, input.right.span()));
1067                }
1068
1069                self.wrap_if_optional(t1, destination)
1070            }
1071        }
1072    }
1073
1074    fn visit_call(&mut self, input: &CallExpression, expected: &Self::AdditionalInput) -> Self::Output {
1075        let callee_program = input.program.or(self.scope_state.program_name).unwrap();
1076
1077        let callee_path = input.function.absolute_path();
1078
1079        let Some(func_symbol) =
1080            self.state.symbol_table.lookup_function(&Location::new(callee_program, callee_path.clone()))
1081        else {
1082            self.emit_err(TypeCheckerError::unknown_sym("function", input.function.clone(), input.function.span()));
1083            return Type::Err;
1084        };
1085
1086        let func = func_symbol.function.clone();
1087
1088        // Check that the call is valid.
1089        // We always set the variant before entering the body of a function, so this unwrap works.
1090        match self.scope_state.variant.unwrap() {
1091            Variant::AsyncFunction | Variant::Function if !matches!(func.variant, Variant::Inline) => self.emit_err(
1092                TypeCheckerError::can_only_call_inline_function("a `function`, `inline`, or `constructor`", input.span),
1093            ),
1094            Variant::Transition | Variant::AsyncTransition
1095                if matches!(func.variant, Variant::Transition)
1096                    && input.program.is_none_or(|program| program == self.scope_state.program_name.unwrap()) =>
1097            {
1098                self.emit_err(TypeCheckerError::cannot_invoke_call_to_local_transition_function(input.span))
1099            }
1100            _ => {}
1101        }
1102
1103        // Check that the call is not to an external `inline` function.
1104        if func.variant == Variant::Inline
1105            && input.program.is_some_and(|program| program != self.scope_state.program_name.unwrap())
1106        {
1107            self.emit_err(TypeCheckerError::cannot_call_external_inline_function(input.span));
1108        }
1109
1110        // Make sure we're not calling a non-inline from an async block
1111        if self.async_block_id.is_some() && !matches!(func.variant, Variant::Inline) {
1112            self.emit_err(TypeCheckerError::can_only_call_inline_function("an async block", input.span));
1113        }
1114
1115        // Async functions return a single future.
1116        let mut ret = if func.variant == Variant::AsyncFunction {
1117            // Async functions always return futures.
1118            Type::Future(FutureType::new(
1119                Vec::new(),
1120                Some(Location::new(callee_program, input.function.absolute_path())),
1121                false,
1122            ))
1123        } else if func.variant == Variant::AsyncTransition {
1124            // Fully infer future type.
1125            let Some(inputs) =
1126                self.async_function_input_types.get(&Location::new(callee_program, vec![Symbol::intern(&format!(
1127                    "finalize/{}",
1128                    input.function.identifier().name
1129                ))]))
1130            else {
1131                self.emit_err(TypeCheckerError::async_function_not_found(input.function.clone(), input.span));
1132                return Type::Future(FutureType::new(
1133                    Vec::new(),
1134                    Some(Location::new(callee_program, callee_path.clone())),
1135                    false,
1136                ));
1137            };
1138
1139            let future_type = Type::Future(FutureType::new(
1140                inputs.clone(),
1141                Some(Location::new(callee_program, callee_path.clone())),
1142                true,
1143            ));
1144            let fully_inferred_type = match &func.output_type {
1145                Type::Tuple(tup) => Type::Tuple(TupleType::new(
1146                    tup.elements()
1147                        .iter()
1148                        .map(|t| if matches!(t, Type::Future(_)) { future_type.clone() } else { t.clone() })
1149                        .collect::<Vec<Type>>(),
1150                )),
1151                Type::Future(_) => future_type,
1152                _ => panic!("Invalid output type for async transition."),
1153            };
1154            self.assert_and_return_type(fully_inferred_type, expected, input.span())
1155        } else {
1156            self.assert_and_return_type(func.output_type, expected, input.span())
1157        };
1158
1159        // Check number of function arguments.
1160        if func.input.len() != input.arguments.len() {
1161            self.emit_err(TypeCheckerError::incorrect_num_args_to_call(
1162                func.input.len(),
1163                input.arguments.len(),
1164                input.span(),
1165            ));
1166        }
1167
1168        // Check the number of const arguments against the number of the function's const parameters
1169        if func.const_parameters.len() != input.const_arguments.len() {
1170            self.emit_err(TypeCheckerError::incorrect_num_const_args(
1171                "Call",
1172                func.const_parameters.len(),
1173                input.const_arguments.len(),
1174                input.span(),
1175            ));
1176        }
1177
1178        // Check the types of const arguments against the types of the function's const parameters
1179        for (expected, argument) in func.const_parameters.iter().zip(input.const_arguments.iter()) {
1180            self.visit_expression(argument, &Some(expected.type_().clone()));
1181        }
1182
1183        let (mut input_futures, mut inferred_finalize_inputs) = (Vec::new(), Vec::new());
1184        for (expected, argument) in func.input.iter().zip(input.arguments.iter()) {
1185            // Get the type of the expression. If the type is not known, do not attempt to attempt any further inference.
1186            let ty = self.visit_expression(argument, &Some(expected.type_().clone()));
1187
1188            if ty == Type::Err {
1189                return Type::Err;
1190            }
1191            // Extract information about futures that are being consumed.
1192            if func.variant == Variant::AsyncFunction && matches!(expected.type_(), Type::Future(_)) {
1193                // Consume the future.
1194                let option_name = match argument {
1195                    Expression::Path(path) => Some(path.identifier().name),
1196                    Expression::TupleAccess(tuple_access) => {
1197                        if let Expression::Path(path) = &tuple_access.tuple {
1198                            Some(path.identifier().name)
1199                        } else {
1200                            None
1201                        }
1202                    }
1203                    _ => None,
1204                };
1205
1206                if let Some(name) = option_name {
1207                    match self.scope_state.futures.shift_remove(&name) {
1208                        Some(future) => {
1209                            self.scope_state.call_location = Some(future);
1210                        }
1211                        None => {
1212                            self.emit_err(TypeCheckerError::unknown_future_consumed(name, argument.span()));
1213                        }
1214                    }
1215                }
1216
1217                match argument {
1218                    Expression::Path(_) | Expression::Call(_) | Expression::TupleAccess(_) => {
1219                        match &self.scope_state.call_location {
1220                            Some(location) => {
1221                                // Get the external program and function name.
1222                                input_futures.push(location.clone());
1223                                // Get the full inferred type.
1224                                inferred_finalize_inputs.push(ty);
1225                            }
1226                            None => {
1227                                self.emit_err(TypeCheckerError::unknown_future_consumed(argument, argument.span()));
1228                            }
1229                        }
1230                    }
1231                    _ => {
1232                        self.emit_err(TypeCheckerError::unknown_future_consumed("unknown", argument.span()));
1233                    }
1234                }
1235            } else {
1236                inferred_finalize_inputs.push(ty);
1237            }
1238        }
1239
1240        let caller_program =
1241            self.scope_state.program_name.expect("`program_name` is always set before traversing a program scope");
1242        // Note: Constructors are added to the call graph under the `constructor` symbol.
1243        // This is safe since `constructor` is a reserved token and cannot be used as a function name.
1244        let caller_function = if self.scope_state.is_constructor {
1245            sym::constructor
1246        } else {
1247            self.scope_state.function.expect("`function` is always set before traversing a function scope")
1248        };
1249
1250        // This is the path to the function that we're in
1251        let caller_path = self
1252            .scope_state
1253            .module_name
1254            .iter()
1255            .cloned()
1256            .chain(std::iter::once(caller_function))
1257            .collect::<Vec<Symbol>>();
1258
1259        let caller = Location::new(caller_program, caller_path.clone());
1260        let callee = Location::new(callee_program, callee_path.clone());
1261        self.state.call_graph.add_edge(caller, callee);
1262
1263        if func.variant.is_transition() && self.scope_state.variant == Some(Variant::AsyncTransition) {
1264            if self.scope_state.has_called_finalize {
1265                self.emit_err(TypeCheckerError::external_call_after_async("function call", input.span));
1266            }
1267
1268            if self.scope_state.already_contains_an_async_block {
1269                self.emit_err(TypeCheckerError::external_call_after_async("block", input.span));
1270            }
1271        }
1272
1273        // Propagate futures from async functions and transitions.
1274        if func.variant.is_async_function() {
1275            // Cannot have async calls in a conditional block.
1276            if self.scope_state.is_conditional {
1277                self.emit_err(TypeCheckerError::async_call_in_conditional(input.span));
1278            }
1279
1280            // Can only call async functions and external async transitions from an async transition body.
1281            if !matches!(self.scope_state.variant, Some(Variant::AsyncTransition) | Some(Variant::Script)) {
1282                self.emit_err(TypeCheckerError::async_call_can_only_be_done_from_async_transition(input.span));
1283            }
1284
1285            // Can only call an async function once in a transition function body.
1286            if self.scope_state.has_called_finalize {
1287                self.emit_err(TypeCheckerError::must_call_async_function_once(input.span));
1288            }
1289
1290            if self.scope_state.already_contains_an_async_block {
1291                self.emit_err(TypeCheckerError::conflicting_async_call_and_block(input.span));
1292            }
1293
1294            // Check that all futures consumed.
1295            if !self.scope_state.futures.is_empty() {
1296                self.emit_err(TypeCheckerError::not_all_futures_consumed(
1297                    self.scope_state.futures.iter().map(|(f, _)| f).join(", "),
1298                    input.span,
1299                ));
1300            }
1301            self.state
1302                .symbol_table
1303                .attach_finalizer(
1304                    Location::new(callee_program, caller_path),
1305                    Location::new(callee_program, callee_path.clone()),
1306                    input_futures,
1307                    inferred_finalize_inputs.clone(),
1308                )
1309                .expect("Failed to attach finalizer");
1310            // Create expectation for finalize inputs that will be checked when checking corresponding finalize function signature.
1311            self.async_function_callers
1312                .entry(Location::new(self.scope_state.program_name.unwrap(), callee_path.clone()))
1313                .or_default()
1314                .insert(self.scope_state.location());
1315
1316            // Set scope state flag.
1317            self.scope_state.has_called_finalize = true;
1318
1319            // Update ret to reflect fully inferred future type.
1320            ret = Type::Future(FutureType::new(
1321                inferred_finalize_inputs,
1322                Some(Location::new(callee_program, callee_path.clone())),
1323                true,
1324            ));
1325
1326            // Type check in case the expected type is known.
1327            self.assert_and_return_type(ret.clone(), expected, input.span());
1328        }
1329
1330        // Set call location so that definition statement knows where future comes from.
1331        self.scope_state.call_location = Some(Location::new(callee_program, callee_path.clone()));
1332
1333        ret
1334    }
1335
1336    fn visit_cast(&mut self, input: &CastExpression, expected: &Self::AdditionalInput) -> Self::Output {
1337        let expression_type = self.visit_expression_reject_numeric(&input.expression, &None);
1338
1339        let assert_castable_type = |actual: &Type, span: Span| {
1340            if !matches!(
1341                actual,
1342                Type::Integer(_) | Type::Boolean | Type::Field | Type::Group | Type::Scalar | Type::Address | Type::Err,
1343            ) {
1344                self.emit_err(TypeCheckerError::type_should_be2(
1345                    actual,
1346                    "an integer, bool, field, group, scalar, or address",
1347                    span,
1348                ));
1349            }
1350        };
1351
1352        assert_castable_type(&input.type_, input.span());
1353
1354        assert_castable_type(&expression_type, input.expression.span());
1355
1356        self.maybe_assert_type(&input.type_, expected, input.span());
1357
1358        input.type_.clone()
1359    }
1360
1361    fn visit_struct_init(&mut self, input: &StructExpression, additional: &Self::AdditionalInput) -> Self::Output {
1362        let struct_ = self.lookup_struct(self.scope_state.program_name, &input.path.absolute_path()).clone();
1363        let Some(struct_) = struct_ else {
1364            self.emit_err(TypeCheckerError::unknown_sym("struct or record", input.path.clone(), input.path.span()));
1365            return Type::Err;
1366        };
1367
1368        // Check the number of const arguments against the number of the struct's const parameters
1369        if struct_.const_parameters.len() != input.const_arguments.len() {
1370            self.emit_err(TypeCheckerError::incorrect_num_const_args(
1371                "Struct expression",
1372                struct_.const_parameters.len(),
1373                input.const_arguments.len(),
1374                input.span(),
1375            ));
1376        }
1377
1378        // Check the types of const arguments against the types of the struct's const parameters
1379        for (expected, argument) in struct_.const_parameters.iter().zip(input.const_arguments.iter()) {
1380            self.visit_expression(argument, &Some(expected.type_().clone()));
1381        }
1382
1383        // Note that it is sufficient for the `program` to be `None` as composite types can only be initialized
1384        // in the program in which they are defined.
1385        let type_ = Type::Composite(CompositeType {
1386            path: input.path.clone(),
1387            const_arguments: input.const_arguments.clone(),
1388            program: None,
1389        });
1390        self.maybe_assert_type(&type_, additional, input.path.span());
1391
1392        // Check number of struct members.
1393        if struct_.members.len() != input.members.len() {
1394            self.emit_err(TypeCheckerError::incorrect_num_struct_members(
1395                struct_.members.len(),
1396                input.members.len(),
1397                input.span(),
1398            ));
1399        }
1400
1401        for Member { identifier, type_, .. } in struct_.members.iter() {
1402            if let Some(actual) = input.members.iter().find(|member| member.identifier.name == identifier.name) {
1403                match &actual.expression {
1404                    None => {
1405                        // If `expression` is None, then the member uses the identifier shorthand, e.g. `Foo { a }`
1406                        // We visit it as an expression rather than just calling `visit_path` so it will get
1407                        // put into the type table.
1408                        self.visit_expression(
1409                            &Path::from(actual.identifier)
1410                                .with_absolute_path(Some(
1411                                    self.scope_state
1412                                        .module_name
1413                                        .iter()
1414                                        .cloned()
1415                                        .chain(std::iter::once(actual.identifier.name))
1416                                        .collect::<Vec<Symbol>>(),
1417                                ))
1418                                .into(),
1419                            &Some(type_.clone()),
1420                        );
1421                    }
1422                    Some(expr) => {
1423                        // Otherwise, visit the associated expression.
1424                        self.visit_expression(expr, &Some(type_.clone()));
1425                    }
1426                };
1427            } else {
1428                self.emit_err(TypeCheckerError::missing_struct_member(struct_.identifier, identifier, input.span()));
1429            };
1430        }
1431
1432        if struct_.is_record {
1433            // First, ensure that the current scope is not an async function. Records should not be instantiated in
1434            // async functions
1435            if self.scope_state.variant == Some(Variant::AsyncFunction) {
1436                self.state
1437                    .handler
1438                    .emit_err(TypeCheckerError::records_not_allowed_inside_async("function", input.span()));
1439            }
1440
1441            // Similarly, ensure that the current scope is not an async block. Records should not be instantiated in
1442            // async blocks
1443            if self.async_block_id.is_some() {
1444                self.state.handler.emit_err(TypeCheckerError::records_not_allowed_inside_async("block", input.span()));
1445            }
1446
1447            // Records where the `owner` is `self.caller` can be problematic because `self.caller` can be a program
1448            // address and programs can't spend records. Emit a warning in this case.
1449            //
1450            // Multiple occurrences of `owner` here is an error but that should be flagged somewhere else.
1451            input.members.iter().filter(|init| init.identifier.name == sym::owner).for_each(|init| {
1452                if let Some(Expression::MemberAccess(access)) = &init.expression
1453                    && let MemberAccess {
1454                        inner: Expression::Path(path),
1455                        name: Identifier { name: sym::caller, .. },
1456                        ..
1457                    } = &**access
1458                    && path.identifier().name == sym::SelfLower
1459                {
1460                    self.emit_warning(TypeCheckerWarning::caller_as_record_owner(input.path.clone(), access.span()));
1461                }
1462            });
1463        }
1464
1465        type_
1466    }
1467
1468    // We do not want to panic on `ErrExpression`s in order to propagate as many errors as possible.
1469    fn visit_err(&mut self, _input: &ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output {
1470        Type::Err
1471    }
1472
1473    fn visit_path(&mut self, input: &Path, expected: &Self::AdditionalInput) -> Self::Output {
1474        let var = self.state.symbol_table.lookup_path(self.scope_state.program_name.unwrap(), &input.absolute_path());
1475
1476        if let Some(var) = var {
1477            if var.declaration == VariableType::Storage && !var.type_.is_vector() && !var.type_.is_mapping() {
1478                self.check_access_allowed("storage access", true, input.span());
1479            }
1480
1481            self.maybe_assert_type(&var.type_, expected, input.span());
1482            var.type_.clone()
1483        } else {
1484            self.emit_err(TypeCheckerError::unknown_sym("variable", input, input.span()));
1485            Type::Err
1486        }
1487    }
1488
1489    fn visit_literal(&mut self, input: &Literal, expected: &Self::AdditionalInput) -> Self::Output {
1490        let span = input.span();
1491
1492        macro_rules! parse_and_return {
1493            ($ty:ty, $variant:expr, $str:expr, $label:expr) => {{
1494                self.parse_integer_literal::<$ty>($str, span, $label);
1495                Type::Integer($variant)
1496            }};
1497        }
1498
1499        let type_ = match &input.variant {
1500            LiteralVariant::Address(..) => Type::Address,
1501            LiteralVariant::Boolean(..) => Type::Boolean,
1502            LiteralVariant::Field(..) => Type::Field,
1503            LiteralVariant::Scalar(..) => Type::Scalar,
1504            LiteralVariant::String(..) => Type::String,
1505            LiteralVariant::Integer(kind, string) => match kind {
1506                IntegerType::U8 => parse_and_return!(u8, IntegerType::U8, string, "u8"),
1507                IntegerType::U16 => parse_and_return!(u16, IntegerType::U16, string, "u16"),
1508                IntegerType::U32 => parse_and_return!(u32, IntegerType::U32, string, "u32"),
1509                IntegerType::U64 => parse_and_return!(u64, IntegerType::U64, string, "u64"),
1510                IntegerType::U128 => parse_and_return!(u128, IntegerType::U128, string, "u128"),
1511                IntegerType::I8 => parse_and_return!(i8, IntegerType::I8, string, "i8"),
1512                IntegerType::I16 => parse_and_return!(i16, IntegerType::I16, string, "i16"),
1513                IntegerType::I32 => parse_and_return!(i32, IntegerType::I32, string, "i32"),
1514                IntegerType::I64 => parse_and_return!(i64, IntegerType::I64, string, "i64"),
1515                IntegerType::I128 => parse_and_return!(i128, IntegerType::I128, string, "i128"),
1516            },
1517            LiteralVariant::Group(s) => {
1518                let trimmed = s.trim_start_matches('-').trim_start_matches('0');
1519                if !trimmed.is_empty()
1520                    && format!("{trimmed}group")
1521                        .parse::<snarkvm::prelude::Group<snarkvm::prelude::TestnetV0>>()
1522                        .is_err()
1523                {
1524                    self.emit_err(TypeCheckerError::invalid_int_value(trimmed, "group", span));
1525                }
1526                Type::Group
1527            }
1528            LiteralVariant::Unsuffixed(_) => match expected {
1529                Some(ty @ Type::Integer(_) | ty @ Type::Field | ty @ Type::Group | ty @ Type::Scalar) => {
1530                    self.check_numeric_literal(input, ty);
1531                    ty.clone()
1532                }
1533                Some(ty @ Type::Optional(opt)) => {
1534                    // Handle optional expected type, e.g., u32?
1535                    let inner = &opt.inner;
1536                    match &**inner {
1537                        Type::Integer(_) | Type::Field | Type::Group | Type::Scalar => {
1538                            self.check_numeric_literal(input, inner);
1539                            Type::Optional(OptionalType { inner: Box::new(*inner.clone()) })
1540                        }
1541                        _ => {
1542                            self.emit_err(TypeCheckerError::unexpected_unsuffixed_numeral(
1543                                format!("type `{ty}`"),
1544                                span,
1545                            ));
1546                            Type::Err
1547                        }
1548                    }
1549                }
1550                Some(ty) => {
1551                    self.emit_err(TypeCheckerError::unexpected_unsuffixed_numeral(format!("type `{ty}`"), span));
1552                    Type::Err
1553                }
1554                None => Type::Numeric,
1555            },
1556            LiteralVariant::None => {
1557                if let Some(ty @ Type::Optional(_)) = expected {
1558                    ty.clone()
1559                } else if let Some(ty) = expected {
1560                    self.emit_err(TypeCheckerError::none_found_non_optional(format!("{ty}"), span));
1561                    Type::Err
1562                } else {
1563                    self.emit_err(TypeCheckerError::could_not_determine_type(format!("{input}"), span));
1564                    Type::Err
1565                }
1566            }
1567        };
1568
1569        self.maybe_assert_type(&type_, expected, span);
1570
1571        type_
1572    }
1573
1574    fn visit_locator(&mut self, input: &LocatorExpression, expected: &Self::AdditionalInput) -> Self::Output {
1575        let maybe_var =
1576            self.state.symbol_table.lookup_global(&Location::new(input.program.name.name, vec![input.name])).cloned();
1577        if let Some(var) = maybe_var {
1578            self.maybe_assert_type(&var.type_, expected, input.span());
1579            var.type_
1580        } else {
1581            self.emit_err(TypeCheckerError::unknown_sym("variable", input.name, input.span()));
1582            Type::Err
1583        }
1584    }
1585
1586    fn visit_ternary(&mut self, input: &TernaryExpression, expected: &Self::AdditionalInput) -> Self::Output {
1587        self.visit_expression(&input.condition, &Some(Type::Boolean));
1588
1589        // We try to coerce one side to another in the ternary operator whenever possible and/or needed.
1590        let (t1, t2) = if expected.is_some() {
1591            (
1592                self.visit_expression_reject_numeric(&input.if_true, expected),
1593                self.visit_expression_reject_numeric(&input.if_false, expected),
1594            )
1595        } else if input.if_false.is_none_expr() {
1596            let t1 = self.visit_expression(&input.if_true, &None);
1597            if matches!(t1, Type::Optional(_)) {
1598                (t1.clone(), self.visit_expression(&input.if_false, &Some(t1.clone())))
1599            } else {
1600                (
1601                    t1.clone(),
1602                    self.visit_expression(
1603                        &input.if_false,
1604                        &Some(Type::Optional(OptionalType { inner: Box::new(t1.clone()) })),
1605                    ),
1606                )
1607            }
1608        } else if input.if_true.is_none_expr() {
1609            let t2 = self.visit_expression(&input.if_false, &None);
1610            if matches!(t2, Type::Optional(_)) {
1611                (t2.clone(), self.visit_expression(&input.if_true, &Some(t2.clone())))
1612            } else {
1613                (
1614                    t2.clone(),
1615                    self.visit_expression(
1616                        &input.if_true,
1617                        &Some(Type::Optional(OptionalType { inner: Box::new(t2.clone()) })),
1618                    ),
1619                )
1620            }
1621        } else {
1622            (
1623                self.visit_expression_reject_numeric(&input.if_true, &None),
1624                self.visit_expression_reject_numeric(&input.if_false, &None),
1625            )
1626        };
1627
1628        let typ = if t1 == Type::Err || t2 == Type::Err {
1629            Type::Err
1630        } else if !t1.can_coerce_to(&t2) && !t2.can_coerce_to(&t1) {
1631            self.emit_err(TypeCheckerError::ternary_branch_mismatch(t1, t2, input.span()));
1632            Type::Err
1633        } else if let Some(expected) = expected {
1634            expected.clone()
1635        } else if t1.can_coerce_to(&t2) {
1636            t2
1637        } else {
1638            t1
1639        };
1640
1641        // Make sure this isn't an external record type - won't work as we can't construct it.
1642        if self.is_external_record(&typ) {
1643            self.emit_err(TypeCheckerError::ternary_over_external_records(&typ, input.span));
1644        }
1645
1646        // None of its members may be external record types either.
1647        if let Type::Tuple(tuple) = &typ
1648            && tuple.elements().iter().any(|ty| self.is_external_record(ty))
1649        {
1650            self.emit_err(TypeCheckerError::ternary_over_external_records(&typ, input.span));
1651        }
1652
1653        typ
1654    }
1655
1656    fn visit_tuple(&mut self, input: &TupleExpression, expected: &Self::AdditionalInput) -> Self::Output {
1657        if let Some(expected) = expected {
1658            if let Type::Tuple(expected_types) = expected {
1659                // If the expected type is a tuple, then ensure it's compatible with `input`
1660
1661                // First, make sure that the number of tuple elements is correct
1662                if expected_types.length() != input.elements.len() {
1663                    self.emit_err(TypeCheckerError::incorrect_tuple_length(
1664                        expected_types.length(),
1665                        input.elements.len(),
1666                        input.span(),
1667                    ));
1668                }
1669
1670                // Now make sure that none of the tuple elements is a tuple
1671                input.elements.iter().zip(expected_types.elements()).for_each(|(expr, expected_el_ty)| {
1672                    if matches!(expr, Expression::Tuple(_)) {
1673                        self.emit_err(TypeCheckerError::nested_tuple_expression(expr.span()));
1674                    }
1675                    self.visit_expression(expr, &Some(expected_el_ty.clone()));
1676                });
1677
1678                // Just return the expected type since we proved it's correct
1679                expected.clone()
1680            } else {
1681                // If the expected type is not a tuple, then we just error out
1682
1683                // This is the expected type of the tuple based on its individual fields
1684                let field_types = input
1685                    .elements
1686                    .iter()
1687                    .map(|field| {
1688                        let ty = self.visit_expression(field, &None);
1689                        if ty == Type::Numeric {
1690                            self.emit_err(TypeCheckerError::could_not_determine_type(field.clone(), field.span()));
1691                            Type::Err
1692                        } else {
1693                            ty
1694                        }
1695                    })
1696                    .collect::<Vec<_>>();
1697                if field_types.iter().all(|f| *f != Type::Err) {
1698                    let tuple_type = Type::Tuple(TupleType::new(field_types));
1699                    self.emit_err(TypeCheckerError::type_should_be2(tuple_type, expected, input.span()));
1700                }
1701
1702                // Recover with the expected type anyways
1703                expected.clone()
1704            }
1705        } else {
1706            // If no `expected` type is provided, then we analyze the tuple itself and infer its type
1707
1708            // We still need to check that none of the tuple elements is a tuple
1709            input.elements.iter().for_each(|expr| {
1710                if matches!(expr, Expression::Tuple(_)) {
1711                    self.emit_err(TypeCheckerError::nested_tuple_expression(expr.span()));
1712                }
1713            });
1714
1715            Type::Tuple(TupleType::new(
1716                input
1717                    .elements
1718                    .iter()
1719                    .map(|field| {
1720                        let ty = self.visit_expression(field, &None);
1721                        if ty == Type::Numeric {
1722                            self.emit_err(TypeCheckerError::could_not_determine_type(field.clone(), field.span()));
1723                            Type::Err
1724                        } else {
1725                            ty
1726                        }
1727                    })
1728                    .collect::<Vec<_>>(),
1729            ))
1730        }
1731    }
1732
1733    fn visit_unary(&mut self, input: &UnaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
1734        let operand_expected = self.unwrap_optional_type(destination);
1735
1736        let assert_signed_int = |slf: &mut Self, type_: &Type| {
1737            if !matches!(
1738                type_,
1739                Type::Err
1740                    | Type::Integer(IntegerType::I8)
1741                    | Type::Integer(IntegerType::I16)
1742                    | Type::Integer(IntegerType::I32)
1743                    | Type::Integer(IntegerType::I64)
1744                    | Type::Integer(IntegerType::I128)
1745            ) {
1746                slf.emit_err(TypeCheckerError::type_should_be2(type_, "a signed integer", input.span()));
1747            }
1748        };
1749
1750        let ty = match input.op {
1751            UnaryOperation::Abs => {
1752                let type_ = self.visit_expression_reject_numeric(&input.receiver, &operand_expected);
1753                assert_signed_int(self, &type_);
1754                type_
1755            }
1756            UnaryOperation::AbsWrapped => {
1757                let type_ = self.visit_expression_reject_numeric(&input.receiver, &operand_expected);
1758                assert_signed_int(self, &type_);
1759                type_
1760            }
1761            UnaryOperation::Double => {
1762                let type_ = self.visit_expression_reject_numeric(&input.receiver, &operand_expected);
1763                if !matches!(&type_, Type::Err | Type::Field | Type::Group) {
1764                    self.emit_err(TypeCheckerError::type_should_be2(&type_, "a field or group", input.span()));
1765                }
1766                type_
1767            }
1768            UnaryOperation::Inverse => {
1769                let mut type_ = self.visit_expression(&input.receiver, &operand_expected);
1770                if type_ == Type::Numeric {
1771                    // We can actually infer to `field` here because only fields can be inverted
1772                    type_ = Type::Field;
1773                    self.state.type_table.insert(input.receiver.id(), Type::Field);
1774                } else {
1775                    self.assert_type(&type_, &Type::Field, input.span());
1776                }
1777                type_
1778            }
1779            UnaryOperation::Negate => {
1780                let type_ = self.visit_expression_reject_numeric(&input.receiver, &operand_expected);
1781                if !matches!(
1782                    &type_,
1783                    Type::Err
1784                        | Type::Integer(IntegerType::I8)
1785                        | Type::Integer(IntegerType::I16)
1786                        | Type::Integer(IntegerType::I32)
1787                        | Type::Integer(IntegerType::I64)
1788                        | Type::Integer(IntegerType::I128)
1789                        | Type::Group
1790                        | Type::Field
1791                ) {
1792                    self.emit_err(TypeCheckerError::type_should_be2(
1793                        &type_,
1794                        "a signed integer, group, or field",
1795                        input.receiver.span(),
1796                    ));
1797                }
1798                type_
1799            }
1800            UnaryOperation::Not => {
1801                let type_ = self.visit_expression_reject_numeric(&input.receiver, &operand_expected);
1802                if !matches!(&type_, Type::Err | Type::Boolean | Type::Integer(_)) {
1803                    self.emit_err(TypeCheckerError::type_should_be2(&type_, "a bool or integer", input.span()));
1804                }
1805                type_
1806            }
1807            UnaryOperation::Square => {
1808                let mut type_ = self.visit_expression(&input.receiver, &operand_expected);
1809                if type_ == Type::Numeric {
1810                    // We can actually infer to `field` here because only fields can be squared
1811                    type_ = Type::Field;
1812                    self.state.type_table.insert(input.receiver.id(), Type::Field);
1813                } else {
1814                    self.assert_type(&type_, &Type::Field, input.span());
1815                }
1816                type_
1817            }
1818            UnaryOperation::SquareRoot => {
1819                let mut type_ = self.visit_expression(&input.receiver, &operand_expected);
1820                if type_ == Type::Numeric {
1821                    // We can actually infer to `field` here because only fields can be square-rooted
1822                    type_ = Type::Field;
1823                    self.state.type_table.insert(input.receiver.id(), Type::Field);
1824                } else {
1825                    self.assert_type(&type_, &Type::Field, input.span());
1826                }
1827                type_
1828            }
1829            UnaryOperation::ToXCoordinate | UnaryOperation::ToYCoordinate => {
1830                let _operand_type = self.visit_expression(&input.receiver, &Some(Type::Group));
1831                self.maybe_assert_type(&Type::Field, destination, input.span());
1832                Type::Field
1833            }
1834        };
1835
1836        self.maybe_assert_type(&ty, destination, input.span());
1837
1838        self.wrap_if_optional(ty, destination)
1839    }
1840
1841    fn visit_unit(&mut self, _input: &UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output {
1842        Type::Unit
1843    }
1844
1845    /* Statements */
1846    fn visit_statement(&mut self, input: &Statement) {
1847        // No statements can follow a return statement.
1848        if self.scope_state.has_return {
1849            self.emit_err(TypeCheckerError::unreachable_code_after_return(input.span()));
1850            return;
1851        }
1852
1853        match input {
1854            Statement::Assert(stmt) => self.visit_assert(stmt),
1855            Statement::Assign(stmt) => self.visit_assign(stmt),
1856            Statement::Block(stmt) => self.visit_block(stmt),
1857            Statement::Conditional(stmt) => self.visit_conditional(stmt),
1858            Statement::Const(stmt) => self.visit_const(stmt),
1859            Statement::Definition(stmt) => self.visit_definition(stmt),
1860            Statement::Expression(stmt) => self.visit_expression_statement(stmt),
1861            Statement::Iteration(stmt) => self.visit_iteration(stmt),
1862            Statement::Return(stmt) => self.visit_return(stmt),
1863        }
1864    }
1865
1866    fn visit_assert(&mut self, input: &AssertStatement) {
1867        match &input.variant {
1868            AssertVariant::Assert(expr) => {
1869                let _type = self.visit_expression(expr, &Some(Type::Boolean));
1870            }
1871            AssertVariant::AssertEq(left, right) | AssertVariant::AssertNeq(left, right) => {
1872                let t1 = self.visit_expression_reject_numeric(left, &None);
1873                let t2 = self.visit_expression_reject_numeric(right, &None);
1874
1875                if t1 != Type::Err && t2 != Type::Err && !t1.eq_user(&t2) {
1876                    let op =
1877                        if matches!(input.variant, AssertVariant::AssertEq(..)) { "assert_eq" } else { "assert_neq" };
1878                    self.emit_err(TypeCheckerError::operation_types_mismatch(op, t1, t2, input.span()));
1879                }
1880            }
1881        }
1882    }
1883
1884    fn visit_assign(&mut self, input: &AssignStatement) {
1885        let (lhs_type, is_storage) = self.visit_expression_assign(&input.place);
1886        let value = &input.value;
1887
1888        if lhs_type == Type::Err {
1889            self.visit_expression(value, &None);
1890            return;
1891        }
1892
1893        if is_storage && !lhs_type.is_vector() && !lhs_type.is_mapping() {
1894            self.check_access_allowed("storage write", true, input.place.span())
1895        }
1896
1897        let expected_rhs_ty = match (is_storage, value.is_none_expr(), &lhs_type) {
1898            (true, false, Type::Optional(OptionalType { inner })) => {
1899                // For storage variables that are being assigned to a value other than `none` where
1900                // the `lhs_type` is an optional, we want the expected type to be the wrapped type
1901                // in that optional. For example:
1902                // ```leo
1903                // storage x: u8; // actually optional
1904                //
1905                // x = 5` // expected type of the RHS is just `u8` (not `u8`?)
1906                // ```
1907                // Note that this means that this would fail with a type mismatch:
1908                // ```leo
1909                // let y: u8? = 5;
1910                // x = y; // Assigning an optional to a storage variable.
1911                // ```
1912                // We should probably eventually support this for completeness, but it add some
1913                // complications that makes it not worth it as this stage.
1914                Some(*inner.clone())
1915            }
1916            _ => {
1917                // Fore everything else, just proceed as normal.
1918                Some(lhs_type)
1919            }
1920        };
1921
1922        self.visit_expression(value, &expected_rhs_ty);
1923    }
1924
1925    fn visit_block(&mut self, input: &Block) {
1926        self.in_scope(input.id, |slf| {
1927            input.statements.iter().for_each(|stmt| slf.visit_statement(stmt));
1928        });
1929    }
1930
1931    fn visit_conditional(&mut self, input: &ConditionalStatement) {
1932        self.visit_expression(&input.condition, &Some(Type::Boolean));
1933
1934        let mut then_block_has_return = false;
1935        let mut otherwise_block_has_return = false;
1936
1937        // Set the `has_return` flag for the then-block.
1938        let previous_has_return = core::mem::replace(&mut self.scope_state.has_return, then_block_has_return);
1939        // Set the `is_conditional` flag.
1940        let previous_is_conditional = core::mem::replace(&mut self.scope_state.is_conditional, true);
1941
1942        // Visit block.
1943        self.in_conditional_scope(|slf| slf.visit_block(&input.then));
1944
1945        // Store the `has_return` flag for the then-block.
1946        then_block_has_return = self.scope_state.has_return;
1947
1948        if let Some(otherwise) = &input.otherwise {
1949            // Set the `has_return` flag for the otherwise-block.
1950            self.scope_state.has_return = otherwise_block_has_return;
1951
1952            match &**otherwise {
1953                Statement::Block(stmt) => {
1954                    // Visit the otherwise-block.
1955                    self.in_conditional_scope(|slf| slf.visit_block(stmt));
1956                }
1957                Statement::Conditional(stmt) => self.visit_conditional(stmt),
1958                _ => unreachable!("Else-case can only be a block or conditional statement."),
1959            }
1960
1961            // Store the `has_return` flag for the otherwise-block.
1962            otherwise_block_has_return = self.scope_state.has_return;
1963        }
1964
1965        // Restore the previous `has_return` flag.
1966        self.scope_state.has_return = previous_has_return || (then_block_has_return && otherwise_block_has_return);
1967        // Restore the previous `is_conditional` flag.
1968        self.scope_state.is_conditional = previous_is_conditional;
1969    }
1970
1971    fn visit_const(&mut self, input: &ConstDeclaration) {
1972        self.visit_type(&input.type_);
1973
1974        // For now, consts that contain optional types are not supported.
1975        // TODO: remove this restriction by supporting const evaluation of optionals including `None`.
1976        if self.contains_optional_type(&input.type_) {
1977            self.emit_err(TypeCheckerError::const_cannot_be_optional(input.span));
1978        }
1979
1980        // Check that the type of the definition is not a unit type, singleton tuple type, or nested tuple type.
1981        match &input.type_ {
1982            // If the type is an empty tuple, return an error.
1983            Type::Unit => self.emit_err(TypeCheckerError::lhs_must_be_identifier_or_tuple(input.span)),
1984            // If the type is a singleton tuple, return an error.
1985            Type::Tuple(tuple) => match tuple.length() {
1986                0 | 1 => unreachable!("Parsing guarantees that tuple types have at least two elements."),
1987                _ => {
1988                    if tuple.elements().iter().any(|type_| matches!(type_, Type::Tuple(_))) {
1989                        self.emit_err(TypeCheckerError::nested_tuple_type(input.span))
1990                    }
1991                }
1992            },
1993            Type::Mapping(_) | Type::Err => unreachable!(
1994                "Parsing guarantees that `mapping` and `err` types are not present at this location in the AST."
1995            ),
1996            // Otherwise, the type is valid.
1997            _ => (), // Do nothing
1998        }
1999
2000        // Check the expression on the right-hand side.
2001        self.visit_expression(&input.value, &Some(input.type_.clone()));
2002
2003        if self.scope_state.function.is_some() {
2004            // Global consts have already been added to the symbol table, so only
2005            // add this one if it's local.
2006            if let Err(err) = self.state.symbol_table.insert_variable(
2007                self.scope_state.program_name.unwrap(),
2008                &[input.place.name],
2009                VariableSymbol { type_: input.type_.clone(), span: input.place.span, declaration: VariableType::Const },
2010            ) {
2011                self.state.handler.emit_err(err);
2012            }
2013        }
2014    }
2015
2016    fn visit_definition(&mut self, input: &DefinitionStatement) {
2017        // Check that the type annotation of the definition is valid, if provided.
2018        if let Some(ty) = &input.type_ {
2019            self.visit_type(ty);
2020            self.assert_type_is_valid(ty, input.span);
2021        }
2022
2023        // Check that the type of the definition is not a unit type, singleton tuple type, or nested tuple type.
2024        match &input.type_ {
2025            // If the type is a singleton tuple, return an error.
2026            Some(Type::Tuple(tuple)) => match tuple.length() {
2027                0 | 1 => unreachable!("Parsing guarantees that tuple types have at least two elements."),
2028                _ => {
2029                    for type_ in tuple.elements() {
2030                        if matches!(type_, Type::Tuple(_)) {
2031                            self.emit_err(TypeCheckerError::nested_tuple_type(input.span))
2032                        }
2033                    }
2034                }
2035            },
2036            Some(Type::Mapping(_)) | Some(Type::Err) => unreachable!(
2037                "Parsing guarantees that `mapping` and `err` types are not present at this location in the AST."
2038            ),
2039            // Otherwise, the type is valid.
2040            _ => (), // Do nothing
2041        }
2042
2043        // Check the expression on the right-hand side. If we could not resolve `Type::Numeric`, then just give up.
2044        // We could do better in the future by potentially looking at consumers of this variable and inferring type
2045        // information from them.
2046        let inferred_type = self.visit_expression_reject_numeric(&input.value, &input.type_);
2047
2048        // If the RHS is a storage vector, error out. Storage vectors cannot be assigned to a variable.
2049        // They can only be accessed directly via `push`, `pop`, etc.
2050        if inferred_type.is_vector() {
2051            self.emit_err(TypeCheckerError::storage_vectors_cannot_be_moved_or_assigned(input.value.span()));
2052        }
2053
2054        // Insert the variables into the symbol table.
2055        match &input.place {
2056            DefinitionPlace::Single(identifier) => {
2057                self.insert_variable(
2058                    Some(inferred_type.clone()),
2059                    identifier,
2060                    // If no type annotation is provided, then just use `inferred_type`.
2061                    input.type_.clone().unwrap_or(inferred_type),
2062                    identifier.span,
2063                );
2064            }
2065            DefinitionPlace::Multiple(identifiers) => {
2066                // Get the tuple type either from `input.type_` or from `inferred_type`.
2067                let tuple_type = match (&input.type_, inferred_type.clone()) {
2068                    (Some(Type::Tuple(tuple_type)), _) => tuple_type.clone(),
2069                    (None, Type::Tuple(tuple_type)) => tuple_type.clone(),
2070                    _ => {
2071                        // This is an error but should have been emitted earlier. Just exit here.
2072                        return;
2073                    }
2074                };
2075
2076                // Ensure the number of identifiers we're defining is the same as the number of tuple elements, as
2077                // indicated by `tuple_type`
2078                if identifiers.len() != tuple_type.length() {
2079                    return self.emit_err(TypeCheckerError::incorrect_num_tuple_elements(
2080                        identifiers.len(),
2081                        tuple_type.length(),
2082                        input.span(),
2083                    ));
2084                }
2085
2086                // Now just insert each tuple element as a separate variable
2087                for (i, identifier) in identifiers.iter().enumerate() {
2088                    let inferred = if let Type::Tuple(inferred_tuple) = &inferred_type {
2089                        inferred_tuple.elements().get(i).cloned().unwrap_or_default()
2090                    } else {
2091                        Type::Err
2092                    };
2093                    self.insert_variable(Some(inferred), identifier, tuple_type.elements()[i].clone(), identifier.span);
2094                }
2095            }
2096        }
2097    }
2098
2099    fn visit_expression_statement(&mut self, input: &ExpressionStatement) {
2100        // Expression statements can only be function calls.
2101        if !matches!(input.expression, Expression::Call(_) | Expression::AssociatedFunction(_) | Expression::Unit(_)) {
2102            self.emit_err(TypeCheckerError::expression_statement_must_be_function_call(input.span()));
2103        } else {
2104            // Check the expression.
2105            self.visit_expression(&input.expression, &None);
2106        }
2107    }
2108
2109    fn visit_iteration(&mut self, input: &IterationStatement) {
2110        // Ensure the type annotation is an integer type
2111        if let Some(ty) = &input.type_ {
2112            self.visit_type(ty);
2113            self.assert_int_type(ty, input.variable.span);
2114        }
2115
2116        // These are the types of the start and end expressions of the iterator range. `visit_expression` will make
2117        // sure they match `input.type_` (i.e. the iterator type annotation) if available.
2118        let start_ty = self.visit_expression(&input.start, &input.type_.clone());
2119        let stop_ty = self.visit_expression(&input.stop, &input.type_.clone());
2120
2121        // Ensure both types are integer types
2122        self.assert_int_type(&start_ty, input.start.span());
2123        self.assert_int_type(&stop_ty, input.stop.span());
2124
2125        if start_ty != stop_ty {
2126            // Emit an error if the types of the range bounds do not match
2127            self.emit_err(TypeCheckerError::range_bounds_type_mismatch(input.start.span() + input.stop.span()));
2128        }
2129
2130        // Now, just set the type of the iterator variable to `start_ty` if `input.type_` is not available. If `stop_ty`
2131        // does not match `start_ty` and `input.type_` is not available, the we just recover with `start_ty` anyways
2132        // and continue.
2133        let iterator_ty = input.type_.clone().unwrap_or(start_ty);
2134        self.state.type_table.insert(input.variable.id(), iterator_ty.clone());
2135
2136        self.in_scope(input.id(), |slf| {
2137            // Add the loop variable to the scope of the loop body.
2138            if let Err(err) = slf.state.symbol_table.insert_variable(
2139                slf.scope_state.program_name.unwrap(),
2140                &[input.variable.name],
2141                VariableSymbol { type_: iterator_ty.clone(), span: input.span(), declaration: VariableType::Const },
2142            ) {
2143                slf.state.handler.emit_err(err);
2144            }
2145
2146            let prior_has_return = core::mem::take(&mut slf.scope_state.has_return);
2147            let prior_has_finalize = core::mem::take(&mut slf.scope_state.has_called_finalize);
2148
2149            slf.visit_block(&input.block);
2150
2151            if slf.scope_state.has_return {
2152                slf.emit_err(TypeCheckerError::loop_body_contains_return(input.span()));
2153            }
2154
2155            if slf.scope_state.has_called_finalize {
2156                slf.emit_err(TypeCheckerError::loop_body_contains_async("function call", input.span()));
2157            }
2158
2159            if slf.scope_state.already_contains_an_async_block {
2160                slf.emit_err(TypeCheckerError::loop_body_contains_async("block expression", input.span()));
2161            }
2162
2163            slf.scope_state.has_return = prior_has_return;
2164            slf.scope_state.has_called_finalize = prior_has_finalize;
2165        });
2166    }
2167
2168    fn visit_return(&mut self, input: &ReturnStatement) {
2169        if self.async_block_id.is_some() {
2170            return self.emit_err(TypeCheckerError::async_block_cannot_return(input.span()));
2171        }
2172
2173        if self.scope_state.is_constructor {
2174            // It must return a unit value; nothing else to check.
2175            if !matches!(input.expression, Expression::Unit(..)) {
2176                self.emit_err(TypeCheckerError::constructor_can_only_return_unit(&input.expression, input.span));
2177            }
2178            return;
2179        }
2180
2181        let caller_name = self.scope_state.function.expect("`self.function` is set every time a function is visited.");
2182        let caller_path =
2183            self.scope_state.module_name.iter().cloned().chain(std::iter::once(caller_name)).collect::<Vec<Symbol>>();
2184
2185        let func_symbol = self
2186            .state
2187            .symbol_table
2188            .lookup_function(&Location::new(self.scope_state.program_name.unwrap(), caller_path.clone()))
2189            .expect("The symbol table creator should already have visited all functions.");
2190
2191        let mut return_type = func_symbol.function.output_type.clone();
2192
2193        if self.scope_state.variant == Some(Variant::AsyncTransition) && self.scope_state.has_called_finalize {
2194            let inferred_future_type = Future(FutureType::new(
2195                if let Some(finalizer) = &func_symbol.finalizer { finalizer.inferred_inputs.clone() } else { vec![] },
2196                Some(Location::new(self.scope_state.program_name.unwrap(), caller_path)),
2197                true,
2198            ));
2199
2200            // Need to modify return type since the function signature is just default future, but the actual return
2201            // type is the fully inferred future of the finalize input type.
2202            let inferred = match return_type.clone() {
2203                Future(_) => inferred_future_type,
2204                Tuple(tuple) => Tuple(TupleType::new(
2205                    tuple
2206                        .elements()
2207                        .iter()
2208                        .map(|t| if matches!(t, Future(_)) { inferred_future_type.clone() } else { t.clone() })
2209                        .collect::<Vec<Type>>(),
2210                )),
2211                _ => {
2212                    return self.emit_err(TypeCheckerError::async_transition_missing_future_to_return(input.span()));
2213                }
2214            };
2215
2216            // Check that the explicit type declared in the function output signature matches the inferred type.
2217            return_type = self.assert_and_return_type(inferred, &Some(return_type), input.span());
2218        }
2219
2220        if matches!(input.expression, Expression::Unit(..)) {
2221            // Manually type check rather than using one of the assert functions for a better error message.
2222            if return_type != Type::Unit {
2223                // TODO - This is a bit hackish. We're reusing an existing error, because
2224                // we have too many errors in TypeCheckerError without hitting the recursion
2225                // limit for macros. But the error message to the user should still be pretty clear.
2226                return self.emit_err(TypeCheckerError::missing_return(input.span()));
2227            }
2228        }
2229
2230        self.visit_expression(&input.expression, &Some(return_type));
2231
2232        // Set the `has_return` flag after processing `input.expression` so that we don't error out
2233        // on something like `return async { .. }`.
2234        self.scope_state.has_return = true;
2235    }
2236}