miden_assembly_syntax/ast/
type.rs

1use alloc::{string::String, vec::Vec};
2
3use miden_debug_types::{SourceSpan, Span, Spanned};
4pub use midenc_hir_type as types;
5use midenc_hir_type::Type;
6
7use super::{ConstantExpr, DocString, Ident};
8
9/// An abstraction over the different types of type declarations allowed in Miden Assembly
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum TypeDecl {
12    /// A named type, i.e. a type alias
13    Alias(TypeAlias),
14    /// A C-like enumeration type with associated constants
15    Enum(EnumType),
16}
17
18impl TypeDecl {
19    pub fn name(&self) -> &Ident {
20        match self {
21            Self::Alias(ty) => &ty.name,
22            Self::Enum(ty) => &ty.name,
23        }
24    }
25
26    pub fn ty(&self) -> &Type {
27        match self {
28            Self::Alias(ty) => &ty.ty,
29            Self::Enum(ty) => &ty.ty,
30        }
31    }
32}
33
34impl Spanned for TypeDecl {
35    fn span(&self) -> SourceSpan {
36        match self {
37            Self::Alias(spanned) => spanned.span,
38            Self::Enum(spanned) => spanned.span,
39        }
40    }
41}
42
43impl From<TypeAlias> for TypeDecl {
44    fn from(value: TypeAlias) -> Self {
45        Self::Alias(value)
46    }
47}
48
49impl From<EnumType> for TypeDecl {
50    fn from(value: EnumType) -> Self {
51        Self::Enum(value)
52    }
53}
54
55/// A [TypeAlias] represents a named [Type].
56///
57/// Type aliases correspond to type declarations in Miden Assembly source files. They are called
58/// aliases, rather than declarations, as the type system for Miden Assembly is structural, rather
59/// than nominal, and so two aliases with the same underlying type are considered equivalent.
60#[derive(Debug, Clone)]
61pub struct TypeAlias {
62    span: SourceSpan,
63    /// The documentation string attached to this definition.
64    docs: Option<DocString>,
65    /// The name of this type alias
66    pub name: Ident,
67    /// The concrete underlying type
68    pub ty: Type,
69}
70
71impl TypeAlias {
72    /// Create a new type alias from a name and type
73    pub fn new(name: Ident, ty: Type) -> Self {
74        Self { span: name.span(), docs: None, name, ty }
75    }
76
77    /// Adds documentation to this type alias
78    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
79        self.docs = docs.map(DocString::new);
80        self
81    }
82
83    /// Override the default source span
84    #[inline]
85    pub fn with_span(mut self, span: SourceSpan) -> Self {
86        self.span = span;
87        self
88    }
89
90    /// Set the source span
91    #[inline]
92    pub fn set_span(&mut self, span: SourceSpan) {
93        self.span = span;
94    }
95}
96
97impl Spanned for TypeAlias {
98    fn span(&self) -> SourceSpan {
99        self.span
100    }
101}
102
103impl Eq for TypeAlias {}
104
105impl PartialEq for TypeAlias {
106    fn eq(&self, other: &Self) -> bool {
107        self.name == other.name && self.docs == other.docs && self.ty == other.ty
108    }
109}
110
111impl core::hash::Hash for TypeAlias {
112    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
113        let Self { span: _, docs, name, ty } = self;
114        docs.hash(state);
115        name.hash(state);
116        ty.hash(state);
117    }
118}
119
120/// A combined type alias and constant declaration corresponding to a C-like enumeration.
121///
122/// C-style enumerations are effectively a type alias for an integer type with a limited set of
123/// valid values with associated names (referred to as _variants_ of the enum type).
124///
125/// In Miden Assembly, these provide a means for a procedure to declare that it expects an argument
126/// of the underlying integral type, but that values other than those of the declared variants are
127/// illegal/invalid. Currently, these are unchecked, and are only used to convey semantic
128/// information. In the future, we may perform static analysis to try and identify invalid instances
129/// of the enumeration when derived from a constant.
130#[derive(Debug, Clone)]
131pub struct EnumType {
132    span: SourceSpan,
133    /// The documentation string attached to this definition.
134    docs: Option<DocString>,
135    /// The enum name
136    name: Ident,
137    /// The type of the discriminant value used for this enum's variants
138    ///
139    /// NOTE: The type must be an integral value, and this is enforced by [`Self::new`].
140    ty: Type,
141    /// The enum variants
142    variants: Vec<Variant>,
143}
144
145impl EnumType {
146    /// Construct a new enum type with the given name and variants
147    ///
148    /// The caller is assumed to have already validated that `ty` is an integral type, and this
149    /// function will assert that this is the case.
150    pub fn new(name: Ident, ty: Type, variants: impl IntoIterator<Item = Variant>) -> Self {
151        assert!(ty.is_integer(), "only integer types are allowed in enum type definitions");
152        Self {
153            span: name.span(),
154            docs: None,
155            name,
156            ty,
157            variants: Vec::from_iter(variants),
158        }
159    }
160
161    /// Adds documentation to this enum declaration.
162    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
163        self.docs = docs.map(DocString::new);
164        self
165    }
166
167    /// Override the default source span
168    pub fn with_span(mut self, span: SourceSpan) -> Self {
169        self.span = span;
170        self
171    }
172
173    /// Set the source span
174    pub fn set_span(&mut self, span: SourceSpan) {
175        self.span = span;
176    }
177
178    /// Get the name of this enum type
179    pub fn name(&self) -> &Ident {
180        &self.name
181    }
182
183    /// Get the concrete type of this enum's variants
184    pub fn ty(&self) -> &Type {
185        &self.ty
186    }
187
188    /// Get the variants of this enum type
189    pub fn variants(&self) -> &[Variant] {
190        &self.variants
191    }
192
193    /// Split this definition into its type alias and variant parts
194    pub fn into_parts(self) -> (TypeAlias, Vec<Variant>) {
195        let Self { span, docs, name, ty, variants } = self;
196        let alias = TypeAlias { span, docs, name, ty };
197        (alias, variants)
198    }
199}
200
201impl Spanned for EnumType {
202    fn span(&self) -> SourceSpan {
203        self.span
204    }
205}
206
207impl Eq for EnumType {}
208
209impl PartialEq for EnumType {
210    fn eq(&self, other: &Self) -> bool {
211        self.name == other.name
212            && self.docs == other.docs
213            && self.ty == other.ty
214            && self.variants == other.variants
215    }
216}
217
218impl core::hash::Hash for EnumType {
219    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
220        let Self { span: _, docs, name, ty, variants } = self;
221        docs.hash(state);
222        name.hash(state);
223        ty.hash(state);
224        variants.hash(state);
225    }
226}
227
228/// A variant of an [EnumType].
229///
230/// See the [EnumType] docs for more information.
231#[derive(Debug, Clone)]
232pub struct Variant {
233    pub span: SourceSpan,
234    /// The documentation string attached to the constant derived from this variant.
235    pub docs: Option<DocString>,
236    /// The name of this enum variant
237    pub name: Ident,
238    /// The discriminant value associated with this variant
239    pub discriminant: ConstantExpr,
240}
241
242impl Variant {
243    /// Construct a new variant of an [EnumType], with the given name and discriminant value.
244    pub fn new(name: Ident, discriminant: impl Into<ConstantExpr>) -> Self {
245        Self {
246            span: name.span(),
247            docs: None,
248            name,
249            discriminant: discriminant.into(),
250        }
251    }
252
253    /// Adds documentation to this variant
254    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
255        self.docs = docs.map(DocString::new);
256        self
257    }
258}
259
260impl Spanned for Variant {
261    fn span(&self) -> SourceSpan {
262        self.span
263    }
264}
265
266impl Eq for Variant {}
267
268impl PartialEq for Variant {
269    fn eq(&self, other: &Self) -> bool {
270        self.name == other.name
271            && self.discriminant == other.discriminant
272            && self.docs == other.docs
273    }
274}
275
276impl core::hash::Hash for Variant {
277    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
278        let Self { span: _, docs, name, discriminant } = self;
279        docs.hash(state);
280        name.hash(state);
281        discriminant.hash(state);
282    }
283}