miden_assembly_syntax/ast/
type.rs

1use alloc::{boxed::Box, string::String, vec::Vec};
2
3use miden_debug_types::{SourceSpan, Span, Spanned};
4pub use midenc_hir_type as types;
5use midenc_hir_type::{AddressSpace, Type, TypeRepr};
6
7use super::{ConstantExpr, DocString, Ident};
8
9// TYPE DECLARATION
10// ================================================================================================
11
12/// An abstraction over the different types of type declarations allowed in Miden Assembly
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum TypeDecl {
15    /// A named type, i.e. a type alias
16    Alias(TypeAlias),
17    /// A C-like enumeration type with associated constants
18    Enum(EnumType),
19}
20
21impl TypeDecl {
22    /// Get the name assigned to this type declaration
23    pub fn name(&self) -> &Ident {
24        match self {
25            Self::Alias(ty) => &ty.name,
26            Self::Enum(ty) => &ty.name,
27        }
28    }
29
30    /// Get the type expression associated with this declaration
31    pub fn ty(&self) -> TypeExpr {
32        match self {
33            Self::Alias(ty) => ty.ty.clone(),
34            Self::Enum(ty) => TypeExpr::Primitive(Span::new(ty.span, ty.ty.clone())),
35        }
36    }
37}
38
39impl Spanned for TypeDecl {
40    fn span(&self) -> SourceSpan {
41        match self {
42            Self::Alias(spanned) => spanned.span,
43            Self::Enum(spanned) => spanned.span,
44        }
45    }
46}
47
48impl From<TypeAlias> for TypeDecl {
49    fn from(value: TypeAlias) -> Self {
50        Self::Alias(value)
51    }
52}
53
54impl From<EnumType> for TypeDecl {
55    fn from(value: EnumType) -> Self {
56        Self::Enum(value)
57    }
58}
59
60impl crate::prettier::PrettyPrint for TypeDecl {
61    fn render(&self) -> crate::prettier::Document {
62        match self {
63            Self::Alias(ty) => ty.render(),
64            Self::Enum(ty) => ty.render(),
65        }
66    }
67}
68
69// FUNCTION TYPE
70// ================================================================================================
71
72/// A procedure type signature
73#[derive(Debug, Clone)]
74pub struct FunctionType {
75    pub span: SourceSpan,
76    pub cc: types::CallConv,
77    pub args: Vec<TypeExpr>,
78    pub results: Vec<TypeExpr>,
79}
80
81impl Eq for FunctionType {}
82
83impl PartialEq for FunctionType {
84    fn eq(&self, other: &Self) -> bool {
85        self.cc == other.cc && self.args == other.args && self.results == other.results
86    }
87}
88
89impl core::hash::Hash for FunctionType {
90    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
91        self.cc.hash(state);
92        self.args.hash(state);
93        self.results.hash(state);
94    }
95}
96
97impl Spanned for FunctionType {
98    fn span(&self) -> SourceSpan {
99        self.span
100    }
101}
102
103impl FunctionType {
104    pub fn new(cc: types::CallConv, args: Vec<TypeExpr>, results: Vec<TypeExpr>) -> Self {
105        Self {
106            span: SourceSpan::UNKNOWN,
107            cc,
108            args,
109            results,
110        }
111    }
112
113    /// Override the default source span
114    #[inline]
115    pub fn with_span(mut self, span: SourceSpan) -> Self {
116        self.span = span;
117        self
118    }
119}
120
121impl crate::prettier::PrettyPrint for FunctionType {
122    fn render(&self) -> crate::prettier::Document {
123        use crate::prettier::*;
124
125        let singleline_args = self
126            .args
127            .iter()
128            .map(|arg| arg.render())
129            .reduce(|acc, arg| acc + const_text(", ") + arg)
130            .unwrap_or(Document::Empty);
131        let multiline_args = indent(
132            4,
133            nl() + self
134                .args
135                .iter()
136                .map(|arg| arg.render())
137                .reduce(|acc, arg| acc + const_text(",") + nl() + arg)
138                .unwrap_or(Document::Empty),
139        ) + nl();
140        let args = singleline_args | multiline_args;
141        let args = const_text("(") + args + const_text(")");
142
143        match self.results.len() {
144            0 => args,
145            1 => args + const_text(" -> ") + self.results[0].render(),
146            _ => {
147                let results = self
148                    .results
149                    .iter()
150                    .map(|r| r.render())
151                    .reduce(|acc, r| acc + const_text(", ") + r)
152                    .unwrap_or(Document::Empty);
153                args + const_text(" -> ") + const_text("(") + results + const_text(")")
154            },
155        }
156    }
157}
158
159// TYPE EXPRESSION
160// ================================================================================================
161
162/// A syntax-level type expression (i.e. primitive type, reference to nominal type, etc.)
163#[derive(Debug, Clone, Eq, PartialEq, Hash)]
164pub enum TypeExpr {
165    /// A primitive integral type, e.g. `i1`, `u16`
166    Primitive(Span<Type>),
167    /// A pointer type expression, e.g. `*u8`
168    Ptr(PointerType),
169    /// An array type expression, e.g. `[u8; 32]`
170    Array(ArrayType),
171    /// A struct type expression, e.g. `struct { a: u32 }`
172    Struct(StructType),
173    /// A reference to a type aliased by name, e.g. `Foo`
174    Ref(Ident),
175}
176
177impl From<Type> for TypeExpr {
178    fn from(ty: Type) -> Self {
179        match ty {
180            Type::Array(t) => Self::Array(ArrayType::new(t.element_type().clone().into(), t.len())),
181            Type::Struct(t) => {
182                Self::Struct(StructType::new(t.fields().iter().enumerate().map(|(i, ft)| {
183                    let name = Ident::new(format!("field{i}")).unwrap();
184                    StructField {
185                        span: SourceSpan::UNKNOWN,
186                        name,
187                        ty: ft.ty.clone().into(),
188                    }
189                })))
190            },
191            Type::Ptr(t) => Self::Ptr((*t).clone().into()),
192            Type::Function(_) => {
193                Self::Ptr(PointerType::new(TypeExpr::Primitive(Span::unknown(Type::Felt))))
194            },
195            Type::List(t) => Self::Ptr(
196                PointerType::new((*t).clone().into()).with_address_space(AddressSpace::Byte),
197            ),
198            Type::I128 | Type::U128 => Self::Array(ArrayType::new(Type::U32.into(), 4)),
199            Type::I64 | Type::U64 => Self::Array(ArrayType::new(Type::U32.into(), 2)),
200            Type::Unknown | Type::Never | Type::F64 => panic!("unrepresentable type value: {ty}"),
201            ty => Self::Primitive(Span::unknown(ty)),
202        }
203    }
204}
205
206impl Spanned for TypeExpr {
207    fn span(&self) -> SourceSpan {
208        match self {
209            Self::Primitive(spanned) => spanned.span(),
210            Self::Ptr(spanned) => spanned.span(),
211            Self::Array(spanned) => spanned.span(),
212            Self::Struct(spanned) => spanned.span(),
213            Self::Ref(spanned) => spanned.span(),
214        }
215    }
216}
217
218impl crate::prettier::PrettyPrint for TypeExpr {
219    fn render(&self) -> crate::prettier::Document {
220        use crate::prettier::*;
221
222        match self {
223            Self::Primitive(ty) => display(ty),
224            Self::Ptr(ty) => ty.render(),
225            Self::Array(ty) => ty.render(),
226            Self::Struct(ty) => ty.render(),
227            Self::Ref(ty) => display(ty),
228        }
229    }
230}
231
232// POINTER TYPE
233// ================================================================================================
234
235#[derive(Debug, Clone)]
236pub struct PointerType {
237    pub span: SourceSpan,
238    pub pointee: Box<TypeExpr>,
239    addrspace: Option<AddressSpace>,
240}
241
242impl From<types::PointerType> for PointerType {
243    fn from(ty: types::PointerType) -> Self {
244        let types::PointerType { addrspace, pointee } = ty;
245        let pointee = Box::new(TypeExpr::from(pointee));
246        Self {
247            span: SourceSpan::UNKNOWN,
248            pointee,
249            addrspace: Some(addrspace),
250        }
251    }
252}
253
254impl Eq for PointerType {}
255
256impl PartialEq for PointerType {
257    fn eq(&self, other: &Self) -> bool {
258        self.address_space() == other.address_space() && self.pointee == other.pointee
259    }
260}
261
262impl core::hash::Hash for PointerType {
263    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
264        self.pointee.hash(state);
265        self.addrspace.hash(state);
266    }
267}
268
269impl Spanned for PointerType {
270    fn span(&self) -> SourceSpan {
271        self.span
272    }
273}
274
275impl PointerType {
276    pub fn new(pointee: TypeExpr) -> Self {
277        Self {
278            span: SourceSpan::UNKNOWN,
279            pointee: Box::new(pointee),
280            addrspace: None,
281        }
282    }
283
284    /// Override the default source span
285    #[inline]
286    pub fn with_span(mut self, span: SourceSpan) -> Self {
287        self.span = span;
288        self
289    }
290
291    /// Override the default address space
292    #[inline]
293    pub fn with_address_space(mut self, addrspace: AddressSpace) -> Self {
294        self.addrspace = Some(addrspace);
295        self
296    }
297
298    /// Get the address space of this pointer type
299    #[inline]
300    pub fn address_space(&self) -> AddressSpace {
301        self.addrspace.unwrap_or(AddressSpace::Element)
302    }
303}
304
305impl crate::prettier::PrettyPrint for PointerType {
306    fn render(&self) -> crate::prettier::Document {
307        use crate::prettier::*;
308
309        let doc = const_text("ptr<") + self.pointee.render();
310        if let Some(addrspace) = self.addrspace.as_ref() {
311            doc + const_text(", ") + text(format!("addrspace({})", addrspace)) + const_text(">")
312        } else {
313            doc + const_text(">")
314        }
315    }
316}
317
318// ARRAY TYPE
319// ================================================================================================
320
321#[derive(Debug, Clone)]
322pub struct ArrayType {
323    pub span: SourceSpan,
324    pub elem: Box<TypeExpr>,
325    pub arity: usize,
326}
327
328impl Eq for ArrayType {}
329
330impl PartialEq for ArrayType {
331    fn eq(&self, other: &Self) -> bool {
332        self.arity == other.arity && self.elem == other.elem
333    }
334}
335
336impl core::hash::Hash for ArrayType {
337    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
338        self.elem.hash(state);
339        self.arity.hash(state);
340    }
341}
342
343impl Spanned for ArrayType {
344    fn span(&self) -> SourceSpan {
345        self.span
346    }
347}
348
349impl ArrayType {
350    pub fn new(elem: TypeExpr, arity: usize) -> Self {
351        Self {
352            span: SourceSpan::UNKNOWN,
353            elem: Box::new(elem),
354            arity,
355        }
356    }
357
358    /// Override the default source span
359    #[inline]
360    pub fn with_span(mut self, span: SourceSpan) -> Self {
361        self.span = span;
362        self
363    }
364}
365
366impl crate::prettier::PrettyPrint for ArrayType {
367    fn render(&self) -> crate::prettier::Document {
368        use crate::prettier::*;
369
370        const_text("[")
371            + self.elem.render()
372            + const_text("; ")
373            + display(self.arity)
374            + const_text("]")
375    }
376}
377
378// STRUCT TYPE
379// ================================================================================================
380
381#[derive(Debug, Clone)]
382pub struct StructType {
383    pub span: SourceSpan,
384    pub repr: Span<TypeRepr>,
385    pub fields: Vec<StructField>,
386}
387
388impl Eq for StructType {}
389
390impl PartialEq for StructType {
391    fn eq(&self, other: &Self) -> bool {
392        self.repr == other.repr && self.fields == other.fields
393    }
394}
395
396impl core::hash::Hash for StructType {
397    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
398        self.repr.hash(state);
399        self.fields.hash(state);
400    }
401}
402
403impl Spanned for StructType {
404    fn span(&self) -> SourceSpan {
405        self.span
406    }
407}
408
409impl StructType {
410    pub fn new(fields: impl IntoIterator<Item = StructField>) -> Self {
411        Self {
412            span: SourceSpan::UNKNOWN,
413            repr: Span::unknown(TypeRepr::Default),
414            fields: fields.into_iter().collect(),
415        }
416    }
417
418    /// Override the default struct representation
419    #[inline]
420    pub fn with_repr(mut self, repr: Span<TypeRepr>) -> Self {
421        self.repr = repr;
422        self
423    }
424
425    /// Override the default source span
426    #[inline]
427    pub fn with_span(mut self, span: SourceSpan) -> Self {
428        self.span = span;
429        self
430    }
431}
432
433impl crate::prettier::PrettyPrint for StructType {
434    fn render(&self) -> crate::prettier::Document {
435        use crate::prettier::*;
436
437        let repr = match &*self.repr {
438            TypeRepr::Default => Document::Empty,
439            TypeRepr::BigEndian => const_text("@bigendian "),
440            repr @ (TypeRepr::Align(_) | TypeRepr::Packed(_) | TypeRepr::Transparent) => {
441                text(format!("@{repr} "))
442            },
443        };
444
445        let singleline_body = self
446            .fields
447            .iter()
448            .map(|field| field.render())
449            .reduce(|acc, field| acc + const_text(", ") + field)
450            .unwrap_or(Document::Empty);
451        let multiline_body = indent(
452            4,
453            nl() + self
454                .fields
455                .iter()
456                .map(|field| field.render())
457                .reduce(|acc, field| acc + const_text(",") + nl() + field)
458                .unwrap_or(Document::Empty),
459        ) + nl();
460        let body = singleline_body | multiline_body;
461
462        repr + const_text("struct") + const_text(" { ") + body + const_text(" }")
463    }
464}
465
466// STRUCT FIELD
467// ================================================================================================
468
469#[derive(Debug, Clone)]
470pub struct StructField {
471    pub span: SourceSpan,
472    pub name: Ident,
473    pub ty: TypeExpr,
474}
475
476impl Eq for StructField {}
477
478impl PartialEq for StructField {
479    fn eq(&self, other: &Self) -> bool {
480        self.name == other.name && self.ty == other.ty
481    }
482}
483
484impl core::hash::Hash for StructField {
485    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
486        self.name.hash(state);
487        self.ty.hash(state);
488    }
489}
490
491impl Spanned for StructField {
492    fn span(&self) -> SourceSpan {
493        self.span
494    }
495}
496
497impl crate::prettier::PrettyPrint for StructField {
498    fn render(&self) -> crate::prettier::Document {
499        use crate::prettier::*;
500
501        display(&self.name) + const_text(": ") + self.ty.render()
502    }
503}
504
505// TYPE ALIAS
506// ================================================================================================
507
508/// A [TypeAlias] represents a named [Type].
509///
510/// Type aliases correspond to type declarations in Miden Assembly source files. They are called
511/// aliases, rather than declarations, as the type system for Miden Assembly is structural, rather
512/// than nominal, and so two aliases with the same underlying type are considered equivalent.
513#[derive(Debug, Clone)]
514pub struct TypeAlias {
515    span: SourceSpan,
516    /// The documentation string attached to this definition.
517    docs: Option<DocString>,
518    /// The name of this type alias
519    pub name: Ident,
520    /// The concrete underlying type
521    pub ty: TypeExpr,
522}
523
524impl TypeAlias {
525    /// Create a new type alias from a name and type
526    pub fn new(name: Ident, ty: TypeExpr) -> Self {
527        Self { span: name.span(), docs: None, name, ty }
528    }
529
530    /// Adds documentation to this type alias
531    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
532        self.docs = docs.map(DocString::new);
533        self
534    }
535
536    /// Override the default source span
537    #[inline]
538    pub fn with_span(mut self, span: SourceSpan) -> Self {
539        self.span = span;
540        self
541    }
542
543    /// Set the source span
544    #[inline]
545    pub fn set_span(&mut self, span: SourceSpan) {
546        self.span = span;
547    }
548}
549
550impl Eq for TypeAlias {}
551
552impl PartialEq for TypeAlias {
553    fn eq(&self, other: &Self) -> bool {
554        self.name == other.name && self.docs == other.docs && self.ty == other.ty
555    }
556}
557
558impl core::hash::Hash for TypeAlias {
559    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
560        let Self { span: _, docs, name, ty } = self;
561        docs.hash(state);
562        name.hash(state);
563        ty.hash(state);
564    }
565}
566
567impl Spanned for TypeAlias {
568    fn span(&self) -> SourceSpan {
569        self.span
570    }
571}
572
573impl crate::prettier::PrettyPrint for TypeAlias {
574    fn render(&self) -> crate::prettier::Document {
575        use crate::prettier::*;
576
577        let doc = self
578            .docs
579            .as_ref()
580            .map(|docstring| docstring.render())
581            .unwrap_or(Document::Empty);
582
583        doc + const_text("type")
584            + const_text(" ")
585            + display(&self.name)
586            + const_text(" = ")
587            + self.ty.render()
588    }
589}
590
591// ENUM TYPE
592// ================================================================================================
593
594/// A combined type alias and constant declaration corresponding to a C-like enumeration.
595///
596/// C-style enumerations are effectively a type alias for an integer type with a limited set of
597/// valid values with associated names (referred to as _variants_ of the enum type).
598///
599/// In Miden Assembly, these provide a means for a procedure to declare that it expects an argument
600/// of the underlying integral type, but that values other than those of the declared variants are
601/// illegal/invalid. Currently, these are unchecked, and are only used to convey semantic
602/// information. In the future, we may perform static analysis to try and identify invalid instances
603/// of the enumeration when derived from a constant.
604#[derive(Debug, Clone)]
605pub struct EnumType {
606    span: SourceSpan,
607    /// The documentation string attached to this definition.
608    docs: Option<DocString>,
609    /// The enum name
610    name: Ident,
611    /// The type of the discriminant value used for this enum's variants
612    ///
613    /// NOTE: The type must be an integral value, and this is enforced by [`Self::new`].
614    ty: Type,
615    /// The enum variants
616    variants: Vec<Variant>,
617}
618
619impl EnumType {
620    /// Construct a new enum type with the given name and variants
621    ///
622    /// The caller is assumed to have already validated that `ty` is an integral type, and this
623    /// function will assert that this is the case.
624    pub fn new(name: Ident, ty: Type, variants: impl IntoIterator<Item = Variant>) -> Self {
625        assert!(ty.is_integer(), "only integer types are allowed in enum type definitions");
626        Self {
627            span: name.span(),
628            docs: None,
629            name,
630            ty,
631            variants: Vec::from_iter(variants),
632        }
633    }
634
635    /// Adds documentation to this enum declaration.
636    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
637        self.docs = docs.map(DocString::new);
638        self
639    }
640
641    /// Override the default source span
642    pub fn with_span(mut self, span: SourceSpan) -> Self {
643        self.span = span;
644        self
645    }
646
647    /// Set the source span
648    pub fn set_span(&mut self, span: SourceSpan) {
649        self.span = span;
650    }
651
652    /// Get the name of this enum type
653    pub fn name(&self) -> &Ident {
654        &self.name
655    }
656
657    /// Get the concrete type of this enum's variants
658    pub fn ty(&self) -> &Type {
659        &self.ty
660    }
661
662    /// Get the variants of this enum type
663    pub fn variants(&self) -> &[Variant] {
664        &self.variants
665    }
666
667    /// Get the variants of this enum type, mutably
668    pub fn variants_mut(&mut self) -> &mut Vec<Variant> {
669        &mut self.variants
670    }
671
672    /// Split this definition into its type alias and variant parts
673    pub fn into_parts(self) -> (TypeAlias, Vec<Variant>) {
674        let Self { span, docs, name, ty, variants } = self;
675        let alias = TypeAlias {
676            span,
677            docs,
678            name,
679            ty: TypeExpr::Primitive(Span::new(span, ty)),
680        };
681        (alias, variants)
682    }
683}
684
685impl Spanned for EnumType {
686    fn span(&self) -> SourceSpan {
687        self.span
688    }
689}
690
691impl Eq for EnumType {}
692
693impl PartialEq for EnumType {
694    fn eq(&self, other: &Self) -> bool {
695        self.name == other.name
696            && self.docs == other.docs
697            && self.ty == other.ty
698            && self.variants == other.variants
699    }
700}
701
702impl core::hash::Hash for EnumType {
703    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
704        let Self { span: _, docs, name, ty, variants } = self;
705        docs.hash(state);
706        name.hash(state);
707        ty.hash(state);
708        variants.hash(state);
709    }
710}
711
712impl crate::prettier::PrettyPrint for EnumType {
713    fn render(&self) -> crate::prettier::Document {
714        use crate::prettier::*;
715
716        let doc = self
717            .docs
718            .as_ref()
719            .map(|docstring| docstring.render())
720            .unwrap_or(Document::Empty);
721
722        let variants = self
723            .variants
724            .iter()
725            .map(|v| v.render())
726            .reduce(|acc, v| acc + const_text(",") + nl() + v)
727            .unwrap_or(Document::Empty);
728
729        doc + const_text("enum")
730            + const_text(" ")
731            + display(&self.name)
732            + const_text(" : ")
733            + self.ty.render()
734            + const_text(" {")
735            + nl()
736            + variants
737            + const_text("}")
738    }
739}
740
741// ENUM VARIANT
742// ================================================================================================
743
744/// A variant of an [EnumType].
745///
746/// See the [EnumType] docs for more information.
747#[derive(Debug, Clone)]
748pub struct Variant {
749    pub span: SourceSpan,
750    /// The documentation string attached to the constant derived from this variant.
751    pub docs: Option<DocString>,
752    /// The name of this enum variant
753    pub name: Ident,
754    /// The discriminant value associated with this variant
755    pub discriminant: ConstantExpr,
756}
757
758impl Variant {
759    /// Construct a new variant of an [EnumType], with the given name and discriminant value.
760    pub fn new(name: Ident, discriminant: ConstantExpr) -> Self {
761        Self {
762            span: name.span(),
763            docs: None,
764            name,
765            discriminant,
766        }
767    }
768
769    /// Override the span for this variant
770    pub fn with_span(mut self, span: SourceSpan) -> Self {
771        self.span = span;
772        self
773    }
774
775    /// Adds documentation to this variant
776    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
777        self.docs = docs.map(DocString::new);
778        self
779    }
780
781    /// Used to validate that this variant's discriminant value is an instance of `ty`,
782    /// which must be a type valid for use as the underlying representation for an enum, i.e. an
783    /// integer type up to 64 bits in size.
784    ///
785    /// It is expected that the discriminant expression has been folded to an integer value by the
786    /// time this is called. If the discriminant has not been fully folded, then an error will be
787    /// returned.
788    pub fn assert_instance_of(&self, ty: &Type) -> Result<(), crate::SemanticAnalysisError> {
789        use crate::{FIELD_MODULUS, SemanticAnalysisError};
790
791        let value = match &self.discriminant {
792            ConstantExpr::Int(value) => value.as_int(),
793            _ => {
794                return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
795                    span: self.discriminant.span(),
796                    repr: ty.clone(),
797                });
798            },
799        };
800
801        match ty {
802            Type::I1 if value > 1 => Err(SemanticAnalysisError::InvalidEnumDiscriminant {
803                span: self.discriminant.span(),
804                repr: ty.clone(),
805            }),
806            Type::I1 => Ok(()),
807            Type::I8 | Type::U8 if value > u8::MAX as u64 => {
808                Err(SemanticAnalysisError::InvalidEnumDiscriminant {
809                    span: self.discriminant.span(),
810                    repr: ty.clone(),
811                })
812            },
813            Type::I8 | Type::U8 => Ok(()),
814            Type::I16 | Type::U16 if value > u16::MAX as u64 => {
815                Err(SemanticAnalysisError::InvalidEnumDiscriminant {
816                    span: self.discriminant.span(),
817                    repr: ty.clone(),
818                })
819            },
820            Type::I16 | Type::U16 => Ok(()),
821            Type::I32 | Type::U32 if value > u32::MAX as u64 => {
822                Err(SemanticAnalysisError::InvalidEnumDiscriminant {
823                    span: self.discriminant.span(),
824                    repr: ty.clone(),
825                })
826            },
827            Type::I32 | Type::U32 => Ok(()),
828            Type::I64 | Type::U64 if value >= FIELD_MODULUS => {
829                Err(SemanticAnalysisError::InvalidEnumDiscriminant {
830                    span: self.discriminant.span(),
831                    repr: ty.clone(),
832                })
833            },
834            _ => Err(SemanticAnalysisError::InvalidEnumRepr { span: self.span }),
835        }
836    }
837}
838
839impl Spanned for Variant {
840    fn span(&self) -> SourceSpan {
841        self.span
842    }
843}
844
845impl Eq for Variant {}
846
847impl PartialEq for Variant {
848    fn eq(&self, other: &Self) -> bool {
849        self.name == other.name
850            && self.discriminant == other.discriminant
851            && self.docs == other.docs
852    }
853}
854
855impl core::hash::Hash for Variant {
856    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
857        let Self { span: _, docs, name, discriminant } = self;
858        docs.hash(state);
859        name.hash(state);
860        discriminant.hash(state);
861    }
862}
863
864impl crate::prettier::PrettyPrint for Variant {
865    fn render(&self) -> crate::prettier::Document {
866        use crate::prettier::*;
867
868        let doc = self
869            .docs
870            .as_ref()
871            .map(|docstring| docstring.render())
872            .unwrap_or(Document::Empty);
873
874        doc + display(&self.name) + const_text(" = ") + self.discriminant.render()
875    }
876}