leo_passes/type_checking/
visitor.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 crate::{CompilerState, VariableSymbol, VariableType, type_checking::scope_state::ScopeState};
18
19use super::*;
20
21use leo_ast::*;
22use leo_errors::{TypeCheckerError, TypeCheckerWarning};
23use leo_span::{Span, Symbol};
24
25use anyhow::bail;
26use indexmap::{IndexMap, IndexSet};
27use snarkvm::{
28    console::algorithms::ECDSASignature,
29    prelude::PrivateKey,
30    synthesizer::program::{CommitVariant, DeserializeVariant, ECDSAVerifyVariant, HashVariant, SerializeVariant},
31};
32use std::{ops::Deref, str::FromStr};
33
34pub struct TypeCheckingVisitor<'a> {
35    pub state: &'a mut CompilerState,
36    /// The state of the current scope being traversed.
37    pub scope_state: ScopeState,
38    /// Mapping from async function stub name to the inferred input types.
39    pub async_function_input_types: IndexMap<Location, Vec<Type>>,
40    /// Mapping from async function name to the names of async transition callers.
41    pub async_function_callers: IndexMap<Location, IndexSet<Location>>,
42    /// The set of used composites.
43    pub used_structs: IndexSet<Vec<Symbol>>,
44    /// So we can check if we exceed limits on array size, number of mappings, or number of functions.
45    pub limits: TypeCheckingInput,
46    /// For detecting the error `TypeCheckerError::async_cannot_assign_outside_conditional`.
47    pub conditional_scopes: Vec<IndexSet<Symbol>>,
48    /// If we're inside an async block, this is the node ID of the contained `Block`. Otherwise, this is `None`.
49    pub async_block_id: Option<NodeID>,
50}
51
52impl TypeCheckingVisitor<'_> {
53    pub fn in_scope<T>(&mut self, id: NodeID, func: impl FnOnce(&mut Self) -> T) -> T {
54        self.state.symbol_table.enter_scope(Some(id));
55        let result = func(self);
56        self.state.symbol_table.enter_parent();
57        result
58    }
59
60    pub fn in_conditional_scope<T>(&mut self, func: impl FnOnce(&mut Self) -> T) -> T {
61        self.conditional_scopes.push(Default::default());
62        let result = func(self);
63        self.conditional_scopes.pop();
64        result
65    }
66
67    pub fn insert_symbol_conditional_scope(&mut self, symbol: Symbol) {
68        self.conditional_scopes.last_mut().expect("A conditional scope must be present.").insert(symbol);
69    }
70
71    pub fn symbol_in_conditional_scope(&mut self, symbol: Symbol) -> bool {
72        self.conditional_scopes.last().map(|set| set.contains(&symbol)).unwrap_or(false)
73    }
74
75    /// Emits a type checker error.
76    pub fn emit_err(&self, err: TypeCheckerError) {
77        self.state.handler.emit_err(err);
78    }
79
80    /// Emits a type checker warning
81    pub fn emit_warning(&mut self, warning: TypeCheckerWarning) {
82        if self.state.warnings.insert(warning.clone().into()) {
83            self.state.handler.emit_warning(warning);
84        }
85    }
86
87    /// Emits an error if the two given types are not equal.
88    pub fn check_eq_types(&self, t1: &Option<Type>, t2: &Option<Type>, span: Span) {
89        match (t1, t2) {
90            (Some(t1), Some(t2)) if !t1.eq_flat_relaxed(t2) => {
91                self.emit_err(TypeCheckerError::type_should_be(t1, t2, span))
92            }
93            (Some(type_), None) | (None, Some(type_)) => {
94                self.emit_err(TypeCheckerError::type_should_be("no type", type_, span))
95            }
96            _ => {}
97        }
98    }
99
100    /// Use this method when you know the actual type.
101    /// Emits an error to the handler if the `actual` type is not equal to the `expected` type.
102    pub fn assert_and_return_type(&mut self, actual: Type, expected: &Option<Type>, span: Span) -> Type {
103        // If expected is `Type::Err`, we don't want to actually report a redundant error.
104        if expected.is_some() && !matches!(expected, Some(Type::Err)) {
105            self.check_eq_types(&Some(actual.clone()), expected, span);
106        }
107        actual
108    }
109
110    pub fn maybe_assert_type(&mut self, actual: &Type, expected: &Option<Type>, span: Span) {
111        if let Some(expected) = expected {
112            self.assert_type(actual, expected, span);
113        }
114    }
115
116    pub fn assert_type(&mut self, actual: &Type, expected: &Type, span: Span) {
117        if actual != &Type::Err && !actual.can_coerce_to(expected) {
118            // If `actual` is Err, we will have already reported an error.
119            self.emit_err(TypeCheckerError::type_should_be2(actual, format!("type `{expected}`"), span));
120        }
121    }
122
123    /// Unwraps an optional type to its inner type for use with operands.
124    /// If the expected type is `T?`, returns `Some(T)`. Otherwise returns the type as-is.
125    pub fn unwrap_optional_type(&self, expected: &Option<Type>) -> Option<Type> {
126        match expected {
127            Some(Type::Optional(opt_type)) => Some(*opt_type.inner.clone()),
128            other => other.clone(),
129        }
130    }
131
132    /// Wraps a type in Optional if the destination type is Optional.
133    /// If destination is `T?` and actual is `T`, returns `T?`. Otherwise returns actual as-is.
134    pub fn wrap_if_optional(&self, actual: Type, destination: &Option<Type>) -> Type {
135        match (actual, destination) {
136            // if destination is Optional<T> and actual is T (not already Optional), wrap it
137            (actual_type, Some(Type::Optional(opt_type))) if !matches!(actual_type, Type::Optional(_)) => {
138                // only wrap if the inner type matches
139                if actual_type.can_coerce_to(&opt_type.inner) {
140                    Type::Optional(OptionalType { inner: Box::new(actual_type) })
141                } else {
142                    actual_type
143                }
144            }
145            (actual_type, _) => actual_type,
146        }
147    }
148
149    pub fn assert_int_type(&self, type_: &Type, span: Span) {
150        if !matches!(type_, Type::Err | Type::Integer(_)) {
151            self.emit_err(TypeCheckerError::type_should_be2(type_, "an integer", span));
152        }
153    }
154
155    pub fn assert_unsigned_type(&self, type_: &Type, span: Span) {
156        if !matches!(
157            type_,
158            Type::Err
159                | Type::Integer(IntegerType::U8)
160                | Type::Integer(IntegerType::U16)
161                | Type::Integer(IntegerType::U32)
162                | Type::Integer(IntegerType::U64)
163                | Type::Integer(IntegerType::U128)
164        ) {
165            self.emit_err(TypeCheckerError::type_should_be2(type_, "an unsigned integer", span));
166        }
167    }
168
169    pub fn assert_bool_int_type(&self, type_: &Type, span: Span) {
170        if !matches!(
171            type_,
172            Type::Err
173                | Type::Boolean
174                | Type::Integer(IntegerType::U8)
175                | Type::Integer(IntegerType::U16)
176                | Type::Integer(IntegerType::U32)
177                | Type::Integer(IntegerType::U64)
178                | Type::Integer(IntegerType::U128)
179                | Type::Integer(IntegerType::I8)
180                | Type::Integer(IntegerType::I16)
181                | Type::Integer(IntegerType::I32)
182                | Type::Integer(IntegerType::I64)
183                | Type::Integer(IntegerType::I128)
184        ) {
185            self.emit_err(TypeCheckerError::type_should_be2(type_, "a bool or integer", span));
186        }
187    }
188
189    pub fn assert_field_int_type(&self, type_: &Type, span: Span) {
190        if !matches!(
191            type_,
192            Type::Err
193                | Type::Field
194                | Type::Integer(IntegerType::U8)
195                | Type::Integer(IntegerType::U16)
196                | Type::Integer(IntegerType::U32)
197                | Type::Integer(IntegerType::U64)
198                | Type::Integer(IntegerType::U128)
199                | Type::Integer(IntegerType::I8)
200                | Type::Integer(IntegerType::I16)
201                | Type::Integer(IntegerType::I32)
202                | Type::Integer(IntegerType::I64)
203                | Type::Integer(IntegerType::I128)
204        ) {
205            self.emit_err(TypeCheckerError::type_should_be2(type_, "a field or integer", span));
206        }
207    }
208
209    pub fn assert_field_group_int_type(&self, type_: &Type, span: Span) {
210        if !matches!(type_, Type::Err | Type::Field | Type::Group | Type::Integer(_)) {
211            self.emit_err(TypeCheckerError::type_should_be2(type_, "a field, group, or integer", span));
212        }
213    }
214
215    /// Type checks the inputs to an associated constant and returns the expected output type.
216    pub fn get_core_constant(&self, type_: &Type, constant: &Identifier) -> Option<CoreConstant> {
217        if let Type::Identifier(ident) = type_ {
218            // Lookup core constant
219            match CoreConstant::from_symbols(ident.name, constant.name) {
220                None => {
221                    // Not a core constant.
222                    self.emit_err(TypeCheckerError::invalid_core_constant(ident.name, constant.name, ident.span()));
223                }
224                Some(core_constant) => return Some(core_constant),
225            }
226        }
227        None
228    }
229
230    /// Emits an error if the `struct` is not a core library struct.
231    /// Emits an error if the `function` is not supported by the struct.
232    pub fn get_core_function_call(&self, associated_function: &AssociatedFunctionExpression) -> Option<CoreFunction> {
233        // Lookup core struct
234        match CoreFunction::try_from(associated_function).ok() {
235            None => {
236                // Not a core library struct.
237                self.emit_err(TypeCheckerError::invalid_core_function(
238                    associated_function.variant.name,
239                    associated_function.name.name,
240                    associated_function.variant.span(),
241                ));
242                None
243            }
244            core_function @ Some(CoreFunction::Deserialize(_, _)) => core_function,
245            Some(core_instruction) => {
246                // Check that the number of type parameters is 0.
247                if !associated_function.type_parameters.is_empty() {
248                    self.emit_err(TypeCheckerError::custom(
249                        format!(
250                            "The core function `{}::{}` cannot have type parameters.",
251                            associated_function.variant, associated_function.name
252                        ),
253                        associated_function.name.span(),
254                    ));
255                    return None;
256                };
257
258                Some(core_instruction)
259            }
260        }
261    }
262
263    /// Type checks the inputs to a core function call and returns the expected output type.
264    /// Emits an error if the correct number of arguments are not provided.
265    /// Emits an error if the arguments are not of the correct type.
266    pub fn check_core_function_call(
267        &mut self,
268        core_function: CoreFunction,
269        arguments: &[Expression],
270        expected: &Option<Type>,
271        function_span: Span,
272    ) -> Type {
273        // Check that the number of arguments is correct.
274        if arguments.len() != core_function.num_args() {
275            self.emit_err(TypeCheckerError::incorrect_num_args_to_call(
276                core_function.num_args(),
277                arguments.len(),
278                function_span,
279            ));
280            return Type::Err;
281        }
282
283        // Type check and reconstructs the arguments for a given core function call.
284        //
285        // Depending on the `core_function`, this handles:
286        // - Optional operations (`unwrap`, `unwrap_or`) with proper type inference
287        // - Container access (`Get`, `Set`) for vectors and mappings
288        // - Vector-specific operations (`push`, `swap_remove`)
289        // - Default handling for other core functions
290        //
291        // Returns a `Vec<(Type, &Expression)>` pairing each argument with its inferred type, or `Type::Err` if
292        // type-checking fails. Argument counts are assumed to be already validated
293        let arguments = match core_function {
294            CoreFunction::OptionalUnwrap => {
295                // Expect exactly one argument
296                let [opt] = arguments else { panic!("number of arguments is already checked") };
297
298                // If an expected type is provided, wrap it in Optional for type-checking
299                let opt_ty = if let Some(expected) = expected {
300                    self.visit_expression(
301                        opt,
302                        &Some(Type::Optional(OptionalType { inner: Box::new(expected.clone()) })),
303                    )
304                } else {
305                    self.visit_expression(opt, &None)
306                };
307
308                vec![(opt_ty, opt)]
309            }
310
311            CoreFunction::OptionalUnwrapOr => {
312                // Expect exactly two arguments: the optional and the fallback value
313                let [opt, fallback] = arguments else { panic!("number of arguments is already checked") };
314
315                if let Some(expected) = expected {
316                    // Both arguments are typed based on the expected type
317                    let opt_ty = self.visit_expression(
318                        opt,
319                        &Some(Type::Optional(OptionalType { inner: Box::new(expected.clone()) })),
320                    );
321                    let fallback_ty = self.visit_expression(fallback, &Some(expected.clone()));
322                    vec![(opt_ty, opt), (fallback_ty, fallback)]
323                } else {
324                    // Infer type from the optional argument
325                    let opt_ty = self.visit_expression(opt, &None);
326                    let fallback_ty = if let Type::Optional(OptionalType { inner }) = &opt_ty {
327                        self.visit_expression(fallback, &Some(*inner.clone()))
328                    } else {
329                        self.visit_expression(fallback, &None)
330                    };
331                    vec![(opt_ty, opt), (fallback_ty, fallback)]
332                }
333            }
334
335            // Get an element from a container (vector or mapping)
336            CoreFunction::Get => {
337                let [container, key_or_index] = arguments else { panic!("number of arguments is already checked") };
338
339                let container_ty = self.visit_expression(container, &None);
340
341                // Key type depends on container type
342                let key_or_index_ty = match container_ty {
343                    Type::Vector(_) => self.visit_expression_infer_default_u32(key_or_index),
344                    Type::Mapping(MappingType { ref key, .. }) => {
345                        self.visit_expression(key_or_index, &Some(*key.clone()))
346                    }
347                    _ => self.visit_expression(key_or_index, &None),
348                };
349
350                vec![(container_ty, container), (key_or_index_ty, key_or_index)]
351            }
352
353            // Set an element in a container (vector or mapping)
354            CoreFunction::Set => {
355                let [container, key_or_index, val] = arguments else {
356                    panic!("number of arguments is already checked")
357                };
358
359                let container_ty = self.visit_expression(container, &None);
360
361                let key_or_index_ty = match container_ty {
362                    Type::Vector(_) => self.visit_expression_infer_default_u32(key_or_index),
363                    Type::Mapping(MappingType { ref key, .. }) => {
364                        self.visit_expression(key_or_index, &Some(*key.clone()))
365                    }
366                    _ => self.visit_expression(key_or_index, &None),
367                };
368
369                let val_ty = match container_ty {
370                    Type::Vector(VectorType { ref element_type }) => {
371                        self.visit_expression(val, &Some(*element_type.clone()))
372                    }
373                    Type::Mapping(MappingType { ref value, .. }) => self.visit_expression(val, &Some(*value.clone())),
374                    _ => self.visit_expression(val, &None),
375                };
376
377                vec![(container_ty, container), (key_or_index_ty, key_or_index), (val_ty, val)]
378            }
379
380            CoreFunction::VectorPush => {
381                let [vec, val] = arguments else { panic!("number of arguments is already checked") };
382
383                // Check vector type
384                let vec_ty = self.visit_expression(vec, &None);
385
386                // Type-check value against vector element type
387                let val_ty = if let Type::Vector(VectorType { element_type }) = &vec_ty {
388                    self.visit_expression(val, &Some(*element_type.clone()))
389                } else {
390                    self.visit_expression(val, &None)
391                };
392
393                vec![(vec_ty, vec), (val_ty, val)]
394            }
395
396            CoreFunction::VectorSwapRemove => {
397                let [vec, index] = arguments else { panic!("number of arguments is already checked") };
398
399                let vec_ty = self.visit_expression(vec, &None);
400
401                // Default to u32 index for vector operations
402                let index_ty = self.visit_expression_infer_default_u32(index);
403
404                vec![(vec_ty, vec), (index_ty, index)]
405            }
406
407            // Default case for other core functions
408            _ => {
409                arguments.iter().map(|arg| (self.visit_expression_reject_numeric(arg, &None), arg)).collect::<Vec<_>>()
410            }
411        };
412
413        let assert_not_mapping_tuple_unit = |type_: &Type, span: Span| {
414            if matches!(type_, Type::Mapping(_) | Type::Tuple(_) | Type::Unit) {
415                self.emit_err(TypeCheckerError::type_should_be2(type_, "anything but a mapping, tuple, or unit", span));
416            }
417        };
418
419        // Make sure the input is no bigger than 64 bits.
420        // Due to overhead in the bitwise representations of types in SnarkVM, 64 bit integers
421        // input more than 64 bits to a hash function, as do all structs and arrays.
422        let assert_pedersen_64_bit_input = |type_: &Type, span: Span| {
423            if !matches!(
424                type_,
425                Type::Integer(IntegerType::U8)
426                    | Type::Integer(IntegerType::U16)
427                    | Type::Integer(IntegerType::U32)
428                    | Type::Integer(IntegerType::I8)
429                    | Type::Integer(IntegerType::I16)
430                    | Type::Integer(IntegerType::I32)
431                    | Type::Boolean
432                    | Type::Err
433            ) {
434                self.emit_err(TypeCheckerError::type_should_be2(
435                    type_,
436                    "an integer of less than 64 bits or a bool",
437                    span,
438                ));
439            }
440        };
441
442        // Make sure the input is no bigger than 128 bits.
443        //
444        // Due to overhead in the bitwise representations of types in SnarkVM, 128 bit integers
445        // input more than 128 bits to a hash function, as do most structs and arrays. We could
446        // actually allow arrays with a single element of type smaller than 64 bits, but it
447        // seems most understandable to the user to simply disallow composite types entirely.
448        let assert_pedersen_128_bit_input = |type_: &Type, span: Span| {
449            if !matches!(
450                type_,
451                Type::Integer(IntegerType::U8)
452                    | Type::Integer(IntegerType::U16)
453                    | Type::Integer(IntegerType::U32)
454                    | Type::Integer(IntegerType::U64)
455                    | Type::Integer(IntegerType::I8)
456                    | Type::Integer(IntegerType::I16)
457                    | Type::Integer(IntegerType::I32)
458                    | Type::Integer(IntegerType::I64)
459                    | Type::Boolean
460                    | Type::Err
461            ) {
462                self.emit_err(TypeCheckerError::type_should_be2(
463                    type_,
464                    "an integer of less than 128 bits or a bool",
465                    span,
466                ));
467            }
468        };
469
470        // Define a regex to match valid program IDs.
471        let program_id_regex = regex::Regex::new(r"^[a-zA-Z][a-zA-Z0-9_]*\.aleo$").unwrap();
472
473        // Check that the arguments are of the correct type.
474        match core_function {
475            CoreFunction::Commit(variant, type_) => {
476                match variant {
477                    CommitVariant::CommitPED64 => {
478                        assert_pedersen_64_bit_input(&arguments[0].0, arguments[0].1.span());
479                    }
480                    CommitVariant::CommitPED128 => {
481                        assert_pedersen_128_bit_input(&arguments[0].0, arguments[0].1.span());
482                    }
483                    _ => {
484                        assert_not_mapping_tuple_unit(&arguments[0].0, arguments[0].1.span());
485                    }
486                }
487                self.assert_type(&arguments[1].0, &Type::Scalar, arguments[1].1.span());
488                type_.into()
489            }
490            CoreFunction::Hash(variant, type_) => {
491                // If the hash variant must be byte aligned, check that the number bits of the input is a multiple of 8.
492                if variant.requires_byte_alignment() {
493                    // Get the input type.
494                    let input_type = &arguments[0].0;
495                    // Get the size in bits.
496                    let size_in_bits = match self.state.network {
497                        NetworkName::TestnetV0 => input_type
498                            .size_in_bits::<TestnetV0, _>(variant.is_raw(), |_| bail!("structs are not supported")),
499                        NetworkName::MainnetV0 => input_type
500                            .size_in_bits::<MainnetV0, _>(variant.is_raw(), |_| bail!("structs are not supported")),
501                        NetworkName::CanaryV0 => input_type
502                            .size_in_bits::<CanaryV0, _>(variant.is_raw(), |_| bail!("structs are not supported")),
503                    };
504                    if let Ok(size_in_bits) = size_in_bits {
505                        // Check that the size in bits is a multiple of 8.
506                        if size_in_bits % 8 != 0 {
507                            self.emit_err(TypeCheckerError::type_should_be2(
508                                input_type,
509                                "a type with a size in bits that is a multiple of 8",
510                                arguments[0].1.span(),
511                            ));
512                            return Type::Err;
513                        }
514                    };
515                }
516                match variant {
517                    HashVariant::HashPED64 => {
518                        assert_pedersen_64_bit_input(&arguments[0].0, arguments[0].1.span());
519                    }
520                    HashVariant::HashPED128 => {
521                        assert_pedersen_128_bit_input(&arguments[0].0, arguments[0].1.span());
522                    }
523                    _ => {
524                        assert_not_mapping_tuple_unit(&arguments[0].0, arguments[0].1.span());
525                    }
526                }
527                type_
528            }
529            CoreFunction::ECDSAVerify(variant) => {
530                // Get the expected signature size.
531                let signature_size = ECDSASignature::SIGNATURE_SIZE_IN_BYTES;
532                // Check that the first input is a 65-byte array.
533                let Type::Array(array_type) = &arguments[0].0 else {
534                    self.emit_err(TypeCheckerError::type_should_be2(
535                        &arguments[0].0,
536                        format!("a [u8; {signature_size}]"),
537                        arguments[0].1.span(),
538                    ));
539                    return Type::Err;
540                };
541                self.assert_type(array_type.element_type(), &Type::Integer(IntegerType::U8), arguments[0].1.span());
542                if let Some(length) = array_type.length.as_u32()
543                    && length as usize != signature_size
544                {
545                    self.emit_err(TypeCheckerError::type_should_be2(
546                        &arguments[0].0,
547                        format!("a [u8; {signature_size}]"),
548                        arguments[0].1.span(),
549                    ));
550                    return Type::Err;
551                };
552
553                // Determine whether the core function is Ethereum-specifc.
554                let is_eth = match variant {
555                    ECDSAVerifyVariant::Digest => false,
556                    ECDSAVerifyVariant::DigestEth => true,
557                    ECDSAVerifyVariant::HashKeccak256 => false,
558                    ECDSAVerifyVariant::HashKeccak256Raw => false,
559                    ECDSAVerifyVariant::HashKeccak256Eth => true,
560                    ECDSAVerifyVariant::HashKeccak384 => false,
561                    ECDSAVerifyVariant::HashKeccak384Raw => false,
562                    ECDSAVerifyVariant::HashKeccak384Eth => true,
563                    ECDSAVerifyVariant::HashKeccak512 => false,
564                    ECDSAVerifyVariant::HashKeccak512Raw => false,
565                    ECDSAVerifyVariant::HashKeccak512Eth => true,
566                    ECDSAVerifyVariant::HashSha3_256 => false,
567                    ECDSAVerifyVariant::HashSha3_256Raw => false,
568                    ECDSAVerifyVariant::HashSha3_256Eth => true,
569                    ECDSAVerifyVariant::HashSha3_384 => false,
570                    ECDSAVerifyVariant::HashSha3_384Raw => false,
571                    ECDSAVerifyVariant::HashSha3_384Eth => true,
572                    ECDSAVerifyVariant::HashSha3_512 => false,
573                    ECDSAVerifyVariant::HashSha3_512Raw => false,
574                    ECDSAVerifyVariant::HashSha3_512Eth => true,
575                };
576                // Get the expected length of the second input.
577                let expected_length = if is_eth {
578                    ECDSASignature::ETHEREUM_ADDRESS_SIZE_IN_BYTES
579                } else {
580                    ECDSASignature::VERIFYING_KEY_SIZE_IN_BYTES
581                };
582                // Check that the second input is a byte array of the expected length.
583                let Type::Array(array_type) = &arguments[1].0 else {
584                    self.emit_err(TypeCheckerError::type_should_be2(
585                        &arguments[1].0,
586                        format!("a [u8; {expected_length}]"),
587                        arguments[1].1.span(),
588                    ));
589                    return Type::Err;
590                };
591                self.assert_type(array_type.element_type(), &Type::Integer(IntegerType::U8), arguments[1].1.span());
592                if let Some(length) = array_type.length.as_u32()
593                    && length as usize != expected_length
594                {
595                    self.emit_err(TypeCheckerError::type_should_be2(
596                        &arguments[1].0,
597                        format!("a [u8; {expected_length}]"),
598                        arguments[1].1.span(),
599                    ));
600                    return Type::Err;
601                };
602
603                // Check that the third input is not a mapping nor a tuple.
604                if matches!(&arguments[2].0, Type::Mapping(_) | Type::Tuple(_) | Type::Unit) {
605                    self.emit_err(TypeCheckerError::type_should_be2(
606                        &arguments[2].0,
607                        "anything but a mapping, tuple, or unit",
608                        arguments[2].1.span(),
609                    ));
610                }
611
612                // If the variant is a digest variant, check that the third input is a byte array of the correct length.
613                if matches!(variant, ECDSAVerifyVariant::Digest | ECDSAVerifyVariant::DigestEth) {
614                    // Get the expected length of the third input.
615                    let expected_length = ECDSASignature::PREHASH_SIZE_IN_BYTES;
616                    // Check that the third input is a byte array of the expected length.
617                    let Type::Array(array_type) = &arguments[2].0 else {
618                        self.emit_err(TypeCheckerError::type_should_be2(
619                            &arguments[2].0,
620                            format!("a [u8; {expected_length}]"),
621                            arguments[2].1.span(),
622                        ));
623                        return Type::Err;
624                    };
625                    self.assert_type(array_type.element_type(), &Type::Integer(IntegerType::U8), arguments[2].1.span());
626                    if let Some(length) = array_type.length.as_u32()
627                        && length as usize != expected_length
628                    {
629                        self.emit_err(TypeCheckerError::type_should_be2(
630                            &arguments[2].0,
631                            format!("a [u8; {expected_length}]"),
632                            arguments[2].1.span(),
633                        ));
634                        return Type::Err;
635                    }
636                }
637
638                // If the variant requires byte alignment, check that the third input is byte aligned.
639                if variant.requires_byte_alignment() {
640                    // Get the input type.
641                    let input_type = &arguments[2].0;
642                    // Get the size in bits.
643                    let size_in_bits = match self.state.network {
644                        NetworkName::TestnetV0 => input_type
645                            .size_in_bits::<TestnetV0, _>(variant.is_raw(), |_| bail!("structs are not supported")),
646                        NetworkName::MainnetV0 => input_type
647                            .size_in_bits::<MainnetV0, _>(variant.is_raw(), |_| bail!("structs are not supported")),
648                        NetworkName::CanaryV0 => input_type
649                            .size_in_bits::<CanaryV0, _>(variant.is_raw(), |_| bail!("structs are not supported")),
650                    };
651                    if let Ok(size_in_bits) = size_in_bits {
652                        // Check that the size in bits is a multiple of 8.
653                        if size_in_bits % 8 != 0 {
654                            self.emit_err(TypeCheckerError::type_should_be2(
655                                input_type,
656                                "a type with a size in bits that is a multiple of 8",
657                                arguments[2].1.span(),
658                            ));
659                            return Type::Err;
660                        }
661                    };
662                }
663
664                Type::Boolean
665            }
666            CoreFunction::Get => {
667                if let Type::Vector(VectorType { element_type }) = &arguments[0].0 {
668                    // Check that the operation is invoked in a `finalize` or `async` block.
669                    self.check_access_allowed("Vector::get", true, function_span);
670
671                    Type::Optional(OptionalType { inner: Box::new(*element_type.clone()) })
672                } else if let Type::Mapping(MappingType { value, .. }) = &arguments[0].0 {
673                    // Check that the operation is invoked in a `finalize` or `async` block.
674                    self.check_access_allowed("Mapping::get", true, function_span);
675
676                    *value.clone()
677                } else {
678                    self.assert_vector_or_mapping_type(&arguments[0].0, arguments[0].1.span());
679                    Type::Err
680                }
681            }
682            CoreFunction::Set => {
683                if arguments[0].0.is_vector() {
684                    // Check that the operation is invoked in a `finalize` or `async` block.
685                    self.check_access_allowed("Vector::set", true, function_span);
686
687                    Type::Unit
688                } else if let Type::Mapping(_) = &arguments[0].0 {
689                    // Check that the operation is invoked in a `finalize` or `async` block.
690                    self.check_access_allowed("Mapping::set", true, function_span);
691
692                    Type::Unit
693                } else {
694                    self.assert_vector_or_mapping_type(&arguments[0].0, arguments[0].1.span());
695                    Type::Err
696                }
697            }
698            CoreFunction::MappingGetOrUse => {
699                // Check that the operation is invoked in a `finalize` block.
700                self.check_access_allowed("Mapping::get_or_use", true, function_span);
701                // Check that the first argument is a mapping.
702                self.assert_mapping_type(&arguments[0].0, arguments[0].1.span());
703
704                let Type::Mapping(mapping_type) = &arguments[0].0 else {
705                    // We will have already handled the error in the assertion.
706                    return Type::Err;
707                };
708
709                // Check that the second argument matches the key type of the mapping.
710                self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1.span());
711                // Check that the third argument matches the value type of the mapping.
712                self.assert_type(&arguments[2].0, &mapping_type.value, arguments[2].1.span());
713
714                mapping_type.value.deref().clone()
715            }
716            CoreFunction::MappingRemove => {
717                // Check that the operation is invoked in a `finalize` block.
718                self.check_access_allowed("Mapping::remove", true, function_span);
719                // Check that the first argument is a mapping.
720                self.assert_mapping_type(&arguments[0].0, arguments[0].1.span());
721
722                let Type::Mapping(mapping_type) = &arguments[0].0 else {
723                    // We will have already handled the error in the assertion.
724                    return Type::Err;
725                };
726
727                // Cannot modify external mappings.
728                if mapping_type.program != self.scope_state.program_name.unwrap() {
729                    self.state
730                        .handler
731                        .emit_err(TypeCheckerError::cannot_modify_external_mapping("remove", function_span));
732                }
733
734                // Check that the second argument matches the key type of the mapping.
735                self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1.span());
736
737                Type::Unit
738            }
739            CoreFunction::MappingContains => {
740                // Check that the operation is invoked in a `finalize` block.
741                self.check_access_allowed("Mapping::contains", true, function_span);
742                // Check that the first argument is a mapping.
743                self.assert_mapping_type(&arguments[0].0, arguments[0].1.span());
744
745                let Type::Mapping(mapping_type) = &arguments[0].0 else {
746                    // We will have already handled the error in the assertion.
747                    return Type::Err;
748                };
749
750                // Check that the second argument matches the key type of the mapping.
751                self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1.span());
752
753                Type::Boolean
754            }
755            CoreFunction::OptionalUnwrap => {
756                // Check that the first argument is an optional.
757                self.assert_optional_type(&arguments[0].0, arguments[0].1.span());
758
759                match &arguments[0].0 {
760                    Type::Optional(opt) => opt.inner.deref().clone(),
761                    _ => Type::Err,
762                }
763            }
764            CoreFunction::OptionalUnwrapOr => {
765                // Check that the first argument is an optional.
766                self.assert_optional_type(&arguments[0].0, arguments[0].1.span());
767
768                match &arguments[0].0 {
769                    Type::Optional(OptionalType { inner }) => {
770                        // Ensure that the wrapped type and the fallback type are the same
771                        self.assert_type(&arguments[1].0, inner, arguments[1].1.span());
772                        inner.deref().clone()
773                    }
774                    _ => Type::Err,
775                }
776            }
777            CoreFunction::VectorPush => {
778                self.check_access_allowed("Vector::push", true, function_span);
779
780                // Check that the first argument is a vector
781                match &arguments[0].0 {
782                    Type::Vector(VectorType { element_type }) => {
783                        // Ensure that the element type and the type of the value to push are the same
784                        self.assert_type(&arguments[1].0, element_type, arguments[1].1.span());
785                        Type::Unit
786                    }
787                    _ => {
788                        self.assert_vector_type(&arguments[0].0, arguments[0].1.span());
789                        Type::Err
790                    }
791                }
792            }
793            CoreFunction::VectorLen => {
794                self.check_access_allowed("Vector::len", true, function_span);
795
796                if arguments[0].0.is_vector() {
797                    Type::Integer(IntegerType::U32)
798                } else {
799                    self.assert_vector_type(&arguments[0].0, arguments[0].1.span());
800                    Type::Err
801                }
802            }
803            CoreFunction::VectorPop => {
804                self.check_access_allowed("Vector::pop", true, function_span);
805
806                if let Type::Vector(VectorType { element_type }) = &arguments[0].0 {
807                    Type::Optional(OptionalType { inner: Box::new(*element_type.clone()) })
808                } else {
809                    self.assert_vector_type(&arguments[0].0, arguments[0].1.span());
810                    Type::Err
811                }
812            }
813            CoreFunction::VectorSwapRemove => {
814                self.check_access_allowed("Vector::swap_remove", true, function_span);
815
816                if let Type::Vector(VectorType { element_type }) = &arguments[0].0 {
817                    *element_type.clone()
818                } else {
819                    self.assert_vector_type(&arguments[0].0, arguments[0].1.span());
820                    Type::Err
821                }
822            }
823            CoreFunction::VectorClear => {
824                if arguments[0].0.is_vector() {
825                    Type::Unit
826                } else {
827                    self.assert_vector_type(&arguments[0].0, arguments[0].1.span());
828                    Type::Err
829                }
830            }
831            CoreFunction::GroupToXCoordinate | CoreFunction::GroupToYCoordinate => {
832                // Check that the first argument is a group.
833                self.assert_type(&arguments[0].0, &Type::Group, arguments[0].1.span());
834                Type::Field
835            }
836            CoreFunction::ChaChaRand(type_) => type_.into(),
837            CoreFunction::SignatureVerify => {
838                // Check that the third argument is not a mapping nor a tuple. We have to do this
839                // before the other checks below to appease the borrow checker
840                assert_not_mapping_tuple_unit(&arguments[2].0, arguments[2].1.span());
841
842                // Check that the first argument is a signature.
843                self.assert_type(&arguments[0].0, &Type::Signature, arguments[0].1.span());
844                // Check that the second argument is an address.
845                self.assert_type(&arguments[1].0, &Type::Address, arguments[1].1.span());
846                Type::Boolean
847            }
848            CoreFunction::FutureAwait => Type::Unit,
849            CoreFunction::ProgramChecksum => {
850                // Get the argument type, expression, and span.
851                let (type_, expression) = &arguments[0];
852                let span = expression.span();
853                // Check that the expression is a program ID.
854                match expression {
855                    Expression::Literal(Literal { variant: LiteralVariant::Address(s), .. })
856                        if program_id_regex.is_match(s) => {}
857                    _ => {
858                        self.emit_err(TypeCheckerError::custom(
859                            "`Program::checksum` must be called on a program ID, e.g. `foo.aleo`",
860                            span,
861                        ));
862                    }
863                }
864                // Verify that the argument is a string.
865                self.assert_type(type_, &Type::Address, span);
866                // Return the type.
867                Type::Array(ArrayType::new(
868                    Type::Integer(IntegerType::U8),
869                    Expression::Literal(Literal::integer(
870                        IntegerType::U8,
871                        "32".to_string(),
872                        Default::default(),
873                        Default::default(),
874                    )),
875                ))
876            }
877            CoreFunction::ProgramEdition => {
878                // Get the argument type, expression, and span.
879                let (type_, expression) = &arguments[0];
880                let span = expression.span();
881                // Check that the expression is a member access.
882                match expression {
883                    Expression::Literal(Literal { variant: LiteralVariant::Address(s), .. })
884                        if program_id_regex.is_match(s) => {}
885                    _ => {
886                        self.emit_err(TypeCheckerError::custom(
887                            "`Program::edition` must be called on a program ID, e.g. `foo.aleo`",
888                            span,
889                        ));
890                    }
891                }
892                // Verify that the argument is a string.
893                self.assert_type(type_, &Type::Address, span);
894                // Return the type.
895                Type::Integer(IntegerType::U16)
896            }
897            CoreFunction::ProgramOwner => {
898                // Get the argument type, expression, and span.
899                let (type_, expression) = &arguments[0];
900                let span = expression.span();
901                // Check that the expression is a member access.
902                match expression {
903                    Expression::Literal(Literal { variant: LiteralVariant::Address(s), .. })
904                        if program_id_regex.is_match(s) => {}
905                    _ => {
906                        self.emit_err(TypeCheckerError::custom(
907                            "`Program::program_owner` must be called on a program ID, e.g. `foo.aleo`",
908                            span,
909                        ));
910                    }
911                }
912                // Verify that the argument is a string.
913                self.assert_type(type_, &Type::Address, span);
914                // Return the type.
915                Type::Address
916            }
917            CoreFunction::Serialize(variant) => {
918                // Determine the variant.
919                let is_raw = match variant {
920                    SerializeVariant::ToBits => false,
921                    SerializeVariant::ToBitsRaw => true,
922                };
923                // Get the input type.
924                let input_type = &arguments[0].0;
925
926                // A helper function to check that a type is an allowed literal type.
927                let is_allowed_literal_type = |type_: &Type| -> bool {
928                    matches!(
929                        type_,
930                        Type::Boolean
931                            | Type::Field
932                            | Type::Group
933                            | Type::Scalar
934                            | Type::Signature
935                            | Type::Address
936                            | Type::Integer(_)
937                            | Type::String
938                            | Type::Numeric
939                    )
940                };
941
942                // Check that the input type is an allowed literal or a (possibly multi-dimensional) array of literals.
943                let is_allowed = match input_type {
944                    Type::Array(array_type) => is_allowed_literal_type(array_type.base_element_type()),
945                    type_ => is_allowed_literal_type(type_),
946                };
947                if !is_allowed {
948                    self.emit_err(TypeCheckerError::type_should_be2(
949                        input_type,
950                        "a literal type or an (multi-dimensional) array of literal types",
951                        arguments[0].1.span(),
952                    ));
953                    return Type::Err;
954                }
955
956                // Get the size in bits.
957                let size_in_bits = match self.state.network {
958                    NetworkName::TestnetV0 => {
959                        input_type.size_in_bits::<TestnetV0, _>(is_raw, |_| bail!("structs are not supported"))
960                    }
961                    NetworkName::MainnetV0 => {
962                        input_type.size_in_bits::<MainnetV0, _>(is_raw, |_| bail!("structs are not supported"))
963                    }
964                    NetworkName::CanaryV0 => {
965                        input_type.size_in_bits::<CanaryV0, _>(is_raw, |_| bail!("structs are not supported"))
966                    }
967                };
968
969                if let Ok(size_in_bits) = size_in_bits {
970                    // Check that the size in bits is valid.
971                    let size_in_bits = if size_in_bits > self.limits.max_array_elements {
972                        self.emit_err(TypeCheckerError::custom(
973                        format!("The input type to `Serialize::*` is too large. Found {size_in_bits} bits, but the maximum allowed is {} bits.", self.limits.max_array_elements),
974                        arguments[0].1.span(),
975                    ));
976                        return Type::Err;
977                    } else if size_in_bits == 0 {
978                        self.emit_err(TypeCheckerError::custom(
979                            "The input type to `Serialize::*` is empty.",
980                            arguments[0].1.span(),
981                        ));
982                        return Type::Err;
983                    } else {
984                        u32::try_from(size_in_bits).expect("`max_array_elements` should fit in a u32")
985                    };
986
987                    // Return the array type.
988                    return Type::Array(ArrayType::bit_array(size_in_bits));
989                }
990
991                // Could not resolve the size in bits at this time.
992                Type::Err
993            }
994            CoreFunction::Deserialize(variant, type_) => {
995                // Determine the variant.
996                let is_raw = match variant {
997                    DeserializeVariant::FromBits => false,
998                    DeserializeVariant::FromBitsRaw => true,
999                };
1000                // Get the input type.
1001                let input_type = &arguments[0].0;
1002
1003                // Get the size in bits.
1004                let size_in_bits = match self.state.network {
1005                    NetworkName::TestnetV0 => {
1006                        type_.size_in_bits::<TestnetV0, _>(is_raw, |_| bail!("structs are not supported"))
1007                    }
1008                    NetworkName::MainnetV0 => {
1009                        type_.size_in_bits::<MainnetV0, _>(is_raw, |_| bail!("structs are not supported"))
1010                    }
1011                    NetworkName::CanaryV0 => {
1012                        type_.size_in_bits::<CanaryV0, _>(is_raw, |_| bail!("structs are not supported"))
1013                    }
1014                };
1015
1016                if let Ok(size_in_bits) = size_in_bits {
1017                    // Check that the size in bits is valid.
1018                    let size_in_bits = if size_in_bits > self.limits.max_array_elements {
1019                        self.emit_err(TypeCheckerError::custom(
1020                        format!("The output type of `Deserialize::*` is too large. Found {size_in_bits} bits, but the maximum allowed is {} bits.", self.limits.max_array_elements),
1021                        arguments[0].1.span(),
1022                    ));
1023                        return Type::Err;
1024                    } else if size_in_bits == 0 {
1025                        self.emit_err(TypeCheckerError::custom(
1026                            "The output type of `Deserialize::*` is empty.",
1027                            arguments[0].1.span(),
1028                        ));
1029                        return Type::Err;
1030                    } else {
1031                        u32::try_from(size_in_bits).expect("`max_array_elements` should fit in a u32")
1032                    };
1033
1034                    // Check that the input type is an array of the correct size.
1035                    let expected_type = Type::Array(ArrayType::bit_array(size_in_bits));
1036                    if !input_type.eq_flat_relaxed(&expected_type) {
1037                        self.emit_err(TypeCheckerError::type_should_be2(
1038                            input_type,
1039                            format!("an array of {size_in_bits} bits"),
1040                            arguments[0].1.span(),
1041                        ));
1042                        return Type::Err;
1043                    }
1044                };
1045
1046                type_.clone()
1047            }
1048            CoreFunction::CheatCodePrintMapping => {
1049                self.assert_mapping_type(&arguments[0].0, arguments[0].1.span());
1050                Type::Unit
1051            }
1052            CoreFunction::CheatCodeSetBlockHeight => {
1053                self.assert_type(&arguments[0].0, &Type::Integer(IntegerType::U32), arguments[0].1.span());
1054                Type::Unit
1055            }
1056            CoreFunction::CheatCodeSetBlockTimestamp => {
1057                self.assert_type(&arguments[0].0, &Type::Integer(IntegerType::I64), arguments[0].1.span());
1058                Type::Unit
1059            }
1060            CoreFunction::CheatCodeSetSigner => {
1061                // Assert that the argument is a string.
1062                self.assert_type(&arguments[0].0, &Type::String, arguments[0].1.span());
1063                // Validate that the argument is a valid private key.
1064                if let Expression::Literal(Literal { variant: LiteralVariant::String(s), .. }) = arguments[0].1 {
1065                    let s = s.replace("\"", "");
1066                    let is_err = match self.state.network {
1067                        NetworkName::TestnetV0 => PrivateKey::<TestnetV0>::from_str(&s).is_err(),
1068                        NetworkName::MainnetV0 => PrivateKey::<MainnetV0>::from_str(&s).is_err(),
1069                        NetworkName::CanaryV0 => PrivateKey::<CanaryV0>::from_str(&s).is_err(),
1070                    };
1071                    if is_err {
1072                        self.emit_err(TypeCheckerError::custom(
1073                            "`CheatCode::set_signer` must be called with a valid private key",
1074                            arguments[0].1.span(),
1075                        ));
1076                    }
1077                };
1078                Type::Unit
1079            }
1080        }
1081    }
1082
1083    /// Emits an error if the struct member is a record type.
1084    pub fn assert_member_is_not_record(&mut self, span: Span, parent: Symbol, type_: &Type) {
1085        match type_ {
1086            Type::Composite(struct_)
1087                if self
1088                    .lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1089                    .is_some_and(|struct_| struct_.is_record) =>
1090            {
1091                self.emit_err(TypeCheckerError::struct_or_record_cannot_contain_record(
1092                    parent,
1093                    struct_.path.clone(),
1094                    span,
1095                ))
1096            }
1097            Type::Tuple(tuple_type) => {
1098                for type_ in tuple_type.elements().iter() {
1099                    self.assert_member_is_not_record(span, parent, type_)
1100                }
1101            }
1102            _ => {} // Do nothing.
1103        }
1104    }
1105
1106    /// Emits an error if the type or its constituent types is not valid.
1107    pub fn assert_type_is_valid(&mut self, type_: &Type, span: Span) {
1108        match type_ {
1109            // Unit types may only appear as the return type of a function.
1110            Type::Unit => {
1111                self.emit_err(TypeCheckerError::unit_type_only_return(span));
1112            }
1113            // String types are temporarily disabled.
1114            Type::String => {
1115                self.emit_err(TypeCheckerError::strings_are_not_supported(span));
1116            }
1117            // Check that named composite type has been defined.
1118            Type::Composite(struct_)
1119                if self
1120                    .lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1121                    .is_none() =>
1122            {
1123                self.emit_err(TypeCheckerError::undefined_type(struct_.path.clone(), span));
1124            }
1125            // Check that the constituent types of the tuple are valid.
1126            Type::Tuple(tuple_type) => {
1127                for type_ in tuple_type.elements().iter() {
1128                    self.assert_type_is_valid(type_, span);
1129                }
1130            }
1131            // Check that the constituent types of mapping are valid.
1132            Type::Mapping(mapping_type) => {
1133                self.assert_type_is_valid(&mapping_type.key, span);
1134                self.assert_type_is_valid(&mapping_type.value, span);
1135            }
1136            // Check that the array element types are valid.
1137            Type::Array(array_type) => {
1138                // Check that the array length is valid.
1139
1140                if let Some(length) = array_type.length.as_u32() {
1141                    if length > self.limits.max_array_elements as u32 {
1142                        self.emit_err(TypeCheckerError::array_too_large(length, self.limits.max_array_elements, span));
1143                    }
1144                } else if let Expression::Literal(_) = &*array_type.length {
1145                    // Literal, but not valid u32 (e.g. too big or invalid format)
1146                    self.emit_err(TypeCheckerError::array_too_large_for_u32(span));
1147                }
1148                // else: not a literal, so defer for later
1149
1150                // Check that the array element type is valid.
1151                match array_type.element_type() {
1152                    // Array elements cannot be futures.
1153                    Type::Future(_) => self.emit_err(TypeCheckerError::array_element_cannot_be_future(span)),
1154                    // Array elements cannot be tuples.
1155                    Type::Tuple(_) => self.emit_err(TypeCheckerError::array_element_cannot_be_tuple(span)),
1156                    // Array elements cannot be records.
1157                    Type::Composite(struct_type) => {
1158                        // Look up the type.
1159                        if let Some(struct_) = self.lookup_struct(
1160                            struct_type.program.or(self.scope_state.program_name),
1161                            &struct_type.path.absolute_path(),
1162                        ) {
1163                            // Check that the type is not a record.
1164                            if struct_.is_record {
1165                                self.emit_err(TypeCheckerError::array_element_cannot_be_record(span));
1166                            }
1167                        }
1168                    }
1169                    _ => {} // Do nothing.
1170                }
1171                self.assert_type_is_valid(array_type.element_type(), span);
1172            }
1173
1174            Type::Optional(OptionalType { inner }) => {
1175                // Some types cannot be wrapped in an optional
1176                if self.disallowed_inside_optional(inner) {
1177                    self.emit_err(TypeCheckerError::optional_wrapping_unsupported(inner, span));
1178                }
1179
1180                // Validate inner type normally
1181                self.assert_type_is_valid(inner, span);
1182            }
1183
1184            Type::Address
1185            | Type::Boolean
1186            | Type::Composite(_)
1187            | Type::Field
1188            | Type::Future(_)
1189            | Type::Group
1190            | Type::Identifier(_)
1191            | Type::Integer(_)
1192            | Type::Scalar
1193            | Type::Signature
1194            | Type::Vector(_)
1195            | Type::Numeric
1196            | Type::Err => {} // Do nothing.
1197        }
1198    }
1199
1200    /// Can type `ty` be used inside an optional?
1201    fn disallowed_inside_optional(&mut self, ty: &Type) -> bool {
1202        match ty {
1203            Type::Unit
1204            | Type::Err
1205            | Type::Future(_)
1206            | Type::Identifier(_)
1207            | Type::Mapping(_)
1208            | Type::Optional(_)
1209            | Type::String
1210            | Type::Signature
1211            | Type::Tuple(_)
1212            | Type::Vector(_) => true,
1213
1214            Type::Composite(struct_type) => {
1215                if let Some(struct_) = self.lookup_struct(
1216                    struct_type.program.or(self.scope_state.program_name),
1217                    &struct_type.path.absolute_path(),
1218                ) {
1219                    if struct_.is_record {
1220                        return true;
1221                    }
1222
1223                    // recursively check all fields
1224                    for field in &struct_.members {
1225                        let field_ty = &field.type_;
1226                        // unwrap optional fields for the check
1227                        let ty_to_check = match field_ty {
1228                            Type::Optional(OptionalType { inner }) => inner,
1229                            _ => field_ty,
1230                        };
1231                        if self.disallowed_inside_optional(ty_to_check) {
1232                            return true;
1233                        }
1234                    }
1235                }
1236                false
1237            }
1238
1239            Type::Array(array_type) => {
1240                let elem_type = match array_type.element_type() {
1241                    Type::Optional(OptionalType { inner }) => inner,
1242                    other => other,
1243                };
1244                self.disallowed_inside_optional(elem_type)
1245            }
1246
1247            Type::Address
1248            | Type::Boolean
1249            | Type::Field
1250            | Type::Group
1251            | Type::Integer(_)
1252            | Type::Numeric
1253            | Type::Scalar => false,
1254        }
1255    }
1256
1257    /// Ensures the given type is valid for use in storage.
1258    /// Emits an error if the type or any of its inner types are invalid.
1259    pub fn assert_storage_type_is_valid(&mut self, type_: &Type, span: Span) {
1260        if type_.is_empty() {
1261            self.emit_err(TypeCheckerError::invalid_storage_type("A zero sized type", span));
1262        }
1263        match type_ {
1264            // Prohibited top-level kinds
1265            Type::Unit => {
1266                self.emit_err(TypeCheckerError::invalid_storage_type("unit", span));
1267            }
1268            Type::String => {
1269                self.emit_err(TypeCheckerError::invalid_storage_type("string", span));
1270            }
1271            Type::Signature => {
1272                self.emit_err(TypeCheckerError::invalid_storage_type("signature", span));
1273            }
1274            Type::Future(_) => {
1275                self.emit_err(TypeCheckerError::invalid_storage_type("future", span));
1276            }
1277            Type::Optional(_) => {
1278                self.emit_err(TypeCheckerError::invalid_storage_type("optional", span));
1279            }
1280            Type::Mapping(_) => {
1281                self.emit_err(TypeCheckerError::invalid_storage_type("mapping", span));
1282            }
1283            Type::Tuple(_) => {
1284                self.emit_err(TypeCheckerError::invalid_storage_type("tuple", span));
1285            }
1286
1287            // Structs (composites)
1288            Type::Composite(struct_type) => {
1289                if let Some(struct_) = self.lookup_struct(
1290                    struct_type.program.or(self.scope_state.program_name),
1291                    &struct_type.path.absolute_path(),
1292                ) {
1293                    if struct_.is_record {
1294                        self.emit_err(TypeCheckerError::invalid_storage_type("record", span));
1295                        return;
1296                    }
1297
1298                    // Recursively check fields.
1299                    for field in &struct_.members {
1300                        self.assert_storage_type_is_valid(&field.type_, span);
1301                    }
1302                } else {
1303                    self.emit_err(TypeCheckerError::invalid_storage_type("undefined struct", span));
1304                }
1305            }
1306
1307            // Arrays
1308            Type::Array(array_type) => {
1309                if let Some(length) = array_type.length.as_u32()
1310                    && (length == 0 || length > self.limits.max_array_elements as u32)
1311                {
1312                    self.emit_err(TypeCheckerError::invalid_storage_type("array", span));
1313                }
1314
1315                let element_ty = array_type.element_type();
1316                match element_ty {
1317                    Type::Future(_) => self.emit_err(TypeCheckerError::invalid_storage_type("future", span)),
1318                    Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_storage_type("tuple", span)),
1319                    Type::Optional(_) => self.emit_err(TypeCheckerError::invalid_storage_type("optional", span)),
1320                    _ => {}
1321                }
1322
1323                self.assert_storage_type_is_valid(element_ty, span);
1324            }
1325
1326            // Everything else (integers, bool, group, etc.)
1327            Type::Address
1328            | Type::Boolean
1329            | Type::Field
1330            | Type::Group
1331            | Type::Identifier(_)
1332            | Type::Integer(_)
1333            | Type::Scalar
1334            | Type::Numeric
1335            | Type::Err
1336            | Type::Vector(_) => {} // valid
1337        }
1338    }
1339
1340    /// Emits an error if the type is not a mapping.
1341    pub fn assert_mapping_type(&self, type_: &Type, span: Span) {
1342        if type_ != &Type::Err && !matches!(type_, Type::Mapping(_)) {
1343            self.emit_err(TypeCheckerError::type_should_be2(type_, "a mapping", span));
1344        }
1345    }
1346
1347    /// Emits an error if the type is not an optional.
1348    pub fn assert_optional_type(&self, type_: &Type, span: Span) {
1349        if type_ != &Type::Err && !matches!(type_, Type::Optional(_)) {
1350            self.emit_err(TypeCheckerError::type_should_be2(type_, "an optional", span));
1351        }
1352    }
1353
1354    /// Emits an error if the type is not a vector
1355    pub fn assert_vector_type(&self, type_: &Type, span: Span) {
1356        if type_ != &Type::Err && !matches!(type_, Type::Vector(_)) {
1357            self.emit_err(TypeCheckerError::type_should_be2(type_, "a vector", span));
1358        }
1359    }
1360
1361    /// Emits an error if the type is not a vector or a mapping.
1362    pub fn assert_vector_or_mapping_type(&self, type_: &Type, span: Span) {
1363        if type_ != &Type::Err && !matches!(type_, Type::Vector(_)) && !matches!(type_, Type::Mapping(_)) {
1364            self.emit_err(TypeCheckerError::type_should_be2(type_, "a vector or a mapping", span));
1365        }
1366    }
1367
1368    pub fn contains_optional_type(&mut self, ty: &Type) -> bool {
1369        let mut visited_paths = IndexSet::<Vec<Symbol>>::new();
1370        self.contains_optional_type_inner(ty, &mut visited_paths)
1371    }
1372
1373    fn contains_optional_type_inner(&mut self, ty: &Type, visited_paths: &mut IndexSet<Vec<Symbol>>) -> bool {
1374        match ty {
1375            Type::Optional(_) => true,
1376
1377            Type::Tuple(tuple) => tuple.elements.iter().any(|e| self.contains_optional_type_inner(e, visited_paths)),
1378
1379            Type::Array(array) => self.contains_optional_type_inner(&array.element_type, visited_paths),
1380
1381            Type::Composite(struct_type) => {
1382                let path = struct_type.path.absolute_path();
1383
1384                // Prevent revisiting the same type
1385                if !visited_paths.insert(path.clone()) {
1386                    return false;
1387                }
1388
1389                if let Some(comp) = self.lookup_struct(struct_type.program.or(self.scope_state.program_name), &path) {
1390                    comp.members
1391                        .iter()
1392                        .any(|Member { type_, .. }| self.contains_optional_type_inner(type_, visited_paths))
1393                } else {
1394                    false
1395                }
1396            }
1397
1398            _ => false,
1399        }
1400    }
1401
1402    pub fn assert_array_type(&self, type_: &Type, span: Span) {
1403        if type_ != &Type::Err && !matches!(type_, Type::Array(_)) {
1404            self.emit_err(TypeCheckerError::type_should_be2(type_, "an array", span));
1405        }
1406    }
1407
1408    /// Helper function to check that the input and output of function are valid
1409    pub fn check_function_signature(&mut self, function: &Function, is_stub: bool) {
1410        let function_path = self
1411            .scope_state
1412            .module_name
1413            .iter()
1414            .cloned()
1415            .chain(std::iter::once(function.identifier.name))
1416            .collect::<Vec<Symbol>>();
1417
1418        self.scope_state.variant = Some(function.variant);
1419
1420        let mut inferred_inputs: Vec<Type> = Vec::new();
1421
1422        if self.scope_state.variant == Some(Variant::AsyncFunction) && !self.scope_state.is_stub {
1423            // Async functions are not allowed to return values.
1424            if !function.output.is_empty() {
1425                self.emit_err(TypeCheckerError::async_function_cannot_return_value(function.span()));
1426            }
1427
1428            // Iterator over the `finalize` member (type Finalizer) of each async transition that calls
1429            // this async function.
1430            let mut caller_finalizers = self
1431                .async_function_callers
1432                .get(&Location::new(self.scope_state.program_name.unwrap(), function_path))
1433                .map(|callers| {
1434                    callers
1435                        .iter()
1436                        .flat_map(|caller| {
1437                            let caller = Location::new(caller.program, caller.path.clone());
1438                            self.state.symbol_table.lookup_function(&caller)
1439                        })
1440                        .flat_map(|fn_symbol| fn_symbol.finalizer.clone())
1441                })
1442                .into_iter()
1443                .flatten();
1444
1445            if let Some(first) = caller_finalizers.next() {
1446                inferred_inputs = first.inferred_inputs.clone();
1447
1448                // If any input is a future that doesn't have the same member type for all
1449                // finalizers, set that member to `Type::Err`.
1450                for finalizer in caller_finalizers {
1451                    assert_eq!(inferred_inputs.len(), finalizer.inferred_inputs.len());
1452                    for (t1, t2) in inferred_inputs.iter_mut().zip(finalizer.inferred_inputs.iter()) {
1453                        Self::merge_types(t1, t2);
1454                    }
1455                }
1456            } else {
1457                self.emit_warning(TypeCheckerWarning::async_function_is_never_called_by_transition_function(
1458                    function.identifier.name,
1459                    function.span(),
1460                ));
1461            }
1462        }
1463
1464        // Ensure that, if the function has generic const paramaters, then it must be an `inline`.
1465        // Otherwise, emit an error.
1466        if self.scope_state.variant != Some(Variant::Inline) && !function.const_parameters.is_empty() {
1467            self.emit_err(TypeCheckerError::only_inline_can_have_const_generics(function.identifier.span()));
1468        }
1469
1470        for const_param in &function.const_parameters {
1471            self.visit_type(const_param.type_());
1472
1473            // Restrictions for const parameters
1474            if !matches!(
1475                const_param.type_(),
1476                Type::Boolean | Type::Integer(_) | Type::Address | Type::Scalar | Type::Group | Type::Field
1477            ) {
1478                self.emit_err(TypeCheckerError::bad_const_generic_type(const_param.type_(), const_param.span()));
1479            }
1480
1481            // Add the input to the symbol table.
1482            if let Err(err) = self.state.symbol_table.insert_variable(
1483                self.scope_state.program_name.unwrap(),
1484                &[const_param.identifier().name],
1485                VariableSymbol {
1486                    type_: const_param.type_().clone(),
1487                    span: const_param.identifier.span(),
1488                    declaration: VariableType::ConstParameter,
1489                },
1490            ) {
1491                self.state.handler.emit_err(err);
1492            }
1493
1494            // Add the input to the type table.
1495            self.state.type_table.insert(const_param.identifier().id(), const_param.type_().clone());
1496        }
1497
1498        // Ensure there aren't too many inputs
1499        if function.input.len() > self.limits.max_inputs && function.variant != Variant::Inline {
1500            self.state.handler.emit_err(TypeCheckerError::function_has_too_many_inputs(
1501                function.variant,
1502                function.identifier,
1503                self.limits.max_inputs,
1504                function.input.len(),
1505                function.identifier.span,
1506            ));
1507        }
1508
1509        // The inputs should have access to the const parameters, so handle them after.
1510        for (i, input) in function.input.iter().enumerate() {
1511            self.visit_type(input.type_());
1512
1513            // No need to check compatibility of these types; that's already been done
1514            let table_type = inferred_inputs.get(i).unwrap_or_else(|| input.type_());
1515
1516            // Check that the type of input parameter is defined.
1517            self.assert_type_is_valid(table_type, input.span());
1518
1519            // Check that the type of the input parameter is not a tuple.
1520            if matches!(table_type, Type::Tuple(_)) {
1521                self.emit_err(TypeCheckerError::function_cannot_take_tuple_as_input(input.span()))
1522            }
1523
1524            // Check that the type of the input parameter does not contain an optional.
1525            if self.contains_optional_type(table_type)
1526                && matches!(function.variant, Variant::Transition | Variant::AsyncTransition | Variant::Function)
1527            {
1528                self.emit_err(TypeCheckerError::function_cannot_take_option_as_input(
1529                    input.identifier,
1530                    table_type,
1531                    input.span(),
1532                ))
1533            }
1534
1535            // Make sure only transitions can take a record as an input.
1536            if let Type::Composite(struct_) = table_type {
1537                // Throw error for undefined type.
1538                if !function.variant.is_transition() {
1539                    if let Some(elem) = self
1540                        .lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1541                    {
1542                        if elem.is_record {
1543                            self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(input.span()))
1544                        }
1545                    } else {
1546                        self.emit_err(TypeCheckerError::undefined_type(struct_.path.clone(), input.span()));
1547                    }
1548                }
1549            }
1550
1551            // This unwrap works since we assign to `variant` above.
1552            match self.scope_state.variant.unwrap() {
1553                // If the function is a transition function, then check that the parameter mode is not a constant.
1554                Variant::Transition | Variant::AsyncTransition if input.mode() == Mode::Constant => {
1555                    self.emit_err(TypeCheckerError::transition_function_inputs_cannot_be_const(input.span()))
1556                }
1557                // If the function is standard function or inline, then check that the parameters do not have an associated mode.
1558                Variant::Function | Variant::Inline if input.mode() != Mode::None => {
1559                    self.emit_err(TypeCheckerError::regular_function_inputs_cannot_have_modes(input.span()))
1560                }
1561                // If the function is an async function, then check that the input parameter is not constant or private.
1562                Variant::AsyncFunction if matches!(input.mode(), Mode::Constant | Mode::Private) => {
1563                    self.emit_err(TypeCheckerError::async_function_input_must_be_public(input.span()));
1564                }
1565                _ => {} // Do nothing.
1566            }
1567
1568            if matches!(table_type, Type::Future(..)) {
1569                // Future parameters may only appear in async functions.
1570                if !matches!(self.scope_state.variant, Some(Variant::AsyncFunction)) {
1571                    self.emit_err(TypeCheckerError::no_future_parameters(input.span()));
1572                }
1573            }
1574
1575            if !is_stub {
1576                // Add the input to the symbol table.
1577                if let Err(err) = self.state.symbol_table.insert_variable(
1578                    self.scope_state.program_name.unwrap(),
1579                    &[input.identifier().name],
1580                    VariableSymbol {
1581                        type_: table_type.clone(),
1582                        span: input.identifier.span(),
1583                        declaration: VariableType::Input(input.mode()),
1584                    },
1585                ) {
1586                    self.state.handler.emit_err(err);
1587                }
1588
1589                // Add the input to the type table.
1590                self.state.type_table.insert(input.identifier().id(), table_type.clone());
1591            }
1592        }
1593
1594        // Ensure there aren't too many outputs
1595        if function.output.len() > self.limits.max_outputs && function.variant != Variant::Inline {
1596            self.state.handler.emit_err(TypeCheckerError::function_has_too_many_outputs(
1597                function.variant,
1598                function.identifier,
1599                self.limits.max_outputs,
1600                function.output.len(),
1601                function.identifier.span,
1602            ));
1603        }
1604
1605        // Type check the function's return type.
1606        // Note that checking that each of the component types are defined is sufficient to check that `output_type` is defined.
1607        function.output.iter().enumerate().for_each(|(index, function_output)| {
1608            self.visit_type(&function_output.type_);
1609
1610            // If the function is not a transition function, then it cannot output a record.
1611            // Note that an external output must always be a record.
1612            if let Type::Composite(struct_) = function_output.type_.clone()
1613                && let Some(val) =
1614                    self.lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1615                && val.is_record
1616                && !function.variant.is_transition()
1617            {
1618                self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(function_output.span));
1619            }
1620
1621            // Check that the output type is valid.
1622            self.assert_type_is_valid(&function_output.type_, function_output.span);
1623
1624            // Check that the type of the output is not a tuple. This is necessary to forbid nested tuples.
1625            if matches!(&function_output.type_, Type::Tuple(_)) {
1626                self.emit_err(TypeCheckerError::nested_tuple_type(function_output.span))
1627            }
1628
1629            // Check that the type of the input parameter does not contain an optional.
1630            if self.contains_optional_type(&function_output.type_)
1631                && matches!(function.variant, Variant::Transition | Variant::AsyncTransition | Variant::Function)
1632            {
1633                self.emit_err(TypeCheckerError::function_cannot_return_option_as_output(
1634                    &function_output.type_,
1635                    function_output.span(),
1636                ))
1637            }
1638
1639            // Check that the mode of the output is valid.
1640            // For functions, only public and private outputs are allowed
1641            if function_output.mode == Mode::Constant {
1642                self.emit_err(TypeCheckerError::cannot_have_constant_output_mode(function_output.span));
1643            }
1644            // Async transitions must return exactly one future, and it must be in the last position.
1645            if self.scope_state.variant == Some(Variant::AsyncTransition)
1646                && ((index < function.output.len() - 1 && matches!(function_output.type_, Type::Future(_)))
1647                    || (index == function.output.len() - 1 && !matches!(function_output.type_, Type::Future(_))))
1648            {
1649                self.emit_err(TypeCheckerError::async_transition_invalid_output(function_output.span));
1650            }
1651            // If the function is not an async transition, then it cannot have a future as output.
1652            if !matches!(self.scope_state.variant, Some(Variant::AsyncTransition) | Some(Variant::Script))
1653                && matches!(function_output.type_, Type::Future(_))
1654            {
1655                self.emit_err(TypeCheckerError::only_async_transition_can_return_future(function_output.span));
1656            }
1657        });
1658
1659        self.visit_type(&function.output_type);
1660    }
1661
1662    /// Merge inferred types into `lhs`.
1663    ///
1664    /// That is, if `lhs` and `rhs` aren't equal, set `lhs` to Type::Err;
1665    /// or, if they're both futures, set any member of `lhs` that isn't
1666    /// equal to the equivalent member of `rhs` to `Type::Err`.
1667    fn merge_types(lhs: &mut Type, rhs: &Type) {
1668        if let Type::Future(f1) = lhs {
1669            if let Type::Future(f2) = rhs {
1670                for (i, type_) in f2.inputs.iter().enumerate() {
1671                    if let Some(lhs_type) = f1.inputs.get_mut(i) {
1672                        Self::merge_types(lhs_type, type_);
1673                    } else {
1674                        f1.inputs.push(Type::Err);
1675                    }
1676                }
1677            } else {
1678                *lhs = Type::Err;
1679            }
1680        } else if !lhs.eq_user(rhs) {
1681            *lhs = Type::Err;
1682        }
1683    }
1684
1685    /// Wrapper around lookup_struct that additionally records all structs that are used in the program.
1686    pub fn lookup_struct(&mut self, program: Option<Symbol>, name: &[Symbol]) -> Option<Composite> {
1687        let record_comp =
1688            program.and_then(|prog| self.state.symbol_table.lookup_record(&Location::new(prog, name.to_vec())));
1689        let comp = record_comp.or_else(|| self.state.symbol_table.lookup_struct(name));
1690        // Record the usage.
1691        if let Some(s) = comp {
1692            // If it's a struct or internal record, mark it used.
1693            if !s.is_record || program == self.scope_state.program_name {
1694                self.used_structs.insert(name.to_vec());
1695            }
1696        }
1697        comp.cloned()
1698    }
1699
1700    /// Inserts variable to symbol table.
1701    pub fn insert_variable(&mut self, inferred_type: Option<Type>, name: &Identifier, type_: Type, span: Span) {
1702        self.insert_symbol_conditional_scope(name.name);
1703
1704        let is_future = match &type_ {
1705            Type::Future(..) => true,
1706            Type::Tuple(tuple_type) if matches!(tuple_type.elements().last(), Some(Type::Future(..))) => true,
1707            _ => false,
1708        };
1709
1710        if is_future {
1711            // It can happen that the call location has not been set if there was an error
1712            // in the call that produced the Future.
1713            if let Some(call_location) = &self.scope_state.call_location {
1714                self.scope_state.futures.insert(name.name, call_location.clone());
1715            }
1716        }
1717
1718        let ty = match (is_future, inferred_type) {
1719            (false, _) => type_,
1720            (true, Some(inferred)) => inferred,
1721            (true, None) => unreachable!("Type checking guarantees the inferred type is present"),
1722        };
1723
1724        // Insert the variable into the symbol table.
1725        if let Err(err) = self.state.symbol_table.insert_variable(
1726            self.scope_state.program_name.unwrap(),
1727            &[name.name],
1728            VariableSymbol { type_: ty.clone(), span, declaration: VariableType::Mut },
1729        ) {
1730            self.state.handler.emit_err(err);
1731        }
1732    }
1733
1734    // Validates whether an access operation is allowed in the current function or block context.
1735    // This prevents illegal use of certain operations depending on whether the code is inside
1736    // an async function, an async block, or a finalize block.
1737    pub fn check_access_allowed(&mut self, name: &str, finalize_op: bool, span: Span) {
1738        // Case 1: Operation is not a finalize op, and we're inside an `async` function.
1739        if self.scope_state.variant == Some(Variant::AsyncFunction) && !finalize_op {
1740            self.state.handler.emit_err(TypeCheckerError::invalid_operation_inside_finalize(name, span));
1741        }
1742        // Case 2: Operation is not a finalize op, and we're inside an `async` block.
1743        else if self.async_block_id.is_some() && !finalize_op {
1744            self.state.handler.emit_err(TypeCheckerError::invalid_operation_inside_async_block(name, span));
1745        }
1746        // Case 3: Operation *is* a finalize op, but we're *not* inside an async context.
1747        else if !matches!(self.scope_state.variant, Some(Variant::AsyncFunction) | Some(Variant::Script))
1748            && self.async_block_id.is_none()
1749            && finalize_op
1750        {
1751            self.state.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(name, span));
1752        }
1753    }
1754
1755    pub fn is_external_record(&self, ty: &Type) -> bool {
1756        if let Type::Composite(typ) = &ty {
1757            let this_program = self.scope_state.program_name.unwrap();
1758            let program = typ.program.unwrap_or(this_program);
1759            program != this_program
1760                && self
1761                    .state
1762                    .symbol_table
1763                    .lookup_record(&Location::new(program, typ.path.absolute_path().to_vec()))
1764                    .is_some()
1765        } else {
1766            false
1767        }
1768    }
1769
1770    pub fn parse_integer_literal<I: FromStrRadix>(&self, raw_string: &str, span: Span, type_string: &str) {
1771        let string = raw_string.replace('_', "");
1772        if I::from_str_by_radix(&string).is_err() {
1773            self.state.handler.emit_err(TypeCheckerError::invalid_int_value(string, type_string, span));
1774        }
1775    }
1776
1777    // Emit an error and update `ty` to be `Type::Err` indicating that the type of the expression could not be inferred.
1778    // Also update `type_table` accordingly
1779    pub fn emit_inference_failure_error(&self, ty: &mut Type, expr: &Expression) {
1780        self.emit_err(TypeCheckerError::could_not_determine_type(expr.clone(), expr.span()));
1781        *ty = Type::Err;
1782        self.state.type_table.insert(expr.id(), Type::Err);
1783    }
1784
1785    // Given a `Literal` and its type, if the literal is a numeric `Unsuffixed` literal, ensure it's a valid literal
1786    // given the type. E.g., a `256` is not a valid `u8`.
1787    pub fn check_numeric_literal(&self, input: &Literal, ty: &Type) -> bool {
1788        if let Literal { variant: LiteralVariant::Unsuffixed(s), .. } = input {
1789            let span = input.span();
1790            let has_nondecimal_prefix =
1791                |s: &str| ["0x", "0o", "0b", "-0x", "-0o", "-0b"].iter().any(|p| s.starts_with(p));
1792
1793            macro_rules! parse_int {
1794                ($t:ty, $name:expr) => {
1795                    self.parse_integer_literal::<$t>(s, span, $name)
1796                };
1797            }
1798
1799            match ty {
1800                Type::Integer(kind) => match kind {
1801                    IntegerType::U8 => parse_int!(u8, "u8"),
1802                    IntegerType::U16 => parse_int!(u16, "u16"),
1803                    IntegerType::U32 => parse_int!(u32, "u32"),
1804                    IntegerType::U64 => parse_int!(u64, "u64"),
1805                    IntegerType::U128 => parse_int!(u128, "u128"),
1806                    IntegerType::I8 => parse_int!(i8, "i8"),
1807                    IntegerType::I16 => parse_int!(i16, "i16"),
1808                    IntegerType::I32 => parse_int!(i32, "i32"),
1809                    IntegerType::I64 => parse_int!(i64, "i64"),
1810                    IntegerType::I128 => parse_int!(i128, "i128"),
1811                },
1812                Type::Group => {
1813                    if has_nondecimal_prefix(s) {
1814                        // This is not checked in the parser for unsuffixed numerals. So do that here.
1815                        self.emit_err(TypeCheckerError::hexbin_literal_nonintegers(span));
1816                        return false;
1817                    } else {
1818                        let trimmed = s.trim_start_matches('-').trim_start_matches('0');
1819                        if !trimmed.is_empty()
1820                            && format!("{trimmed}group")
1821                                .parse::<snarkvm::prelude::Group<snarkvm::prelude::TestnetV0>>()
1822                                .is_err()
1823                        {
1824                            self.emit_err(TypeCheckerError::invalid_int_value(trimmed, "group", span));
1825                            return false;
1826                        }
1827                    }
1828                }
1829                Type::Field | Type::Scalar => {
1830                    if has_nondecimal_prefix(s) {
1831                        // This is not checked in the parser for unsuffixed numerals. So do that here.
1832                        self.emit_err(TypeCheckerError::hexbin_literal_nonintegers(span));
1833                        return false;
1834                    }
1835                }
1836                _ => {
1837                    // Other types aren't expected here
1838                }
1839            }
1840        }
1841        true
1842    }
1843}