leo_ast/types/
type_.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::{ArrayType, CompositeType, FutureType, Identifier, IntegerType, MappingType, Path, TupleType};
18
19use itertools::Itertools;
20use leo_span::Symbol;
21use serde::{Deserialize, Serialize};
22use snarkvm::prelude::{
23    Network,
24    PlaintextType,
25    PlaintextType::{Array, Literal, Struct},
26};
27use std::fmt;
28
29/// Explicit type used for defining a variable or expression type
30#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
31pub enum Type {
32    /// The `address` type.
33    Address,
34    /// The array type.
35    Array(ArrayType),
36    /// The `bool` type.
37    Boolean,
38    /// The `struct` type.
39    Composite(CompositeType),
40    /// The `field` type.
41    Field,
42    /// The `future` type.
43    Future(FutureType),
44    /// The `group` type.
45    Group,
46    /// A reference to a built in type.
47    Identifier(Identifier),
48    /// An integer type.
49    Integer(IntegerType),
50    /// A mapping type.
51    Mapping(MappingType),
52    /// The `scalar` type.
53    Scalar,
54    /// The `signature` type.
55    Signature,
56    /// The `string` type.
57    String,
58    /// A static tuple of at least one type.
59    Tuple(TupleType),
60    /// Numeric type which should be resolved to `Field`, `Group`, `Integer(_)`, or `Scalar`.
61    Numeric,
62    /// The `unit` type.
63    Unit,
64    /// Placeholder for a type that could not be resolved or was not well-formed.
65    /// Will eventually lead to a compile error.
66    #[default]
67    Err,
68}
69
70impl Type {
71    /// Returns `true` if the self `Type` is equal to the other `Type` in all aspects besides composite program of origin.
72    ///
73    /// In the case of futures, it also makes sure that if both are not explicit, they are equal.
74    ///
75    /// Flattens array syntax: `[[u8; 1]; 2] == [u8; (2, 1)] == true`
76    ///
77    /// Composite types are considered equal if their names match. If either side still has const generic arguments,
78    /// they are treated as equal unconditionally since monomorphization and other passes of type-checking will handle
79    /// mismatches later.
80    pub fn eq_flat_relaxed(&self, other: &Self) -> bool {
81        match (self, other) {
82            (Type::Address, Type::Address)
83            | (Type::Boolean, Type::Boolean)
84            | (Type::Field, Type::Field)
85            | (Type::Group, Type::Group)
86            | (Type::Scalar, Type::Scalar)
87            | (Type::Signature, Type::Signature)
88            | (Type::String, Type::String)
89            | (Type::Unit, Type::Unit) => true,
90            (Type::Array(left), Type::Array(right)) => {
91                // Two arrays are equal if their element types are the same and if their lengths
92                // are the same, assuming the lengths can be extracted as `u32`.
93                (match (left.length.as_u32(), right.length.as_u32()) {
94                    (Some(l1), Some(l2)) => l1 == l2,
95                    _ => {
96                        // An array with an undetermined length (e.g., one that depends on a `const`) is considered
97                        // equal to other arrays because their lengths _may_ eventually be proven equal.
98                        true
99                    }
100                }) && left.element_type().eq_flat_relaxed(right.element_type())
101            }
102            (Type::Identifier(left), Type::Identifier(right)) => left.matches(right),
103            (Type::Integer(left), Type::Integer(right)) => left.eq(right),
104            (Type::Mapping(left), Type::Mapping(right)) => {
105                left.key.eq_flat_relaxed(&right.key) && left.value.eq_flat_relaxed(&right.value)
106            }
107            (Type::Tuple(left), Type::Tuple(right)) if left.length() == right.length() => left
108                .elements()
109                .iter()
110                .zip_eq(right.elements().iter())
111                .all(|(left_type, right_type)| left_type.eq_flat_relaxed(right_type)),
112            (Type::Composite(left), Type::Composite(right)) => {
113                // If either composite still has const generic arguments, treat them as equal.
114                // Type checking will run again after monomorphization.
115                if !left.const_arguments.is_empty() || !right.const_arguments.is_empty() {
116                    return true;
117                }
118
119                // Two composite types are the same if their programs and their _absolute_ paths match.
120                // If the absolute paths are not available, then we really can't compare the two
121                // types and we just return `false` to be conservative.
122                (left.program == right.program)
123                    && match (&left.path.try_absolute_path(), &right.path.try_absolute_path()) {
124                        (Some(l), Some(r)) => l == r,
125                        _ => false,
126                    }
127            }
128            // Don't type check when type hasn't been explicitly defined.
129            (Type::Future(left), Type::Future(right)) if !left.is_explicit || !right.is_explicit => true,
130            (Type::Future(left), Type::Future(right)) if left.inputs.len() == right.inputs.len() => left
131                .inputs()
132                .iter()
133                .zip_eq(right.inputs().iter())
134                .all(|(left_type, right_type)| left_type.eq_flat_relaxed(right_type)),
135            _ => false,
136        }
137    }
138
139    pub fn from_snarkvm<N: Network>(t: &PlaintextType<N>, program: Option<Symbol>) -> Self {
140        match t {
141            Literal(lit) => match lit {
142                snarkvm::prelude::LiteralType::Address => Type::Address,
143                snarkvm::prelude::LiteralType::Boolean => Type::Boolean,
144                snarkvm::prelude::LiteralType::Field => Type::Field,
145                snarkvm::prelude::LiteralType::Group => Type::Group,
146                snarkvm::prelude::LiteralType::U8 => Type::Integer(IntegerType::U8),
147                snarkvm::prelude::LiteralType::U16 => Type::Integer(IntegerType::U16),
148                snarkvm::prelude::LiteralType::U32 => Type::Integer(IntegerType::U32),
149                snarkvm::prelude::LiteralType::U64 => Type::Integer(IntegerType::U64),
150                snarkvm::prelude::LiteralType::U128 => Type::Integer(IntegerType::U128),
151                snarkvm::prelude::LiteralType::I8 => Type::Integer(IntegerType::I8),
152                snarkvm::prelude::LiteralType::I16 => Type::Integer(IntegerType::I16),
153                snarkvm::prelude::LiteralType::I32 => Type::Integer(IntegerType::I32),
154                snarkvm::prelude::LiteralType::I64 => Type::Integer(IntegerType::I64),
155                snarkvm::prelude::LiteralType::I128 => Type::Integer(IntegerType::I128),
156                snarkvm::prelude::LiteralType::Scalar => Type::Scalar,
157                snarkvm::prelude::LiteralType::Signature => Type::Signature,
158                snarkvm::prelude::LiteralType::String => Type::String,
159            },
160            Struct(s) => Type::Composite(CompositeType {
161                path: {
162                    let ident = Identifier::from(s);
163                    Path::from(ident).with_absolute_path(Some(vec![ident.name]))
164                },
165                const_arguments: Vec::new(),
166                program,
167            }),
168            Array(array) => Type::Array(ArrayType::from_snarkvm(array, program)),
169        }
170    }
171}
172
173impl fmt::Display for Type {
174    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175        match *self {
176            Type::Address => write!(f, "address"),
177            Type::Array(ref array_type) => write!(f, "{array_type}"),
178            Type::Boolean => write!(f, "bool"),
179            Type::Field => write!(f, "field"),
180            Type::Future(ref future_type) => write!(f, "{future_type}"),
181            Type::Group => write!(f, "group"),
182            Type::Identifier(ref variable) => write!(f, "{variable}"),
183            Type::Integer(ref integer_type) => write!(f, "{integer_type}"),
184            Type::Mapping(ref mapping_type) => write!(f, "{mapping_type}"),
185            Type::Scalar => write!(f, "scalar"),
186            Type::Signature => write!(f, "signature"),
187            Type::String => write!(f, "string"),
188            Type::Composite(ref struct_type) => write!(f, "{struct_type}"),
189            Type::Tuple(ref tuple) => write!(f, "{tuple}"),
190            Type::Numeric => write!(f, "numeric"),
191            Type::Unit => write!(f, "()"),
192            Type::Err => write!(f, "error"),
193        }
194    }
195}