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::{
18    ArrayType,
19    CompositeType,
20    FutureType,
21    Identifier,
22    IntegerType,
23    MappingType,
24    OptionalType,
25    Path,
26    TupleType,
27};
28
29use itertools::Itertools;
30use leo_span::Symbol;
31use serde::{Deserialize, Serialize};
32use snarkvm::prelude::{
33    Network,
34    PlaintextType,
35    PlaintextType::{Array, Literal, Struct},
36};
37use std::fmt;
38
39/// Explicit type used for defining a variable or expression type
40#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
41pub enum Type {
42    /// The `address` type.
43    Address,
44    /// The array type.
45    Array(ArrayType),
46    /// The `bool` type.
47    Boolean,
48    /// The `struct` type.
49    Composite(CompositeType),
50    /// The `field` type.
51    Field,
52    /// The `future` type.
53    Future(FutureType),
54    /// The `group` type.
55    Group,
56    /// A reference to a built in type.
57    Identifier(Identifier),
58    /// An integer type.
59    Integer(IntegerType),
60    /// A mapping type.
61    Mapping(MappingType),
62    /// A nullable type.
63    Optional(OptionalType),
64    /// The `scalar` type.
65    Scalar,
66    /// The `signature` type.
67    Signature,
68    /// The `string` type.
69    String,
70    /// A static tuple of at least one type.
71    Tuple(TupleType),
72    /// Numeric type which should be resolved to `Field`, `Group`, `Integer(_)`, or `Scalar`.
73    Numeric,
74    /// The `unit` type.
75    Unit,
76    /// Placeholder for a type that could not be resolved or was not well-formed.
77    /// Will eventually lead to a compile error.
78    #[default]
79    Err,
80}
81
82impl Type {
83    /// Are the types considered equal as far as the Leo user is concerned?
84    ///
85    /// In particular, any comparison involving an `Err` is `true`, and Futures which aren't explicit compare equal to
86    /// other Futures.
87    ///
88    /// An array with an undetermined length (e.g., one that depends on a `const`) is considered equal to other arrays
89    /// if their element types match. This allows const propagation to potentially resolve the length before type
90    /// checking is performed again.
91    ///
92    /// Composite types are considered equal if their names and resolved program names match. If either side still has
93    /// const generic arguments, they are treated as equal unconditionally since monomorphization and other passes of
94    /// type-checking will handle mismatches later.
95    pub fn eq_user(&self, other: &Type) -> bool {
96        match (self, other) {
97            (Type::Err, _)
98            | (_, Type::Err)
99            | (Type::Address, Type::Address)
100            | (Type::Boolean, Type::Boolean)
101            | (Type::Field, Type::Field)
102            | (Type::Group, Type::Group)
103            | (Type::Scalar, Type::Scalar)
104            | (Type::Signature, Type::Signature)
105            | (Type::String, Type::String)
106            | (Type::Unit, Type::Unit) => true,
107            (Type::Array(left), Type::Array(right)) => {
108                (match (left.length.as_u32(), right.length.as_u32()) {
109                    (Some(l1), Some(l2)) => l1 == l2,
110                    _ => {
111                        // An array with an undetermined length (e.g., one that depends on a `const`) is considered
112                        // equal to other arrays because their lengths _may_ eventually be proven equal.
113                        true
114                    }
115                }) && left.element_type().eq_user(right.element_type())
116            }
117            (Type::Identifier(left), Type::Identifier(right)) => left.name == right.name,
118            (Type::Integer(left), Type::Integer(right)) => left == right,
119            (Type::Mapping(left), Type::Mapping(right)) => {
120                left.key.eq_user(&right.key) && left.value.eq_user(&right.value)
121            }
122            (Type::Optional(left), Type::Optional(right)) => left.inner.eq_user(&right.inner),
123            (Type::Tuple(left), Type::Tuple(right)) if left.length() == right.length() => left
124                .elements()
125                .iter()
126                .zip_eq(right.elements().iter())
127                .all(|(left_type, right_type)| left_type.eq_user(right_type)),
128            (Type::Composite(left), Type::Composite(right)) => {
129                // If either composite still has const generic arguments, treat them as equal.
130                // Type checking will run again after monomorphization.
131                if !left.const_arguments.is_empty() || !right.const_arguments.is_empty() {
132                    return true;
133                }
134
135                // Two composite types are the same if their programs and their _absolute_ paths match.
136                (left.program == right.program)
137                    && match (&left.path.try_absolute_path(), &right.path.try_absolute_path()) {
138                        (Some(l), Some(r)) => l == r,
139                        _ => false,
140                    }
141            }
142            (Type::Future(left), Type::Future(right)) if !left.is_explicit || !right.is_explicit => true,
143            (Type::Future(left), Type::Future(right)) if left.inputs.len() == right.inputs.len() => left
144                .inputs()
145                .iter()
146                .zip_eq(right.inputs().iter())
147                .all(|(left_type, right_type)| left_type.eq_user(right_type)),
148            _ => false,
149        }
150    }
151
152    /// Returns `true` if the self `Type` is equal to the other `Type` in all aspects besides composite program of origin.
153    ///
154    /// In the case of futures, it also makes sure that if both are not explicit, they are equal.
155    ///
156    /// Flattens array syntax: `[[u8; 1]; 2] == [u8; (2, 1)] == true`
157    ///
158    /// Composite types are considered equal if their names match. If either side still has const generic arguments,
159    /// they are treated as equal unconditionally since monomorphization and other passes of type-checking will handle
160    /// mismatches later.
161    pub fn eq_flat_relaxed(&self, other: &Self) -> bool {
162        match (self, other) {
163            (Type::Address, Type::Address)
164            | (Type::Boolean, Type::Boolean)
165            | (Type::Field, Type::Field)
166            | (Type::Group, Type::Group)
167            | (Type::Scalar, Type::Scalar)
168            | (Type::Signature, Type::Signature)
169            | (Type::String, Type::String)
170            | (Type::Unit, Type::Unit) => true,
171            (Type::Array(left), Type::Array(right)) => {
172                // Two arrays are equal if their element types are the same and if their lengths
173                // are the same, assuming the lengths can be extracted as `u32`.
174                (match (left.length.as_u32(), right.length.as_u32()) {
175                    (Some(l1), Some(l2)) => l1 == l2,
176                    _ => {
177                        // An array with an undetermined length (e.g., one that depends on a `const`) is considered
178                        // equal to other arrays because their lengths _may_ eventually be proven equal.
179                        true
180                    }
181                }) && left.element_type().eq_flat_relaxed(right.element_type())
182            }
183            (Type::Identifier(left), Type::Identifier(right)) => left.matches(right),
184            (Type::Integer(left), Type::Integer(right)) => left.eq(right),
185            (Type::Mapping(left), Type::Mapping(right)) => {
186                left.key.eq_flat_relaxed(&right.key) && left.value.eq_flat_relaxed(&right.value)
187            }
188            (Type::Optional(left), Type::Optional(right)) => left.inner.eq_flat_relaxed(&right.inner),
189            (Type::Tuple(left), Type::Tuple(right)) if left.length() == right.length() => left
190                .elements()
191                .iter()
192                .zip_eq(right.elements().iter())
193                .all(|(left_type, right_type)| left_type.eq_flat_relaxed(right_type)),
194            (Type::Composite(left), Type::Composite(right)) => {
195                // If either composite still has const generic arguments, treat them as equal.
196                // Type checking will run again after monomorphization.
197                if !left.const_arguments.is_empty() || !right.const_arguments.is_empty() {
198                    return true;
199                }
200
201                // Two composite types are the same if their _absolute_ paths match.
202                // If the absolute paths are not available, then we really can't compare the two
203                // types and we just return `false` to be conservative.
204                match (&left.path.try_absolute_path(), &right.path.try_absolute_path()) {
205                    (Some(l), Some(r)) => l == r,
206                    _ => false,
207                }
208            }
209            // Don't type check when type hasn't been explicitly defined.
210            (Type::Future(left), Type::Future(right)) if !left.is_explicit || !right.is_explicit => true,
211            (Type::Future(left), Type::Future(right)) if left.inputs.len() == right.inputs.len() => left
212                .inputs()
213                .iter()
214                .zip_eq(right.inputs().iter())
215                .all(|(left_type, right_type)| left_type.eq_flat_relaxed(right_type)),
216            _ => false,
217        }
218    }
219
220    pub fn from_snarkvm<N: Network>(t: &PlaintextType<N>, program: Option<Symbol>) -> Self {
221        match t {
222            Literal(lit) => match lit {
223                snarkvm::prelude::LiteralType::Address => Type::Address,
224                snarkvm::prelude::LiteralType::Boolean => Type::Boolean,
225                snarkvm::prelude::LiteralType::Field => Type::Field,
226                snarkvm::prelude::LiteralType::Group => Type::Group,
227                snarkvm::prelude::LiteralType::U8 => Type::Integer(IntegerType::U8),
228                snarkvm::prelude::LiteralType::U16 => Type::Integer(IntegerType::U16),
229                snarkvm::prelude::LiteralType::U32 => Type::Integer(IntegerType::U32),
230                snarkvm::prelude::LiteralType::U64 => Type::Integer(IntegerType::U64),
231                snarkvm::prelude::LiteralType::U128 => Type::Integer(IntegerType::U128),
232                snarkvm::prelude::LiteralType::I8 => Type::Integer(IntegerType::I8),
233                snarkvm::prelude::LiteralType::I16 => Type::Integer(IntegerType::I16),
234                snarkvm::prelude::LiteralType::I32 => Type::Integer(IntegerType::I32),
235                snarkvm::prelude::LiteralType::I64 => Type::Integer(IntegerType::I64),
236                snarkvm::prelude::LiteralType::I128 => Type::Integer(IntegerType::I128),
237                snarkvm::prelude::LiteralType::Scalar => Type::Scalar,
238                snarkvm::prelude::LiteralType::Signature => Type::Signature,
239                snarkvm::prelude::LiteralType::String => Type::String,
240            },
241            Struct(s) => Type::Composite(CompositeType {
242                path: {
243                    let ident = Identifier::from(s);
244                    Path::from(ident).with_absolute_path(Some(vec![ident.name]))
245                },
246                const_arguments: Vec::new(),
247                program,
248            }),
249            Array(array) => Type::Array(ArrayType::from_snarkvm(array, program)),
250        }
251    }
252
253    /// Determines whether `self` can be coerced to the `expected` type.
254    ///
255    /// This method checks if the current type can be implicitly coerced to the expected type
256    /// according to specific rules:
257    /// - `Optional<T>` can be coerced to `Optional<T>`.
258    /// - `T` can be coerced to `Optional<T>`.
259    /// - Arrays `[T; N]` can be coerced to `[Optional<T>; N]` if lengths match or are unknown,
260    ///   and element types are coercible.
261    /// - Falls back to an equality check for other types.
262    ///
263    /// # Arguments
264    /// * `expected` - The type to which `self` is being coerced.
265    ///
266    /// # Returns
267    /// `true` if coercion is allowed; `false` otherwise.
268    pub fn can_coerce_to(&self, expected: &Type) -> bool {
269        use Type::*;
270
271        match (self, expected) {
272            // Allow Optional<T> → Optional<T>
273            (Optional(actual_opt), Optional(expected_opt)) => actual_opt.inner.can_coerce_to(&expected_opt.inner),
274
275            // Allow T → Optional<T>
276            (a, Optional(opt)) => a.can_coerce_to(&opt.inner),
277
278            // Allow [T; N] → [Optional<T>; N]
279            (Array(a_arr), Array(e_arr)) => {
280                let lengths_equal = match (a_arr.length.as_u32(), e_arr.length.as_u32()) {
281                    (Some(l1), Some(l2)) => l1 == l2,
282                    _ => true,
283                };
284
285                lengths_equal && a_arr.element_type().can_coerce_to(e_arr.element_type())
286            }
287
288            // Fallback: check for exact match
289            _ => self.eq_user(expected),
290        }
291    }
292}
293
294impl fmt::Display for Type {
295    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296        match *self {
297            Type::Address => write!(f, "address"),
298            Type::Array(ref array_type) => write!(f, "{array_type}"),
299            Type::Boolean => write!(f, "bool"),
300            Type::Field => write!(f, "field"),
301            Type::Future(ref future_type) => write!(f, "{future_type}"),
302            Type::Group => write!(f, "group"),
303            Type::Identifier(ref variable) => write!(f, "{variable}"),
304            Type::Integer(ref integer_type) => write!(f, "{integer_type}"),
305            Type::Mapping(ref mapping_type) => write!(f, "{mapping_type}"),
306            Type::Optional(ref optional_type) => write!(f, "{optional_type}"),
307            Type::Scalar => write!(f, "scalar"),
308            Type::Signature => write!(f, "signature"),
309            Type::String => write!(f, "string"),
310            Type::Composite(ref struct_type) => write!(f, "{struct_type}"),
311            Type::Tuple(ref tuple) => write!(f, "{tuple}"),
312            Type::Numeric => write!(f, "numeric"),
313            Type::Unit => write!(f, "()"),
314            Type::Err => write!(f, "error"),
315        }
316    }
317}