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::CheatCodeSetSigner => {
1057                // Assert that the argument is a string.
1058                self.assert_type(&arguments[0].0, &Type::String, arguments[0].1.span());
1059                // Validate that the argument is a valid private key.
1060                if let Expression::Literal(Literal { variant: LiteralVariant::String(s), .. }) = arguments[0].1 {
1061                    let s = s.replace("\"", "");
1062                    let is_err = match self.state.network {
1063                        NetworkName::TestnetV0 => PrivateKey::<TestnetV0>::from_str(&s).is_err(),
1064                        NetworkName::MainnetV0 => PrivateKey::<MainnetV0>::from_str(&s).is_err(),
1065                        NetworkName::CanaryV0 => PrivateKey::<CanaryV0>::from_str(&s).is_err(),
1066                    };
1067                    if is_err {
1068                        self.emit_err(TypeCheckerError::custom(
1069                            "`CheatCode::set_signer` must be called with a valid private key",
1070                            arguments[0].1.span(),
1071                        ));
1072                    }
1073                };
1074                Type::Unit
1075            }
1076        }
1077    }
1078
1079    /// Emits an error if the struct member is a record type.
1080    pub fn assert_member_is_not_record(&mut self, span: Span, parent: Symbol, type_: &Type) {
1081        match type_ {
1082            Type::Composite(struct_)
1083                if self
1084                    .lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1085                    .is_some_and(|struct_| struct_.is_record) =>
1086            {
1087                self.emit_err(TypeCheckerError::struct_or_record_cannot_contain_record(
1088                    parent,
1089                    struct_.path.clone(),
1090                    span,
1091                ))
1092            }
1093            Type::Tuple(tuple_type) => {
1094                for type_ in tuple_type.elements().iter() {
1095                    self.assert_member_is_not_record(span, parent, type_)
1096                }
1097            }
1098            _ => {} // Do nothing.
1099        }
1100    }
1101
1102    /// Emits an error if the type or its constituent types is not valid.
1103    pub fn assert_type_is_valid(&mut self, type_: &Type, span: Span) {
1104        match type_ {
1105            // Unit types may only appear as the return type of a function.
1106            Type::Unit => {
1107                self.emit_err(TypeCheckerError::unit_type_only_return(span));
1108            }
1109            // String types are temporarily disabled.
1110            Type::String => {
1111                self.emit_err(TypeCheckerError::strings_are_not_supported(span));
1112            }
1113            // Check that named composite type has been defined.
1114            Type::Composite(struct_)
1115                if self
1116                    .lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1117                    .is_none() =>
1118            {
1119                self.emit_err(TypeCheckerError::undefined_type(struct_.path.clone(), span));
1120            }
1121            // Check that the constituent types of the tuple are valid.
1122            Type::Tuple(tuple_type) => {
1123                for type_ in tuple_type.elements().iter() {
1124                    self.assert_type_is_valid(type_, span);
1125                }
1126            }
1127            // Check that the constituent types of mapping are valid.
1128            Type::Mapping(mapping_type) => {
1129                self.assert_type_is_valid(&mapping_type.key, span);
1130                self.assert_type_is_valid(&mapping_type.value, span);
1131            }
1132            // Check that the array element types are valid.
1133            Type::Array(array_type) => {
1134                // Check that the array length is valid.
1135
1136                if let Some(length) = array_type.length.as_u32() {
1137                    if length == 0 {
1138                        self.emit_err(TypeCheckerError::array_empty(span));
1139                    } else if length > self.limits.max_array_elements as u32 {
1140                        self.emit_err(TypeCheckerError::array_too_large(length, self.limits.max_array_elements, span));
1141                    }
1142                } else if let Expression::Literal(_) = &*array_type.length {
1143                    // Literal, but not valid u32 (e.g. too big or invalid format)
1144                    self.emit_err(TypeCheckerError::array_too_large_for_u32(span));
1145                }
1146                // else: not a literal, so defer for later
1147
1148                // Check that the array element type is valid.
1149                match array_type.element_type() {
1150                    // Array elements cannot be futures.
1151                    Type::Future(_) => self.emit_err(TypeCheckerError::array_element_cannot_be_future(span)),
1152                    // Array elements cannot be tuples.
1153                    Type::Tuple(_) => self.emit_err(TypeCheckerError::array_element_cannot_be_tuple(span)),
1154                    // Array elements cannot be records.
1155                    Type::Composite(struct_type) => {
1156                        // Look up the type.
1157                        if let Some(struct_) = self.lookup_struct(
1158                            struct_type.program.or(self.scope_state.program_name),
1159                            &struct_type.path.absolute_path(),
1160                        ) {
1161                            // Check that the type is not a record.
1162                            if struct_.is_record {
1163                                self.emit_err(TypeCheckerError::array_element_cannot_be_record(span));
1164                            }
1165                        }
1166                    }
1167                    _ => {} // Do nothing.
1168                }
1169                self.assert_type_is_valid(array_type.element_type(), span);
1170            }
1171            Type::Optional(OptionalType { inner }) => {
1172                match &**inner {
1173                    Type::Composite(struct_type) => {
1174                        // Look up the type.
1175                        if let Some(struct_) = self.lookup_struct(
1176                            struct_type.program.or(self.scope_state.program_name),
1177                            &struct_type.path.absolute_path(),
1178                        ) {
1179                            // Check that the type is not a record.
1180                            if struct_.is_record {
1181                                self.emit_err(TypeCheckerError::optional_wrapping_of_records_unsupported(inner, span));
1182                            }
1183                        }
1184                    }
1185                    Type::Future(_)
1186                    | Type::Identifier(_)
1187                    | Type::Mapping(_)
1188                    | Type::Optional(_)
1189                    | Type::String
1190                    | Type::Address
1191                    | Type::Signature
1192                    | Type::Tuple(_) => {
1193                        self.emit_err(TypeCheckerError::optional_wrapping_unsupported(inner, span));
1194                    }
1195                    _ => self.assert_type_is_valid(inner, span),
1196                }
1197            }
1198            _ => {} // Do nothing.
1199        }
1200    }
1201
1202    /// Ensures the given type is valid for use in storage.
1203    /// Emits an error if the type or any of its inner types are invalid.
1204    pub fn assert_storage_type_is_valid(&mut self, type_: &Type, span: Span) {
1205        match type_ {
1206            // Prohibited top-level kinds
1207            Type::Unit => {
1208                self.emit_err(TypeCheckerError::invalid_storage_type("unit", span));
1209            }
1210            Type::String => {
1211                self.emit_err(TypeCheckerError::invalid_storage_type("string", span));
1212            }
1213            Type::Address => {
1214                self.emit_err(TypeCheckerError::invalid_storage_type("address", span));
1215            }
1216            Type::Signature => {
1217                self.emit_err(TypeCheckerError::invalid_storage_type("signature", span));
1218            }
1219            Type::Future(_) => {
1220                self.emit_err(TypeCheckerError::invalid_storage_type("future", span));
1221            }
1222            Type::Optional(_) => {
1223                self.emit_err(TypeCheckerError::invalid_storage_type("optional", span));
1224            }
1225            Type::Mapping(_) => {
1226                self.emit_err(TypeCheckerError::invalid_storage_type("mapping", span));
1227            }
1228            Type::Tuple(_) => {
1229                self.emit_err(TypeCheckerError::invalid_storage_type("tuple", span));
1230            }
1231
1232            // Structs (composites)
1233            Type::Composite(struct_type) => {
1234                if let Some(struct_) = self.lookup_struct(
1235                    struct_type.program.or(self.scope_state.program_name),
1236                    &struct_type.path.absolute_path(),
1237                ) {
1238                    if struct_.is_record {
1239                        self.emit_err(TypeCheckerError::invalid_storage_type("record", span));
1240                        return;
1241                    }
1242
1243                    // Recursively check fields.
1244                    for field in &struct_.members {
1245                        self.assert_storage_type_is_valid(&field.type_, span);
1246                    }
1247                } else {
1248                    self.emit_err(TypeCheckerError::invalid_storage_type("undefined struct", span));
1249                }
1250            }
1251
1252            // Arrays
1253            Type::Array(array_type) => {
1254                if let Some(length) = array_type.length.as_u32()
1255                    && (length == 0 || length > self.limits.max_array_elements as u32)
1256                {
1257                    self.emit_err(TypeCheckerError::invalid_storage_type("array", span));
1258                }
1259
1260                let element_ty = array_type.element_type();
1261                match element_ty {
1262                    Type::Future(_) => self.emit_err(TypeCheckerError::invalid_storage_type("future", span)),
1263                    Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_storage_type("tuple", span)),
1264                    Type::Optional(_) => self.emit_err(TypeCheckerError::invalid_storage_type("optional", span)),
1265                    _ => {}
1266                }
1267
1268                self.assert_storage_type_is_valid(element_ty, span);
1269            }
1270
1271            // Everything else (integers, bool, group, etc.)
1272            _ => {} // valid
1273        }
1274    }
1275
1276    /// Emits an error if the type is not a mapping.
1277    pub fn assert_mapping_type(&self, type_: &Type, span: Span) {
1278        if type_ != &Type::Err && !matches!(type_, Type::Mapping(_)) {
1279            self.emit_err(TypeCheckerError::type_should_be2(type_, "a mapping", span));
1280        }
1281    }
1282
1283    /// Emits an error if the type is not an optional.
1284    pub fn assert_optional_type(&self, type_: &Type, span: Span) {
1285        if type_ != &Type::Err && !matches!(type_, Type::Optional(_)) {
1286            self.emit_err(TypeCheckerError::type_should_be2(type_, "an optional", span));
1287        }
1288    }
1289
1290    /// Emits an error if the type is not a vector
1291    pub fn assert_vector_type(&self, type_: &Type, span: Span) {
1292        if type_ != &Type::Err && !matches!(type_, Type::Vector(_)) {
1293            self.emit_err(TypeCheckerError::type_should_be2(type_, "a vector", span));
1294        }
1295    }
1296
1297    /// Emits an error if the type is not a vector or a mapping.
1298    pub fn assert_vector_or_mapping_type(&self, type_: &Type, span: Span) {
1299        if type_ != &Type::Err && !matches!(type_, Type::Vector(_)) && !matches!(type_, Type::Mapping(_)) {
1300            self.emit_err(TypeCheckerError::type_should_be2(type_, "a vector or a mapping", span));
1301        }
1302    }
1303
1304    pub fn contains_optional_type(&mut self, ty: &Type) -> bool {
1305        let mut visited_paths = IndexSet::<Vec<Symbol>>::new();
1306        self.contains_optional_type_inner(ty, &mut visited_paths)
1307    }
1308
1309    fn contains_optional_type_inner(&mut self, ty: &Type, visited_paths: &mut IndexSet<Vec<Symbol>>) -> bool {
1310        match ty {
1311            Type::Optional(_) => true,
1312
1313            Type::Tuple(tuple) => tuple.elements.iter().any(|e| self.contains_optional_type_inner(e, visited_paths)),
1314
1315            Type::Array(array) => self.contains_optional_type_inner(&array.element_type, visited_paths),
1316
1317            Type::Composite(struct_type) => {
1318                let path = struct_type.path.absolute_path();
1319
1320                // Prevent revisiting the same type
1321                if !visited_paths.insert(path.clone()) {
1322                    return false;
1323                }
1324
1325                if let Some(comp) = self.lookup_struct(struct_type.program.or(self.scope_state.program_name), &path) {
1326                    comp.members
1327                        .iter()
1328                        .any(|Member { type_, .. }| self.contains_optional_type_inner(type_, visited_paths))
1329                } else {
1330                    false
1331                }
1332            }
1333
1334            _ => false,
1335        }
1336    }
1337
1338    pub fn assert_array_type(&self, type_: &Type, span: Span) {
1339        if type_ != &Type::Err && !matches!(type_, Type::Array(_)) {
1340            self.emit_err(TypeCheckerError::type_should_be2(type_, "an array", span));
1341        }
1342    }
1343
1344    /// Helper function to check that the input and output of function are valid
1345    pub fn check_function_signature(&mut self, function: &Function, is_stub: bool) {
1346        let function_path = self
1347            .scope_state
1348            .module_name
1349            .iter()
1350            .cloned()
1351            .chain(std::iter::once(function.identifier.name))
1352            .collect::<Vec<Symbol>>();
1353
1354        self.scope_state.variant = Some(function.variant);
1355
1356        let mut inferred_inputs: Vec<Type> = Vec::new();
1357
1358        if self.scope_state.variant == Some(Variant::AsyncFunction) && !self.scope_state.is_stub {
1359            // Async functions are not allowed to return values.
1360            if !function.output.is_empty() {
1361                self.emit_err(TypeCheckerError::async_function_cannot_return_value(function.span()));
1362            }
1363
1364            // Iterator over the `finalize` member (type Finalizer) of each async transition that calls
1365            // this async function.
1366            let mut caller_finalizers = self
1367                .async_function_callers
1368                .get(&Location::new(self.scope_state.program_name.unwrap(), function_path))
1369                .map(|callers| {
1370                    callers
1371                        .iter()
1372                        .flat_map(|caller| {
1373                            let caller = Location::new(caller.program, caller.path.clone());
1374                            self.state.symbol_table.lookup_function(&caller)
1375                        })
1376                        .flat_map(|fn_symbol| fn_symbol.finalizer.clone())
1377                })
1378                .into_iter()
1379                .flatten();
1380
1381            if let Some(first) = caller_finalizers.next() {
1382                inferred_inputs = first.inferred_inputs.clone();
1383
1384                // If any input is a future that doesn't have the same member type for all
1385                // finalizers, set that member to `Type::Err`.
1386                for finalizer in caller_finalizers {
1387                    assert_eq!(inferred_inputs.len(), finalizer.inferred_inputs.len());
1388                    for (t1, t2) in inferred_inputs.iter_mut().zip(finalizer.inferred_inputs.iter()) {
1389                        Self::merge_types(t1, t2);
1390                    }
1391                }
1392            } else {
1393                self.emit_warning(TypeCheckerWarning::async_function_is_never_called_by_transition_function(
1394                    function.identifier.name,
1395                    function.span(),
1396                ));
1397            }
1398        }
1399
1400        // Ensure that, if the function has generic const paramaters, then it must be an `inline`.
1401        // Otherwise, emit an error.
1402        if self.scope_state.variant != Some(Variant::Inline) && !function.const_parameters.is_empty() {
1403            self.emit_err(TypeCheckerError::only_inline_can_have_const_generics(function.identifier.span()));
1404        }
1405
1406        for const_param in &function.const_parameters {
1407            self.visit_type(const_param.type_());
1408
1409            // Restrictions for const parameters
1410            if !matches!(
1411                const_param.type_(),
1412                Type::Boolean | Type::Integer(_) | Type::Address | Type::Scalar | Type::Group | Type::Field
1413            ) {
1414                self.emit_err(TypeCheckerError::bad_const_generic_type(const_param.type_(), const_param.span()));
1415            }
1416
1417            // Add the input to the symbol table.
1418            if let Err(err) = self.state.symbol_table.insert_variable(
1419                self.scope_state.program_name.unwrap(),
1420                &[const_param.identifier().name],
1421                VariableSymbol {
1422                    type_: const_param.type_().clone(),
1423                    span: const_param.identifier.span(),
1424                    declaration: VariableType::ConstParameter,
1425                },
1426            ) {
1427                self.state.handler.emit_err(err);
1428            }
1429
1430            // Add the input to the type table.
1431            self.state.type_table.insert(const_param.identifier().id(), const_param.type_().clone());
1432        }
1433
1434        // Ensure there aren't too many inputs
1435        if function.input.len() > self.limits.max_inputs && function.variant != Variant::Inline {
1436            self.state.handler.emit_err(TypeCheckerError::function_has_too_many_inputs(
1437                function.variant,
1438                function.identifier,
1439                self.limits.max_inputs,
1440                function.input.len(),
1441                function.identifier.span,
1442            ));
1443        }
1444
1445        // The inputs should have access to the const parameters, so handle them after.
1446        for (i, input) in function.input.iter().enumerate() {
1447            self.visit_type(input.type_());
1448
1449            // No need to check compatibility of these types; that's already been done
1450            let table_type = inferred_inputs.get(i).unwrap_or_else(|| input.type_());
1451
1452            // Check that the type of input parameter is defined.
1453            self.assert_type_is_valid(table_type, input.span());
1454
1455            // Check that the type of the input parameter is not a tuple.
1456            if matches!(table_type, Type::Tuple(_)) {
1457                self.emit_err(TypeCheckerError::function_cannot_take_tuple_as_input(input.span()))
1458            }
1459
1460            // Check that the type of the input parameter does not contain an optional.
1461            if self.contains_optional_type(table_type)
1462                && matches!(function.variant, Variant::Transition | Variant::AsyncTransition | Variant::Function)
1463            {
1464                self.emit_err(TypeCheckerError::function_cannot_take_option_as_input(
1465                    input.identifier,
1466                    table_type,
1467                    input.span(),
1468                ))
1469            }
1470
1471            // Make sure only transitions can take a record as an input.
1472            if let Type::Composite(struct_) = table_type {
1473                // Throw error for undefined type.
1474                if !function.variant.is_transition() {
1475                    if let Some(elem) = self
1476                        .lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1477                    {
1478                        if elem.is_record {
1479                            self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(input.span()))
1480                        }
1481                    } else {
1482                        self.emit_err(TypeCheckerError::undefined_type(struct_.path.clone(), input.span()));
1483                    }
1484                }
1485            }
1486
1487            // This unwrap works since we assign to `variant` above.
1488            match self.scope_state.variant.unwrap() {
1489                // If the function is a transition function, then check that the parameter mode is not a constant.
1490                Variant::Transition | Variant::AsyncTransition if input.mode() == Mode::Constant => {
1491                    self.emit_err(TypeCheckerError::transition_function_inputs_cannot_be_const(input.span()))
1492                }
1493                // If the function is standard function or inline, then check that the parameters do not have an associated mode.
1494                Variant::Function | Variant::Inline if input.mode() != Mode::None => {
1495                    self.emit_err(TypeCheckerError::regular_function_inputs_cannot_have_modes(input.span()))
1496                }
1497                // If the function is an async function, then check that the input parameter is not constant or private.
1498                Variant::AsyncFunction if matches!(input.mode(), Mode::Constant | Mode::Private) => {
1499                    self.emit_err(TypeCheckerError::async_function_input_must_be_public(input.span()));
1500                }
1501                _ => {} // Do nothing.
1502            }
1503
1504            if matches!(table_type, Type::Future(..)) {
1505                // Future parameters may only appear in async functions.
1506                if !matches!(self.scope_state.variant, Some(Variant::AsyncFunction)) {
1507                    self.emit_err(TypeCheckerError::no_future_parameters(input.span()));
1508                }
1509            }
1510
1511            if !is_stub {
1512                // Add the input to the symbol table.
1513                if let Err(err) = self.state.symbol_table.insert_variable(
1514                    self.scope_state.program_name.unwrap(),
1515                    &[input.identifier().name],
1516                    VariableSymbol {
1517                        type_: table_type.clone(),
1518                        span: input.identifier.span(),
1519                        declaration: VariableType::Input(input.mode()),
1520                    },
1521                ) {
1522                    self.state.handler.emit_err(err);
1523                }
1524
1525                // Add the input to the type table.
1526                self.state.type_table.insert(input.identifier().id(), table_type.clone());
1527            }
1528        }
1529
1530        // Ensure there aren't too many outputs
1531        if function.output.len() > self.limits.max_outputs && function.variant != Variant::Inline {
1532            self.state.handler.emit_err(TypeCheckerError::function_has_too_many_outputs(
1533                function.variant,
1534                function.identifier,
1535                self.limits.max_outputs,
1536                function.output.len(),
1537                function.identifier.span,
1538            ));
1539        }
1540
1541        // Type check the function's return type.
1542        // Note that checking that each of the component types are defined is sufficient to check that `output_type` is defined.
1543        function.output.iter().enumerate().for_each(|(index, function_output)| {
1544            self.visit_type(&function_output.type_);
1545
1546            // If the function is not a transition function, then it cannot output a record.
1547            // Note that an external output must always be a record.
1548            if let Type::Composite(struct_) = function_output.type_.clone()
1549                && let Some(val) =
1550                    self.lookup_struct(struct_.program.or(self.scope_state.program_name), &struct_.path.absolute_path())
1551                && val.is_record
1552                && !function.variant.is_transition()
1553            {
1554                self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(function_output.span));
1555            }
1556
1557            // Check that the output type is valid.
1558            self.assert_type_is_valid(&function_output.type_, function_output.span);
1559
1560            // Check that the type of the output is not a tuple. This is necessary to forbid nested tuples.
1561            if matches!(&function_output.type_, Type::Tuple(_)) {
1562                self.emit_err(TypeCheckerError::nested_tuple_type(function_output.span))
1563            }
1564
1565            // Check that the type of the input parameter does not contain an optional.
1566            if self.contains_optional_type(&function_output.type_)
1567                && matches!(function.variant, Variant::Transition | Variant::AsyncTransition | Variant::Function)
1568            {
1569                self.emit_err(TypeCheckerError::function_cannot_return_option_as_output(
1570                    &function_output.type_,
1571                    function_output.span(),
1572                ))
1573            }
1574
1575            // Check that the mode of the output is valid.
1576            // For functions, only public and private outputs are allowed
1577            if function_output.mode == Mode::Constant {
1578                self.emit_err(TypeCheckerError::cannot_have_constant_output_mode(function_output.span));
1579            }
1580            // Async transitions must return exactly one future, and it must be in the last position.
1581            if self.scope_state.variant == Some(Variant::AsyncTransition)
1582                && ((index < function.output.len() - 1 && matches!(function_output.type_, Type::Future(_)))
1583                    || (index == function.output.len() - 1 && !matches!(function_output.type_, Type::Future(_))))
1584            {
1585                self.emit_err(TypeCheckerError::async_transition_invalid_output(function_output.span));
1586            }
1587            // If the function is not an async transition, then it cannot have a future as output.
1588            if !matches!(self.scope_state.variant, Some(Variant::AsyncTransition) | Some(Variant::Script))
1589                && matches!(function_output.type_, Type::Future(_))
1590            {
1591                self.emit_err(TypeCheckerError::only_async_transition_can_return_future(function_output.span));
1592            }
1593        });
1594
1595        self.visit_type(&function.output_type);
1596    }
1597
1598    /// Merge inferred types into `lhs`.
1599    ///
1600    /// That is, if `lhs` and `rhs` aren't equal, set `lhs` to Type::Err;
1601    /// or, if they're both futures, set any member of `lhs` that isn't
1602    /// equal to the equivalent member of `rhs` to `Type::Err`.
1603    fn merge_types(lhs: &mut Type, rhs: &Type) {
1604        if let Type::Future(f1) = lhs {
1605            if let Type::Future(f2) = rhs {
1606                for (i, type_) in f2.inputs.iter().enumerate() {
1607                    if let Some(lhs_type) = f1.inputs.get_mut(i) {
1608                        Self::merge_types(lhs_type, type_);
1609                    } else {
1610                        f1.inputs.push(Type::Err);
1611                    }
1612                }
1613            } else {
1614                *lhs = Type::Err;
1615            }
1616        } else if !lhs.eq_user(rhs) {
1617            *lhs = Type::Err;
1618        }
1619    }
1620
1621    /// Wrapper around lookup_struct that additionally records all structs that are used in the program.
1622    pub fn lookup_struct(&mut self, program: Option<Symbol>, name: &[Symbol]) -> Option<Composite> {
1623        let record_comp =
1624            program.and_then(|prog| self.state.symbol_table.lookup_record(&Location::new(prog, name.to_vec())));
1625        let comp = record_comp.or_else(|| self.state.symbol_table.lookup_struct(name));
1626        // Record the usage.
1627        if let Some(s) = comp {
1628            // If it's a struct or internal record, mark it used.
1629            if !s.is_record || program == self.scope_state.program_name {
1630                self.used_structs.insert(name.to_vec());
1631            }
1632        }
1633        comp.cloned()
1634    }
1635
1636    /// Inserts variable to symbol table.
1637    pub fn insert_variable(&mut self, inferred_type: Option<Type>, name: &Identifier, type_: Type, span: Span) {
1638        self.insert_symbol_conditional_scope(name.name);
1639
1640        let is_future = match &type_ {
1641            Type::Future(..) => true,
1642            Type::Tuple(tuple_type) if matches!(tuple_type.elements().last(), Some(Type::Future(..))) => true,
1643            _ => false,
1644        };
1645
1646        if is_future {
1647            // It can happen that the call location has not been set if there was an error
1648            // in the call that produced the Future.
1649            if let Some(call_location) = &self.scope_state.call_location {
1650                self.scope_state.futures.insert(name.name, call_location.clone());
1651            }
1652        }
1653
1654        let ty = match (is_future, inferred_type) {
1655            (false, _) => type_,
1656            (true, Some(inferred)) => inferred,
1657            (true, None) => unreachable!("Type checking guarantees the inferred type is present"),
1658        };
1659
1660        // Insert the variable into the symbol table.
1661        if let Err(err) = self.state.symbol_table.insert_variable(
1662            self.scope_state.program_name.unwrap(),
1663            &[name.name],
1664            VariableSymbol { type_: ty.clone(), span, declaration: VariableType::Mut },
1665        ) {
1666            self.state.handler.emit_err(err);
1667        }
1668    }
1669
1670    // Validates whether an access operation is allowed in the current function or block context.
1671    // This prevents illegal use of certain operations depending on whether the code is inside
1672    // an async function, an async block, or a finalize block.
1673    pub fn check_access_allowed(&mut self, name: &str, finalize_op: bool, span: Span) {
1674        // Case 1: Operation is not a finalize op, and we're inside an `async` function.
1675        if self.scope_state.variant == Some(Variant::AsyncFunction) && !finalize_op {
1676            self.state.handler.emit_err(TypeCheckerError::invalid_operation_inside_finalize(name, span));
1677        }
1678        // Case 2: Operation is not a finalize op, and we're inside an `async` block.
1679        else if self.async_block_id.is_some() && !finalize_op {
1680            self.state.handler.emit_err(TypeCheckerError::invalid_operation_inside_async_block(name, span));
1681        }
1682        // Case 3: Operation *is* a finalize op, but we're *not* inside an async context.
1683        else if !matches!(self.scope_state.variant, Some(Variant::AsyncFunction) | Some(Variant::Script))
1684            && self.async_block_id.is_none()
1685            && finalize_op
1686        {
1687            self.state.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(name, span));
1688        }
1689    }
1690
1691    pub fn is_external_record(&self, ty: &Type) -> bool {
1692        if let Type::Composite(typ) = &ty {
1693            let this_program = self.scope_state.program_name.unwrap();
1694            let program = typ.program.unwrap_or(this_program);
1695            program != this_program
1696                && self
1697                    .state
1698                    .symbol_table
1699                    .lookup_record(&Location::new(program, typ.path.absolute_path().to_vec()))
1700                    .is_some()
1701        } else {
1702            false
1703        }
1704    }
1705
1706    pub fn parse_integer_literal<I: FromStrRadix>(&self, raw_string: &str, span: Span, type_string: &str) {
1707        let string = raw_string.replace('_', "");
1708        if I::from_str_by_radix(&string).is_err() {
1709            self.state.handler.emit_err(TypeCheckerError::invalid_int_value(string, type_string, span));
1710        }
1711    }
1712
1713    // Emit an error and update `ty` to be `Type::Err` indicating that the type of the expression could not be inferred.
1714    // Also update `type_table` accordingly
1715    pub fn emit_inference_failure_error(&self, ty: &mut Type, expr: &Expression) {
1716        self.emit_err(TypeCheckerError::could_not_determine_type(expr.clone(), expr.span()));
1717        *ty = Type::Err;
1718        self.state.type_table.insert(expr.id(), Type::Err);
1719    }
1720
1721    // Given a `Literal` and its type, if the literal is a numeric `Unsuffixed` literal, ensure it's a valid literal
1722    // given the type. E.g., a `256` is not a valid `u8`.
1723    pub fn check_numeric_literal(&self, input: &Literal, ty: &Type) -> bool {
1724        if let Literal { variant: LiteralVariant::Unsuffixed(s), .. } = input {
1725            let span = input.span();
1726            let has_nondecimal_prefix =
1727                |s: &str| ["0x", "0o", "0b", "-0x", "-0o", "-0b"].iter().any(|p| s.starts_with(p));
1728
1729            macro_rules! parse_int {
1730                ($t:ty, $name:expr) => {
1731                    self.parse_integer_literal::<$t>(s, span, $name)
1732                };
1733            }
1734
1735            match ty {
1736                Type::Integer(kind) => match kind {
1737                    IntegerType::U8 => parse_int!(u8, "u8"),
1738                    IntegerType::U16 => parse_int!(u16, "u16"),
1739                    IntegerType::U32 => parse_int!(u32, "u32"),
1740                    IntegerType::U64 => parse_int!(u64, "u64"),
1741                    IntegerType::U128 => parse_int!(u128, "u128"),
1742                    IntegerType::I8 => parse_int!(i8, "i8"),
1743                    IntegerType::I16 => parse_int!(i16, "i16"),
1744                    IntegerType::I32 => parse_int!(i32, "i32"),
1745                    IntegerType::I64 => parse_int!(i64, "i64"),
1746                    IntegerType::I128 => parse_int!(i128, "i128"),
1747                },
1748                Type::Group => {
1749                    if has_nondecimal_prefix(s) {
1750                        // This is not checked in the parser for unsuffixed numerals. So do that here.
1751                        self.emit_err(TypeCheckerError::hexbin_literal_nonintegers(span));
1752                        return false;
1753                    } else {
1754                        let trimmed = s.trim_start_matches('-').trim_start_matches('0');
1755                        if !trimmed.is_empty()
1756                            && format!("{trimmed}group")
1757                                .parse::<snarkvm::prelude::Group<snarkvm::prelude::TestnetV0>>()
1758                                .is_err()
1759                        {
1760                            self.emit_err(TypeCheckerError::invalid_int_value(trimmed, "group", span));
1761                            return false;
1762                        }
1763                    }
1764                }
1765                Type::Field | Type::Scalar => {
1766                    if has_nondecimal_prefix(s) {
1767                        // This is not checked in the parser for unsuffixed numerals. So do that here.
1768                        self.emit_err(TypeCheckerError::hexbin_literal_nonintegers(span));
1769                        return false;
1770                    }
1771                }
1772                _ => {
1773                    // Other types aren't expected here
1774                }
1775            }
1776        }
1777        true
1778    }
1779}