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