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