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, TupleType, common};
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    pub fn eq_flat_relaxed(&self, other: &Self) -> bool {
78        match (self, other) {
79            (Type::Address, Type::Address)
80            | (Type::Boolean, Type::Boolean)
81            | (Type::Field, Type::Field)
82            | (Type::Group, Type::Group)
83            | (Type::Scalar, Type::Scalar)
84            | (Type::Signature, Type::Signature)
85            | (Type::String, Type::String)
86            | (Type::Unit, Type::Unit) => true,
87            (Type::Array(left), Type::Array(right)) => {
88                // Two arrays are equal if their element types are the same and if their lengths
89                // are the same, assuming the lengths can be extracted as `u32`.
90                left.element_type().eq_flat_relaxed(right.element_type())
91                    && left.length.as_u32().is_some()
92                    && right.length.as_u32().is_some()
93                    && left.length.as_u32() == right.length.as_u32()
94            }
95            (Type::Identifier(left), Type::Identifier(right)) => left.matches(right),
96            (Type::Integer(left), Type::Integer(right)) => left.eq(right),
97            (Type::Mapping(left), Type::Mapping(right)) => {
98                left.key.eq_flat_relaxed(&right.key) && left.value.eq_flat_relaxed(&right.value)
99            }
100            (Type::Tuple(left), Type::Tuple(right)) if left.length() == right.length() => left
101                .elements()
102                .iter()
103                .zip_eq(right.elements().iter())
104                .all(|(left_type, right_type)| left_type.eq_flat_relaxed(right_type)),
105            (Type::Composite(left), Type::Composite(right)) => left.id.name == right.id.name,
106            // Don't type check when type hasn't been explicitly defined.
107            (Type::Future(left), Type::Future(right)) if !left.is_explicit || !right.is_explicit => true,
108            (Type::Future(left), Type::Future(right)) if left.inputs.len() == right.inputs.len() => left
109                .inputs()
110                .iter()
111                .zip_eq(right.inputs().iter())
112                .all(|(left_type, right_type)| left_type.eq_flat_relaxed(right_type)),
113            _ => false,
114        }
115    }
116
117    pub fn from_snarkvm<N: Network>(t: &PlaintextType<N>, program: Option<Symbol>) -> Self {
118        match t {
119            Literal(lit) => match lit {
120                snarkvm::prelude::LiteralType::Address => Type::Address,
121                snarkvm::prelude::LiteralType::Boolean => Type::Boolean,
122                snarkvm::prelude::LiteralType::Field => Type::Field,
123                snarkvm::prelude::LiteralType::Group => Type::Group,
124                snarkvm::prelude::LiteralType::U8 => Type::Integer(IntegerType::U8),
125                snarkvm::prelude::LiteralType::U16 => Type::Integer(IntegerType::U16),
126                snarkvm::prelude::LiteralType::U32 => Type::Integer(IntegerType::U32),
127                snarkvm::prelude::LiteralType::U64 => Type::Integer(IntegerType::U64),
128                snarkvm::prelude::LiteralType::U128 => Type::Integer(IntegerType::U128),
129                snarkvm::prelude::LiteralType::I8 => Type::Integer(IntegerType::I8),
130                snarkvm::prelude::LiteralType::I16 => Type::Integer(IntegerType::I16),
131                snarkvm::prelude::LiteralType::I32 => Type::Integer(IntegerType::I32),
132                snarkvm::prelude::LiteralType::I64 => Type::Integer(IntegerType::I64),
133                snarkvm::prelude::LiteralType::I128 => Type::Integer(IntegerType::I128),
134                snarkvm::prelude::LiteralType::Scalar => Type::Scalar,
135                snarkvm::prelude::LiteralType::Signature => Type::Signature,
136                snarkvm::prelude::LiteralType::String => Type::String,
137            },
138            Struct(s) => Type::Composite(CompositeType { id: common::Identifier::from(s), program }),
139            Array(array) => Type::Array(ArrayType::from_snarkvm(array, program)),
140        }
141    }
142}
143
144impl fmt::Display for Type {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        match *self {
147            Type::Address => write!(f, "address"),
148            Type::Array(ref array_type) => write!(f, "{array_type}"),
149            Type::Boolean => write!(f, "bool"),
150            Type::Field => write!(f, "field"),
151            Type::Future(ref future_type) => write!(f, "{future_type}"),
152            Type::Group => write!(f, "group"),
153            Type::Identifier(ref variable) => write!(f, "{variable}"),
154            Type::Integer(ref integer_type) => write!(f, "{integer_type}"),
155            Type::Mapping(ref mapping_type) => write!(f, "{mapping_type}"),
156            Type::Scalar => write!(f, "scalar"),
157            Type::Signature => write!(f, "signature"),
158            Type::String => write!(f, "string"),
159            Type::Composite(ref struct_type) => write!(f, "{struct_type}"),
160            Type::Tuple(ref tuple) => write!(f, "{tuple}"),
161            Type::Numeric => write!(f, "numeric"),
162            Type::Unit => write!(f, "()"),
163            Type::Err => write!(f, "error"),
164        }
165    }
166}