Skip to main content

simplicityhl/
types.rs

1use std::fmt;
2use std::str::FromStr;
3use std::sync::Arc;
4
5use miniscript::iter::{Tree, TreeLike};
6use simplicity::types::{CompleteBound, Final};
7
8use crate::array::{BTreeSlice, Partition};
9use crate::num::{NonZeroPow2Usize, Pow2Usize};
10use crate::str::{AliasName, Identifier};
11use crate::unstable::impl_require_feature;
12
13/// Primitives of the SimplicityHL type system, excluding type aliases.
14#[derive(Debug, PartialEq, Eq, Hash, Clone)]
15#[non_exhaustive]
16pub enum TypeInner<A> {
17    /// Sum of the left and right types
18    Either(A, A),
19    /// Option of the inner type
20    Option(A),
21    /// Boolean type
22    Boolean,
23    /// Unsigned integer type
24    UInt(UIntType),
25    /// Tuple of potentially different types
26    Tuple(Arc<[A]>),
27    /// Array of the same type
28    Array(A, usize),
29    /// List of the same type
30    List(A, NonZeroPow2Usize),
31    /// Nominal enum type, represented as a balanced sum of its variants'
32    /// payload types
33    Enum(EnumInfo),
34}
35
36/// One variant of a nominal enum type: its name and payload types.
37///
38/// A variant with no payload types is a unit variant; a variant with
39/// payloads carries a tuple of values of those types.
40#[derive(Debug, PartialEq, Eq, Hash, Clone)]
41pub struct EnumVariantInfo {
42    name: Identifier,
43    payload: Arc<[ResolvedType]>,
44    /// The SimplicityHL type of the variant's contents: unit for unit
45    /// variants, the payload type itself for single payloads, a tuple
46    /// otherwise. Precomputed so it can be borrowed during destructuring.
47    payload_ty: ResolvedType,
48}
49
50impl EnumVariantInfo {
51    pub(crate) fn new(name: Identifier, payload: Arc<[ResolvedType]>) -> Self {
52        let payload_ty = match payload.len() {
53            0 => ResolvedType::unit(),
54            1 => payload[0].clone(),
55            _ => ResolvedType::tuple(payload.iter().cloned()),
56        };
57        Self {
58            name,
59            payload,
60            payload_ty,
61        }
62    }
63
64    /// Access the name of the variant.
65    pub const fn name(&self) -> &Identifier {
66        &self.name
67    }
68
69    /// Access the payload types of the variant, in declaration order.
70    /// Empty for unit variants.
71    pub fn payload(&self) -> &[ResolvedType] {
72        &self.payload
73    }
74
75    /// The SimplicityHL type of the variant's contents, as one type.
76    pub fn payload_type(&self) -> &ResolvedType {
77        &self.payload_ty
78    }
79
80    /// The structural type of the variant's contents: the leaf this
81    /// variant occupies in the enum's balanced sum.
82    pub(crate) fn structural_payload(&self) -> StructuralType {
83        StructuralType::from(&self.payload_ty)
84    }
85}
86
87/// Definition of a nominal enum type: its name and variants in
88/// declaration order.
89///
90/// An enum with `n` variants is represented as a balanced sum of its `n`
91/// variant payload types (see [`BTreeSlice`] for the tree shape), so a value
92/// of the type is exactly one of the `n` variants: an undeclared variant is
93/// unrepresentable. A variant's position among the declared variants
94/// determines its leaf in the sum; there is no separate discriminant.
95///
96/// Identity is the declared name: enums may only be declared at the top
97/// level of the program's own files, so the name is unique program-wide and
98/// serialized forms (such as the ABI) can identify an enum by it.
99#[derive(Debug, PartialEq, Eq, Hash, Clone)]
100pub struct EnumInfo {
101    name: Arc<str>,
102    variants: Arc<[EnumVariantInfo]>,
103}
104
105impl EnumInfo {
106    /// Create an enum definition with the given `name` and `variants`.
107    ///
108    /// `variants` must not be empty: a sum of zero types would be
109    /// uninhabited, which Simplicity's type algebra cannot express.
110    /// A single-variant enum is a named wrapper of its payload.
111    pub(crate) fn new(name: Arc<str>, variants: Arc<[EnumVariantInfo]>) -> Self {
112        debug_assert!(!variants.is_empty());
113        Self { name, variants }
114    }
115
116    /// Access the declared name of the enum.
117    pub fn name(&self) -> &str {
118        &self.name
119    }
120
121    /// Access the variants of the enum in declaration order.
122    pub fn variants(&self) -> &[EnumVariantInfo] {
123        &self.variants
124    }
125
126    /// Get the variant with the given `name` and its position among the
127    /// declared variants.
128    ///
129    /// The position determines the variant's leaf in the balanced sum.
130    pub fn variant(&self, name: &Identifier) -> Option<(usize, &EnumVariantInfo)> {
131        self.variants
132            .iter()
133            .enumerate()
134            .find(|(_, v)| v.name() == name)
135    }
136
137    /// The structural payload types of all variants, in declaration order:
138    /// the leaves of the enum's balanced sum.
139    pub(crate) fn structural_variants(&self) -> Vec<StructuralType> {
140        self.variants
141            .iter()
142            .map(EnumVariantInfo::structural_payload)
143            .collect()
144    }
145}
146
147impl<A> TypeInner<A> {
148    /// Helper method for displaying type primitives based on the number of yielded children.
149    ///
150    /// We cannot implement [`fmt::Display`] because `n_children_yielded` is an extra argument.
151    fn display(&self, f: &mut fmt::Formatter<'_>, n_children_yielded: usize) -> fmt::Result {
152        match self {
153            TypeInner::Either(_, _) => match n_children_yielded {
154                0 => f.write_str("Either<"),
155                1 => f.write_str(", "),
156                n => {
157                    debug_assert_eq!(n, 2);
158                    f.write_str(">")
159                }
160            },
161            TypeInner::Option(_) => match n_children_yielded {
162                0 => f.write_str("Option<"),
163                n => {
164                    debug_assert_eq!(n, 1);
165                    f.write_str(">")
166                }
167            },
168            TypeInner::Boolean => f.write_str("bool"),
169            TypeInner::UInt(ty) => write!(f, "{ty}"),
170            TypeInner::Tuple(elements) => match n_children_yielded {
171                0 => {
172                    f.write_str("(")?;
173                    if elements.is_empty() {
174                        f.write_str(")")?;
175                    }
176                    Ok(())
177                }
178                n if n == elements.len() => {
179                    if n == 1 {
180                        f.write_str(",")?;
181                    }
182                    f.write_str(")")
183                }
184                n => {
185                    debug_assert!(n < elements.len());
186                    f.write_str(", ")
187                }
188            },
189            TypeInner::Array(_, size) => match n_children_yielded {
190                0 => f.write_str("["),
191                n => {
192                    debug_assert_eq!(n, 1);
193                    write!(f, "; {size}]")
194                }
195            },
196            TypeInner::List(_, bound) => match n_children_yielded {
197                0 => f.write_str("List<"),
198                n => {
199                    debug_assert_eq!(n, 1);
200                    write!(f, ", {bound}>")
201                }
202            },
203            TypeInner::Enum(info) => write!(f, "{}", info.name()),
204        }
205    }
206}
207
208/// Unsigned integer type.
209#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
210#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
211pub enum UIntType {
212    /// 1-bit unsigned integer
213    U1,
214    /// 2-bit unsigned integer
215    U2,
216    /// 4-bit unsigned integer
217    U4,
218    /// 8-bit unsigned integer
219    U8,
220    /// 16-bit unsigned integer
221    U16,
222    /// 32-bit unsigned integer
223    U32,
224    /// 64-bit unsigned integer
225    U64,
226    /// 128-bit unsigned integer
227    U128,
228    /// 256-bit unsigned integer
229    U256,
230}
231
232impl UIntType {
233    /// Take `n` and return the `2^n`-bit unsigned integer type.
234    pub const fn two_n(n: u32) -> Option<Self> {
235        match n {
236            0 => Some(UIntType::U1),
237            1 => Some(UIntType::U2),
238            2 => Some(UIntType::U4),
239            3 => Some(UIntType::U8),
240            4 => Some(UIntType::U16),
241            5 => Some(UIntType::U32),
242            6 => Some(UIntType::U64),
243            7 => Some(UIntType::U128),
244            8 => Some(UIntType::U256),
245            _ => None,
246        }
247    }
248
249    /// Return the bit width of values of this type.
250    pub const fn bit_width(self) -> Pow2Usize {
251        let bit_width: usize = match self {
252            UIntType::U1 => 1,
253            UIntType::U2 => 2,
254            UIntType::U4 => 4,
255            UIntType::U8 => 8,
256            UIntType::U16 => 16,
257            UIntType::U32 => 32,
258            UIntType::U64 => 64,
259            UIntType::U128 => 128,
260            UIntType::U256 => 256,
261        };
262        debug_assert!(bit_width.is_power_of_two());
263        Pow2Usize::new_unchecked(bit_width)
264    }
265
266    /// Create the unsigned integer type for the given `bit_width`.
267    pub const fn from_bit_width(bit_width: Pow2Usize) -> Option<Self> {
268        match bit_width.get() {
269            1 => Some(UIntType::U1),
270            2 => Some(UIntType::U2),
271            4 => Some(UIntType::U4),
272            8 => Some(UIntType::U8),
273            16 => Some(UIntType::U16),
274            32 => Some(UIntType::U32),
275            64 => Some(UIntType::U64),
276            128 => Some(UIntType::U128),
277            256 => Some(UIntType::U256),
278            _ => None,
279        }
280    }
281
282    /// Return the byte width of values of this type.
283    ///
284    /// Return 0 for types that take less than an entire byte: `u1`, `u2`, `u4`.
285    pub const fn byte_width(self) -> usize {
286        self.bit_width().get() / 8
287    }
288}
289
290impl fmt::Debug for UIntType {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        write!(f, "{}", self)
293    }
294}
295
296impl fmt::Display for UIntType {
297    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298        match self {
299            UIntType::U1 => f.write_str("u1"),
300            UIntType::U2 => f.write_str("u2"),
301            UIntType::U4 => f.write_str("u4"),
302            UIntType::U8 => f.write_str("u8"),
303            UIntType::U16 => f.write_str("u16"),
304            UIntType::U32 => f.write_str("u32"),
305            UIntType::U64 => f.write_str("u64"),
306            UIntType::U128 => f.write_str("u128"),
307            UIntType::U256 => f.write_str("u256"),
308        }
309    }
310}
311
312impl FromStr for UIntType {
313    type Err = String;
314
315    fn from_str(s: &str) -> Result<Self, Self::Err> {
316        match s {
317            "u1" => Ok(UIntType::U1),
318            "u2" => Ok(UIntType::U2),
319            "u4" => Ok(UIntType::U4),
320            "u8" => Ok(UIntType::U8),
321            "u16" => Ok(UIntType::U16),
322            "u32" => Ok(UIntType::U32),
323            "u64" => Ok(UIntType::U64),
324            "u128" => Ok(UIntType::U128),
325            "u256" => Ok(UIntType::U256),
326            _ => Err("Unknown integer type".to_string()),
327        }
328    }
329}
330
331impl TryFrom<&StructuralType> for UIntType {
332    type Error = ();
333
334    fn try_from(value: &StructuralType) -> Result<Self, Self::Error> {
335        let mut current = value.as_ref();
336        let mut n = 0;
337        while let Some((left, right)) = current.as_product() {
338            if left.tmr() != right.tmr() {
339                return Err(());
340            }
341            current = left;
342            n += 1;
343        }
344        if let Some((left, right)) = current.as_sum() {
345            if left.is_unit() && right.is_unit() {
346                return UIntType::two_n(n).ok_or(());
347            }
348        }
349        Err(())
350    }
351}
352
353impl TryFrom<&ResolvedType> for UIntType {
354    type Error = ();
355
356    fn try_from(value: &ResolvedType) -> Result<Self, Self::Error> {
357        UIntType::try_from(&StructuralType::from(value))
358    }
359}
360
361macro_rules! construct_int {
362    ($name: ident, $ty: ident, $text: expr) => {
363        #[doc = "Create the type of"]
364        #[doc = $text]
365        #[doc = "integers."]
366        fn $name() -> Self {
367            Self::from(UIntType::$ty)
368        }
369    };
370}
371
372/// Various type constructors.
373pub trait TypeConstructible: Sized + From<UIntType> {
374    /// Create a sum of the given `left` and `right` types.
375    fn either(left: Self, right: Self) -> Self;
376
377    /// Create an option of the given `inner` type.
378    fn option(inner: Self) -> Self;
379
380    /// Create the Boolean type.
381    fn boolean() -> Self;
382
383    /// Create a tuple from the given `elements`.
384    ///
385    /// The empty tuple is the unit type.
386    /// A tuple of two types is a product.
387    fn tuple<I: IntoIterator<Item = Self>>(elements: I) -> Self;
388
389    /// Create the unit type.
390    fn unit() -> Self {
391        Self::tuple([])
392    }
393
394    /// Create a product of the given `left` and `right` types.
395    fn product(left: Self, right: Self) -> Self {
396        Self::tuple([left, right])
397    }
398
399    /// Create an array with `size` many values of the `element` type.
400    fn array(element: Self, size: usize) -> Self;
401
402    /// Create an array of `size` many bytes.
403    fn byte_array(size: usize) -> Self {
404        Self::array(Self::u8(), size)
405    }
406
407    /// Create a list with less than `bound` many values of the `element` type.
408    fn list(element: Self, bound: NonZeroPow2Usize) -> Self;
409
410    construct_int!(u1, U1, "1-bit");
411    construct_int!(u2, U2, "2-bit");
412    construct_int!(u4, U4, "4-bit");
413    construct_int!(u8, U8, "8-bit");
414    construct_int!(u16, U16, "16-bit");
415    construct_int!(u32, U32, "32-bit");
416    construct_int!(u64, U64, "64-bit");
417    construct_int!(u128, U128, "128-bit");
418    construct_int!(u256, U256, "256-bit");
419}
420
421/// Various type destructors for types that maintain the structure in which they were created.
422///
423/// [`StructuralType`] collapses its structure into Simplicity's units, sums and products,
424/// which is why it does not implement this trait.
425pub trait TypeDeconstructible: Sized {
426    /// Access the left and right types of a sum.
427    fn as_either(&self) -> Option<(&Self, &Self)>;
428
429    /// Access the inner type of an option.
430    fn as_option(&self) -> Option<&Self>;
431
432    /// Check if the type is Boolean.
433    fn is_boolean(&self) -> bool;
434
435    /// Access the internals of an integer type.
436    fn as_integer(&self) -> Option<UIntType>;
437
438    /// Access the element types of a tuple.
439    fn as_tuple(&self) -> Option<&[Arc<Self>]>;
440
441    /// Check if the type is the unit (empty tuple).
442    fn is_unit(&self) -> bool {
443        matches!(self.as_tuple(), Some(components) if components.is_empty())
444    }
445
446    /// Access the element type and size of an array.
447    fn as_array(&self) -> Option<(&Self, usize)>;
448
449    /// Access the element type and bound of a list.
450    fn as_list(&self) -> Option<(&Self, NonZeroPow2Usize)>;
451}
452
453/// SimplicityHL type without type aliases.
454#[derive(PartialEq, Eq, Hash, Clone)]
455pub struct ResolvedType(TypeInner<Arc<Self>>);
456
457impl ResolvedType {
458    /// Access the inner type primitive.
459    pub fn as_inner(&self) -> &TypeInner<Arc<Self>> {
460        &self.0
461    }
462}
463
464/// Nominal enum types.
465///
466/// These methods are inherent rather than part of [`TypeConstructible`] and [`TypeDeconstructible`].
467/// Those traits model the structural type algebra that every type universe (aliased, resolved, structural)
468/// shares, while a nominal enum exists only at the resolved level.
469///
470/// At the structural level its identity is erased into a balanced sum, and at the source level enums
471/// enter types by name only.
472/// Keeping the constructor off the shared traits also means that only [`crate::ast`]'s scope
473/// (which owns the uniqueness of declaration ids) can mint enum types.
474impl ResolvedType {
475    /// Create a nominal enum type from the given definition.
476    pub const fn enumeration(info: EnumInfo) -> Self {
477        Self(TypeInner::Enum(info))
478    }
479
480    /// Access the enum definition if this is an enum type.
481    pub const fn as_enum(&self) -> Option<&EnumInfo> {
482        match &self.0 {
483            TypeInner::Enum(info) => Some(info),
484            _ => None,
485        }
486    }
487
488    /// Check whether the type mentions an enum, at any nesting depth.
489    pub fn contains_enum(&self) -> bool {
490        self.post_order_iter()
491            .any(|data| data.node.as_enum().is_some())
492    }
493}
494
495impl TypeConstructible for ResolvedType {
496    fn either(left: Self, right: Self) -> Self {
497        Self(TypeInner::Either(Arc::new(left), Arc::new(right)))
498    }
499
500    fn option(inner: Self) -> Self {
501        Self(TypeInner::Option(Arc::new(inner)))
502    }
503
504    fn boolean() -> Self {
505        Self(TypeInner::Boolean)
506    }
507
508    fn tuple<I: IntoIterator<Item = Self>>(elements: I) -> Self {
509        Self(TypeInner::Tuple(
510            elements.into_iter().map(Arc::new).collect(),
511        ))
512    }
513
514    fn array(element: Self, size: usize) -> Self {
515        Self(TypeInner::Array(Arc::new(element), size))
516    }
517
518    fn list(element: Self, bound: NonZeroPow2Usize) -> Self {
519        Self(TypeInner::List(Arc::new(element), bound))
520    }
521}
522
523impl TypeDeconstructible for ResolvedType {
524    fn as_either(&self) -> Option<(&Self, &Self)> {
525        match self.as_inner() {
526            TypeInner::Either(ty_l, ty_r) => Some((ty_l, ty_r)),
527            _ => None,
528        }
529    }
530
531    fn as_option(&self) -> Option<&Self> {
532        match self.as_inner() {
533            TypeInner::Option(ty) => Some(ty),
534            _ => None,
535        }
536    }
537
538    fn is_boolean(&self) -> bool {
539        matches!(self.as_inner(), TypeInner::Boolean)
540    }
541
542    fn as_integer(&self) -> Option<UIntType> {
543        match self.as_inner() {
544            TypeInner::UInt(ty) => Some(*ty),
545            _ => None,
546        }
547    }
548
549    fn as_tuple(&self) -> Option<&[Arc<Self>]> {
550        match self.as_inner() {
551            TypeInner::Tuple(components) => Some(components),
552            _ => None,
553        }
554    }
555
556    fn as_array(&self) -> Option<(&Self, usize)> {
557        match self.as_inner() {
558            TypeInner::Array(ty, size) => Some((ty, *size)),
559            _ => None,
560        }
561    }
562
563    fn as_list(&self) -> Option<(&Self, NonZeroPow2Usize)> {
564        match self.as_inner() {
565            TypeInner::List(ty, bound) => Some((ty, *bound)),
566            _ => None,
567        }
568    }
569}
570
571impl TreeLike for &ResolvedType {
572    fn as_node(&self) -> Tree<Self> {
573        match &self.0 {
574            TypeInner::Boolean | TypeInner::UInt(..) | TypeInner::Enum(..) => Tree::Nullary,
575            TypeInner::Option(l) | TypeInner::Array(l, _) | TypeInner::List(l, _) => Tree::Unary(l),
576            TypeInner::Either(l, r) => Tree::Binary(l, r),
577            TypeInner::Tuple(elements) => Tree::Nary(elements.iter().map(Arc::as_ref).collect()),
578        }
579    }
580}
581
582impl fmt::Debug for ResolvedType {
583    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584        write!(f, "{}", self)
585    }
586}
587
588impl fmt::Display for ResolvedType {
589    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590        for data in self.verbose_pre_order_iter() {
591            data.node.0.display(f, data.n_children_yielded)?;
592        }
593        Ok(())
594    }
595}
596
597impl From<UIntType> for ResolvedType {
598    fn from(value: UIntType) -> Self {
599        Self(TypeInner::UInt(value))
600    }
601}
602
603#[cfg(feature = "arbitrary")]
604impl crate::ArbitraryRec for ResolvedType {
605    // Deliberately never generates `TypeInner::Enum`.
606    // Enum values serialize as bare strings that only resolve against a program's declarations
607    // (`UnresolvedValues::resolve`), so the self-contained witness JSON round-trip target (`parse_witness_json_rtt`)
608    // would fail by design.
609    fn arbitrary_rec(u: &mut arbitrary::Unstructured, budget: usize) -> arbitrary::Result<Self> {
610        use arbitrary::Arbitrary;
611
612        match budget.checked_sub(1) {
613            None => match u.int_in_range(0..=1)? {
614                0 => Ok(Self::boolean()),
615                1 => UIntType::arbitrary(u).map(Self::from),
616                _ => unreachable!(),
617            },
618            Some(new_budget) => match u.int_in_range(0..=6)? {
619                0 => Ok(Self::boolean()),
620                1 => UIntType::arbitrary(u).map(Self::from),
621                2 => Self::arbitrary_rec(u, new_budget).map(Self::option),
622                3 => {
623                    let left = Self::arbitrary_rec(u, new_budget)?;
624                    let right = Self::arbitrary_rec(u, new_budget)?;
625                    Ok(Self::either(left, right))
626                }
627                4 => {
628                    let len = u.int_in_range(0..=3)?;
629                    (0..len)
630                        .map(|_| Self::arbitrary_rec(u, new_budget))
631                        .collect::<arbitrary::Result<Vec<Self>>>()
632                        .map(Self::tuple)
633                }
634                5 => {
635                    let element = Self::arbitrary_rec(u, new_budget)?;
636                    let size = u.int_in_range(0..=3)?;
637                    Ok(Self::array(element, size))
638                }
639                6 => {
640                    let element = Self::arbitrary_rec(u, new_budget)?;
641                    let exp = u.int_in_range(1u32..=4)?;
642                    let bound = NonZeroPow2Usize::new_unchecked(2usize.saturating_pow(exp));
643                    Ok(Self::list(element, bound))
644                }
645                _ => unreachable!(),
646            },
647        }
648    }
649}
650
651#[cfg(feature = "arbitrary")]
652impl<'a> arbitrary::Arbitrary<'a> for ResolvedType {
653    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
654        <Self as crate::ArbitraryRec>::arbitrary_rec(u, 3)
655    }
656}
657
658/// SimplicityHL type with type aliases.
659#[derive(PartialEq, Eq, Hash, Clone)]
660pub struct AliasedType(AliasedInner);
661
662/// Type alias or primitive.
663///
664/// Private struct to allow future changes.
665#[derive(Debug, PartialEq, Eq, Hash, Clone)]
666enum AliasedInner {
667    /// Type alias.
668    Alias(AliasName),
669    /// Builtin type alias.
670    Builtin(BuiltinAlias),
671    /// Type primitive.
672    Inner(TypeInner<Arc<AliasedType>>),
673}
674
675/// Type alias with predefined definition.
676#[derive(Copy, Clone, PartialEq, Eq, Hash)]
677#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
678pub enum BuiltinAlias {
679    Ctx8,
680    Pubkey,
681    Message,
682    Message64,
683    Signature,
684    Scalar,
685    Fe,
686    Ge,
687    Gej,
688    Point,
689    Height,
690    Time,
691    Distance,
692    Duration,
693    Lock,
694    Outpoint,
695    Confidential1,
696    ExplicitAsset,
697    Asset1,
698    ExplicitAmount,
699    Amount1,
700    ExplicitNonce,
701    Nonce,
702    TokenAmount1,
703}
704
705impl AliasedType {
706    /// Access a user-defined alias.
707    pub const fn as_alias(&self) -> Option<&AliasName> {
708        match &self.0 {
709            AliasedInner::Alias(name) => Some(name),
710            _ => None,
711        }
712    }
713
714    /// Access a buitlin alias.
715    pub const fn as_builtin(&self) -> Option<&BuiltinAlias> {
716        match &self.0 {
717            AliasedInner::Builtin(builtin) => Some(builtin),
718            _ => None,
719        }
720    }
721
722    /// Create a type alias from the given `identifier`.
723    pub const fn alias(name: AliasName) -> Self {
724        Self(AliasedInner::Alias(name))
725    }
726
727    /// Create a builtin type alias.
728    pub const fn builtin(builtin: BuiltinAlias) -> Self {
729        Self(AliasedInner::Builtin(builtin))
730    }
731
732    /// Resolve all aliases in the type based on the given map of `aliases` to types.
733    pub fn resolve<F, E>(&self, mut get_alias: F) -> Result<ResolvedType, E>
734    where
735        F: FnMut(&AliasName) -> Result<ResolvedType, E>,
736    {
737        let mut output = vec![];
738        for data in self.post_order_iter() {
739            match &data.node.0 {
740                AliasedInner::Alias(name) => {
741                    let resolved = get_alias(name)?;
742                    output.push(resolved);
743                }
744                AliasedInner::Builtin(builtin) => {
745                    let resolved = builtin.resolve();
746                    output.push(resolved);
747                }
748                AliasedInner::Inner(inner) => match inner {
749                    TypeInner::Either(_, _) => {
750                        let right = output.pop().unwrap();
751                        let left = output.pop().unwrap();
752                        output.push(ResolvedType::either(left, right));
753                    }
754                    TypeInner::Option(_) => {
755                        let inner = output.pop().unwrap();
756                        output.push(ResolvedType::option(inner));
757                    }
758                    TypeInner::Boolean => output.push(ResolvedType::boolean()),
759                    TypeInner::UInt(integer) => output.push(ResolvedType::from(*integer)),
760                    TypeInner::Tuple(_) => {
761                        let size = data.node.n_children();
762                        let elements = output.split_off(output.len() - size);
763                        debug_assert_eq!(elements.len(), size);
764                        output.push(ResolvedType::tuple(elements));
765                    }
766                    TypeInner::Array(_, size) => {
767                        let element = output.pop().unwrap();
768                        output.push(ResolvedType::array(element, *size));
769                    }
770                    TypeInner::List(_, bound) => {
771                        let element = output.pop().unwrap();
772                        output.push(ResolvedType::list(element, *bound));
773                    }
774                    // There is no syntax for writing an enum type inline (enums enter aliased types only by name)
775                    TypeInner::Enum(info) => {
776                        output.push(ResolvedType::enumeration(info.clone()));
777                    }
778                },
779            }
780        }
781        debug_assert_eq!(output.len(), 1);
782        Ok(output.pop().unwrap())
783    }
784
785    /// Resolve all aliases in the type based on the builtin type aliases only.
786    pub fn resolve_builtin(&self) -> Result<ResolvedType, AliasName> {
787        self.resolve(|name: &AliasName| Err(name.clone()))
788    }
789}
790
791impl_require_feature!(AliasedType {
792    recurse: 0;
793});
794
795impl_require_feature!(AliasedInner {
796    variants:
797        Alias(_),
798        Builtin(_),
799        Inner(inner),
800});
801
802impl_require_feature!(TypeInner<Arc<AliasedType>> {
803    variants:
804        Either(left, right),
805        Option(element),
806        Boolean,
807        UInt(_),
808        Tuple(elements),
809        Array(element, _),
810        List(element, _),
811        Enum(_),
812});
813
814impl TypeConstructible for AliasedType {
815    fn either(left: Self, right: Self) -> Self {
816        Self(AliasedInner::Inner(TypeInner::Either(
817            Arc::new(left),
818            Arc::new(right),
819        )))
820    }
821
822    fn option(inner: Self) -> Self {
823        Self(AliasedInner::Inner(TypeInner::Option(Arc::new(inner))))
824    }
825
826    fn boolean() -> Self {
827        Self(AliasedInner::Inner(TypeInner::Boolean))
828    }
829
830    fn tuple<I: IntoIterator<Item = Self>>(elements: I) -> Self {
831        Self(AliasedInner::Inner(TypeInner::Tuple(
832            elements.into_iter().map(Arc::new).collect(),
833        )))
834    }
835
836    fn array(element: Self, size: usize) -> Self {
837        Self(AliasedInner::Inner(TypeInner::Array(
838            Arc::new(element),
839            size,
840        )))
841    }
842
843    fn list(element: Self, bound: NonZeroPow2Usize) -> Self {
844        Self(AliasedInner::Inner(TypeInner::List(
845            Arc::new(element),
846            bound,
847        )))
848    }
849}
850
851impl TypeDeconstructible for AliasedType {
852    fn as_either(&self) -> Option<(&Self, &Self)> {
853        match &self.0 {
854            AliasedInner::Inner(TypeInner::Either(ty_l, ty_r)) => Some((ty_l, ty_r)),
855            _ => None,
856        }
857    }
858
859    fn as_option(&self) -> Option<&Self> {
860        match &self.0 {
861            AliasedInner::Inner(TypeInner::Option(ty)) => Some(ty),
862            _ => None,
863        }
864    }
865
866    fn is_boolean(&self) -> bool {
867        matches!(&self.0, AliasedInner::Inner(TypeInner::Boolean))
868    }
869
870    fn as_integer(&self) -> Option<UIntType> {
871        match &self.0 {
872            AliasedInner::Inner(TypeInner::UInt(ty)) => Some(*ty),
873            _ => None,
874        }
875    }
876
877    fn as_tuple(&self) -> Option<&[Arc<Self>]> {
878        match &self.0 {
879            AliasedInner::Inner(TypeInner::Tuple(components)) => Some(components),
880            _ => None,
881        }
882    }
883
884    fn as_array(&self) -> Option<(&Self, usize)> {
885        match &self.0 {
886            AliasedInner::Inner(TypeInner::Array(ty, size)) => Some((ty, *size)),
887            _ => None,
888        }
889    }
890
891    fn as_list(&self) -> Option<(&Self, NonZeroPow2Usize)> {
892        match &self.0 {
893            AliasedInner::Inner(TypeInner::List(ty, bound)) => Some((ty, *bound)),
894            _ => None,
895        }
896    }
897}
898
899impl TreeLike for &AliasedType {
900    fn as_node(&self) -> Tree<Self> {
901        match &self.0 {
902            AliasedInner::Alias(_) | AliasedInner::Builtin(_) => Tree::Nullary,
903            AliasedInner::Inner(inner) => match inner {
904                TypeInner::Boolean | TypeInner::UInt(..) | TypeInner::Enum(..) => Tree::Nullary,
905                TypeInner::Option(l) | TypeInner::Array(l, _) | TypeInner::List(l, _) => {
906                    Tree::Unary(l)
907                }
908                TypeInner::Either(l, r) => Tree::Binary(l, r),
909                TypeInner::Tuple(elements) => {
910                    Tree::Nary(elements.iter().map(Arc::as_ref).collect())
911                }
912            },
913        }
914    }
915}
916
917impl fmt::Debug for AliasedType {
918    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
919        write!(f, "{}", self)
920    }
921}
922
923impl fmt::Display for AliasedType {
924    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
925        for data in self.verbose_pre_order_iter() {
926            match &data.node.0 {
927                AliasedInner::Alias(alias) => write!(f, "{alias}")?,
928                AliasedInner::Builtin(builtin) => write!(f, "{builtin}")?,
929                AliasedInner::Inner(inner) => inner.display(f, data.n_children_yielded)?,
930            }
931        }
932        Ok(())
933    }
934}
935
936impl From<UIntType> for AliasedType {
937    fn from(value: UIntType) -> Self {
938        Self(AliasedInner::Inner(TypeInner::UInt(value)))
939    }
940}
941
942impl From<AliasName> for AliasedType {
943    fn from(value: AliasName) -> Self {
944        Self::alias(value)
945    }
946}
947
948impl From<BuiltinAlias> for AliasedType {
949    fn from(value: BuiltinAlias) -> Self {
950        Self::builtin(value)
951    }
952}
953
954#[cfg(feature = "arbitrary")]
955impl crate::ArbitraryRec for AliasedType {
956    fn arbitrary_rec(u: &mut arbitrary::Unstructured, budget: usize) -> arbitrary::Result<Self> {
957        use arbitrary::Arbitrary;
958
959        match budget.checked_sub(1) {
960            None => match u.int_in_range(0..=3)? {
961                0 => AliasName::arbitrary(u).map(Self::alias),
962                1 => BuiltinAlias::arbitrary(u).map(Self::builtin),
963                2 => Ok(Self::boolean()),
964                3 => UIntType::arbitrary(u).map(Self::from),
965                _ => unreachable!(),
966            },
967            Some(new_budget) => match u.int_in_range(0..=8)? {
968                0 => AliasName::arbitrary(u).map(Self::alias),
969                1 => BuiltinAlias::arbitrary(u).map(Self::builtin),
970                2 => Ok(Self::boolean()),
971                3 => UIntType::arbitrary(u).map(Self::from),
972                4 => Self::arbitrary_rec(u, new_budget).map(Self::option),
973                5 => {
974                    let left = Self::arbitrary_rec(u, new_budget)?;
975                    let right = Self::arbitrary_rec(u, new_budget)?;
976                    Ok(Self::either(left, right))
977                }
978                6 => {
979                    let len = u.int_in_range(0..=3)?;
980                    (0..len)
981                        .map(|_| Self::arbitrary_rec(u, new_budget))
982                        .collect::<arbitrary::Result<Vec<Self>>>()
983                        .map(Self::tuple)
984                }
985                7 => {
986                    let element = Self::arbitrary_rec(u, new_budget)?;
987                    let size = u.int_in_range(0..=3)?;
988                    Ok(Self::array(element, size))
989                }
990                8 => {
991                    let element = Self::arbitrary_rec(u, new_budget)?;
992                    let bound = NonZeroPow2Usize::arbitrary(u)?;
993                    Ok(Self::list(element, bound))
994                }
995                _ => unreachable!(),
996            },
997        }
998    }
999}
1000
1001#[cfg(feature = "arbitrary")]
1002impl<'a> arbitrary::Arbitrary<'a> for AliasedType {
1003    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1004        <Self as crate::ArbitraryRec>::arbitrary_rec(u, 3)
1005    }
1006}
1007
1008impl BuiltinAlias {
1009    pub fn resolve(self) -> ResolvedType {
1010        use BuiltinAlias as B;
1011        use UIntType::*;
1012
1013        match self {
1014            B::Ctx8 => ResolvedType::tuple([
1015                ResolvedType::list(U8.into(), NonZeroPow2Usize::new(64).unwrap()),
1016                ResolvedType::tuple([U64.into(), U256.into()]),
1017            ]),
1018            B::Pubkey | B::Message | B::Scalar | B::Fe | B::ExplicitAsset | B::ExplicitNonce => {
1019                U256.into()
1020            }
1021            B::Message64 | B::Signature => ResolvedType::array(U8.into(), 64),
1022            B::Ge => ResolvedType::tuple([U256.into(), U256.into()]),
1023            B::Gej => {
1024                ResolvedType::tuple([ResolvedType::tuple([U256.into(), U256.into()]), U256.into()])
1025            }
1026            B::Point | B::Confidential1 => ResolvedType::tuple([U1.into(), U256.into()]),
1027            B::Height | B::Time | B::Lock => U32.into(),
1028            B::Distance | B::Duration => U16.into(),
1029            B::Outpoint => ResolvedType::tuple([U256.into(), U32.into()]),
1030            B::Asset1 | B::Nonce => {
1031                ResolvedType::either(ResolvedType::tuple([U1.into(), U256.into()]), U256.into())
1032            }
1033            B::ExplicitAmount => U64.into(),
1034            B::Amount1 | B::TokenAmount1 => {
1035                ResolvedType::either(ResolvedType::tuple([U1.into(), U256.into()]), U64.into())
1036            }
1037        }
1038    }
1039}
1040
1041impl fmt::Debug for BuiltinAlias {
1042    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1043        write!(f, "{}", self)
1044    }
1045}
1046
1047impl fmt::Display for BuiltinAlias {
1048    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1049        match self {
1050            BuiltinAlias::Ctx8 => f.write_str("Ctx8"),
1051            BuiltinAlias::Pubkey => f.write_str("Pubkey"),
1052            BuiltinAlias::Message => f.write_str("Message"),
1053            BuiltinAlias::Message64 => f.write_str("Message64"),
1054            BuiltinAlias::Signature => f.write_str("Signature"),
1055            BuiltinAlias::Scalar => f.write_str("Scalar"),
1056            BuiltinAlias::Fe => f.write_str("Fe"),
1057            BuiltinAlias::Ge => f.write_str("Ge"),
1058            BuiltinAlias::Gej => f.write_str("Gej"),
1059            BuiltinAlias::Point => f.write_str("Point"),
1060            BuiltinAlias::Height => f.write_str("Height"),
1061            BuiltinAlias::Time => f.write_str("Time"),
1062            BuiltinAlias::Distance => f.write_str("Distance"),
1063            BuiltinAlias::Duration => f.write_str("Duration"),
1064            BuiltinAlias::Lock => f.write_str("Lock"),
1065            BuiltinAlias::Outpoint => f.write_str("Outpoint"),
1066            BuiltinAlias::Confidential1 => f.write_str("Confidential1"),
1067            BuiltinAlias::ExplicitAsset => f.write_str("ExplicitAsset"),
1068            BuiltinAlias::Asset1 => f.write_str("Asset1"),
1069            BuiltinAlias::ExplicitAmount => f.write_str("ExplicitAmount"),
1070            BuiltinAlias::Amount1 => f.write_str("Amount1"),
1071            BuiltinAlias::ExplicitNonce => f.write_str("ExplicitNonce"),
1072            BuiltinAlias::Nonce => f.write_str("Nonce"),
1073            BuiltinAlias::TokenAmount1 => f.write_str("TokenAmount1"),
1074        }
1075    }
1076}
1077
1078impl FromStr for BuiltinAlias {
1079    type Err = String;
1080
1081    fn from_str(s: &str) -> Result<Self, Self::Err> {
1082        match s {
1083            "Ctx8" => Ok(BuiltinAlias::Ctx8),
1084            "Pubkey" => Ok(BuiltinAlias::Pubkey),
1085            "Message" => Ok(BuiltinAlias::Message),
1086            "Message64" => Ok(BuiltinAlias::Message64),
1087            "Signature" => Ok(BuiltinAlias::Signature),
1088            "Scalar" => Ok(BuiltinAlias::Scalar),
1089            "Fe" => Ok(BuiltinAlias::Fe),
1090            "Ge" => Ok(BuiltinAlias::Ge),
1091            "Gej" => Ok(BuiltinAlias::Gej),
1092            "Point" => Ok(BuiltinAlias::Point),
1093            "Height" => Ok(BuiltinAlias::Height),
1094            "Time" => Ok(BuiltinAlias::Time),
1095            "Distance" => Ok(BuiltinAlias::Distance),
1096            "Duration" => Ok(BuiltinAlias::Duration),
1097            "Lock" => Ok(BuiltinAlias::Lock),
1098            "Outpoint" => Ok(BuiltinAlias::Outpoint),
1099            "Confidential1" => Ok(BuiltinAlias::Confidential1),
1100            "ExplicitAsset" => Ok(BuiltinAlias::ExplicitAsset),
1101            "Asset1" => Ok(BuiltinAlias::Asset1),
1102            "ExplicitAmount" => Ok(BuiltinAlias::ExplicitAmount),
1103            "Amount1" => Ok(BuiltinAlias::Amount1),
1104            "ExplicitNonce" => Ok(BuiltinAlias::ExplicitNonce),
1105            "Nonce" => Ok(BuiltinAlias::Nonce),
1106            "TokenAmount1" => Ok(BuiltinAlias::TokenAmount1),
1107            _ => Err("Unknown alias".to_string()),
1108        }
1109    }
1110}
1111
1112/// Internal structure of a SimplicityHL type.
1113/// 1:1 isomorphism to Simplicity.
1114#[derive(Clone, PartialEq, Eq, Hash)]
1115pub struct StructuralType(Arc<Final>);
1116
1117impl AsRef<Final> for StructuralType {
1118    fn as_ref(&self) -> &Final {
1119        &self.0
1120    }
1121}
1122
1123impl From<StructuralType> for Arc<Final> {
1124    fn from(value: StructuralType) -> Self {
1125        value.0
1126    }
1127}
1128
1129impl From<Arc<Final>> for StructuralType {
1130    fn from(value: Arc<Final>) -> Self {
1131        Self(value)
1132    }
1133}
1134
1135impl TreeLike for StructuralType {
1136    fn as_node(&self) -> Tree<Self> {
1137        match self.0.bound() {
1138            CompleteBound::Unit => Tree::Nullary,
1139            CompleteBound::Sum(l, r) | CompleteBound::Product(l, r) => {
1140                Tree::Binary(Self(l.clone()), Self(r.clone()))
1141            }
1142        }
1143    }
1144}
1145
1146impl fmt::Debug for StructuralType {
1147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1148        write!(f, "{}", self.0)
1149    }
1150}
1151
1152impl fmt::Display for StructuralType {
1153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1154        write!(f, "{}", self.0)
1155    }
1156}
1157
1158impl From<UIntType> for StructuralType {
1159    fn from(value: UIntType) -> Self {
1160        let inner = match value {
1161            UIntType::U1 => Final::two_two_n(0),
1162            UIntType::U2 => Final::two_two_n(1),
1163            UIntType::U4 => Final::two_two_n(2),
1164            UIntType::U8 => Final::two_two_n(3),
1165            UIntType::U16 => Final::two_two_n(4),
1166            UIntType::U32 => Final::two_two_n(5),
1167            UIntType::U64 => Final::two_two_n(6),
1168            UIntType::U128 => Final::two_two_n(7),
1169            UIntType::U256 => Final::two_two_n(8),
1170        };
1171        Self(inner)
1172    }
1173}
1174
1175impl From<&ResolvedType> for StructuralType {
1176    fn from(value: &ResolvedType) -> Self {
1177        let mut output = vec![];
1178        for data in value.post_order_iter() {
1179            match &data.node.0 {
1180                TypeInner::Either(_, _) => {
1181                    let right = output.pop().unwrap();
1182                    let left = output.pop().unwrap();
1183                    output.push(StructuralType::either(left, right));
1184                }
1185                TypeInner::Option(_) => {
1186                    let inner = output.pop().unwrap();
1187                    output.push(StructuralType::option(inner));
1188                }
1189                TypeInner::Boolean => output.push(StructuralType::boolean()),
1190                TypeInner::UInt(integer) => output.push(StructuralType::from(*integer)),
1191                TypeInner::Tuple(_) => {
1192                    let size = data.node.n_children();
1193                    let elements = output.split_off(output.len() - size);
1194                    debug_assert_eq!(elements.len(), size);
1195                    output.push(StructuralType::tuple(elements));
1196                }
1197                TypeInner::Array(_, size) => {
1198                    let element = output.pop().unwrap();
1199                    output.push(StructuralType::array(element, *size));
1200                }
1201                TypeInner::List(_, bound) => {
1202                    let element = output.pop().unwrap();
1203                    output.push(StructuralType::list(element, *bound));
1204                }
1205                TypeInner::Enum(info) => {
1206                    output.push(StructuralType::balanced_sum(info.structural_variants()));
1207                }
1208            }
1209        }
1210        debug_assert_eq!(output.len(), 1);
1211        output.pop().unwrap()
1212    }
1213}
1214
1215impl TypeConstructible for StructuralType {
1216    fn either(left: Self, right: Self) -> Self {
1217        Self(Final::sum(left.0, right.0))
1218    }
1219
1220    fn option(inner: Self) -> Self {
1221        Self::either(Self::unit(), inner)
1222    }
1223
1224    fn boolean() -> Self {
1225        Self::either(Self::unit(), Self::unit())
1226    }
1227
1228    fn tuple<I: IntoIterator<Item = Self>>(elements: I) -> Self {
1229        let elements: Vec<_> = elements.into_iter().collect();
1230        let tree = BTreeSlice::from_slice(&elements);
1231        tree.fold(Self::product).unwrap_or_else(Self::unit)
1232    }
1233
1234    // Keep this implementation to prevent an infinite loop in <Self as TypeConstructible>::tuple
1235    fn unit() -> Self {
1236        Self(Final::unit())
1237    }
1238
1239    // Keep this implementation to prevent an infinite loop in <Self as TypeConstructible>::tuple
1240    fn product(left: Self, right: Self) -> Self {
1241        Self(Final::product(left.0, right.0))
1242    }
1243
1244    fn array(element: Self, size: usize) -> Self {
1245        // Cheap clone because Arc<Final> consists of Arcs
1246        let elements = vec![element; size];
1247        let tree = BTreeSlice::from_slice(&elements);
1248        tree.fold(Self::product).unwrap_or_else(Self::unit)
1249    }
1250
1251    fn list(element: Self, bound: NonZeroPow2Usize) -> Self {
1252        // Cheap clone because Arc<Final> consists of Arcs
1253        let el_vector = vec![element.0; bound.get() - 1];
1254        let partition = Partition::from_slice(&el_vector, bound);
1255        debug_assert!(partition.is_complete());
1256        let process = |block: &[Arc<Final>], size: usize| -> Arc<Final> {
1257            debug_assert_eq!(block.len(), size);
1258            let tree = BTreeSlice::from_slice(block);
1259            let array = tree.fold(Final::product).unwrap();
1260            Final::sum(Final::unit(), array)
1261        };
1262        let inner = partition.fold(process, Final::product);
1263        Self(inner)
1264    }
1265}
1266
1267impl StructuralType {
1268    /// The balanced sum of the given leaf types.
1269    /// The structural type of an enum whose variants have these payload types.
1270    /// The tree shape is the one of [`BTreeSlice`], values ([`StructuralValue::enum_injection`])
1271    /// and the match lowering navigate the same shape.
1272    ///
1273    /// ## Panics
1274    ///
1275    /// `leaves` is empty: a sum of zero types would be uninhabited.
1276    ///
1277    /// [`StructuralValue::enum_injection`]: crate::value::StructuralValue
1278    pub(crate) fn balanced_sum(leaves: Vec<Self>) -> Self {
1279        BTreeSlice::from_slice(&leaves)
1280            .fold(Self::either)
1281            .expect("at least one leaf")
1282    }
1283
1284    /// Convert into an unfinalized type that can be used in Simplicity's unification algorithm.
1285    pub fn to_unfinalized<'brand>(
1286        &self,
1287        inference_context: &simplicity::types::Context<'brand>,
1288    ) -> simplicity::types::Type<'brand> {
1289        simplicity::types::Type::complete(inference_context, self.0.clone())
1290    }
1291}
1292
1293#[cfg(test)]
1294mod tests {
1295    use super::*;
1296    use crate::str::Identifier;
1297
1298    #[test]
1299    fn display_type() {
1300        let unit = ResolvedType::unit();
1301        assert_eq!("()", &unit.to_string());
1302        let singleton = ResolvedType::tuple([ResolvedType::u1()]);
1303        assert_eq!("(u1,)", &singleton.to_string());
1304        let pair = ResolvedType::tuple([ResolvedType::u1(), ResolvedType::u8()]);
1305        assert_eq!("(u1, u8)", &pair.to_string());
1306        let triple =
1307            ResolvedType::tuple([ResolvedType::u1(), ResolvedType::u8(), ResolvedType::u16()]);
1308        assert_eq!("(u1, u8, u16)", &triple.to_string());
1309        let empty_array = ResolvedType::array(ResolvedType::unit(), 0);
1310        assert_eq!("[(); 0]", &empty_array.to_string());
1311        let array = ResolvedType::array(ResolvedType::unit(), 3);
1312        assert_eq!("[(); 3]", &array.to_string());
1313        let list = ResolvedType::list(ResolvedType::unit(), NonZeroPow2Usize::TWO);
1314        assert_eq!("List<(), 2>", &list.to_string());
1315        let either = ResolvedType::either(ResolvedType::unit(), ResolvedType::u32());
1316        assert_eq!("Either<(), u32>", &either.to_string());
1317    }
1318
1319    #[test]
1320    fn enum_variant_info_payload_types() {
1321        let unit = EnumVariantInfo::new(Identifier::from_str_unchecked("Unit"), Arc::from([]));
1322        assert_eq!(&ResolvedType::unit(), unit.payload_type());
1323
1324        let single = EnumVariantInfo::new(
1325            Identifier::from_str_unchecked("Single"),
1326            Arc::from([ResolvedType::boolean()]),
1327        );
1328        assert_eq!(&ResolvedType::boolean(), single.payload_type());
1329
1330        let pair = EnumVariantInfo::new(
1331            Identifier::from_str_unchecked("Pair"),
1332            Arc::from([ResolvedType::boolean(), ResolvedType::boolean()]),
1333        );
1334        assert_eq!(
1335            &ResolvedType::tuple([ResolvedType::boolean(), ResolvedType::boolean()]),
1336            pair.payload_type()
1337        );
1338
1339        let info = EnumInfo::new(Arc::from("Test"), Arc::from([unit, single, pair]));
1340        assert_eq!("Test", info.name());
1341        assert_eq!(3, info.structural_variants().len());
1342        let (index, variant) = info
1343            .variant(&Identifier::from_str_unchecked("Pair"))
1344            .expect("Pair is a declared variant");
1345        assert_eq!(2, index);
1346        assert_eq!("Pair", variant.name().as_inner());
1347    }
1348}