Skip to main content

toasty_core/stmt/
ty_union.rs

1use super::Type;
2
3/// A set of types representing the possible result types of a match expression.
4///
5/// `TypeUnion` enforces the set invariant: inserting a type that is already
6/// present is a no-op. Order is not significant; two `TypeUnion` values are
7/// equal if they contain the same types regardless of insertion order.
8#[derive(Debug, Clone)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct TypeUnion {
11    // Invariant: no duplicates.
12    types: Vec<Type>,
13}
14
15impl TypeUnion {
16    /// Creates an empty type union.
17    ///
18    /// # Examples
19    ///
20    /// ```
21    /// # use toasty_core::stmt::TypeUnion;
22    /// let u = TypeUnion::new();
23    /// assert!(u.is_empty());
24    /// ```
25    pub fn new() -> Self {
26        Self { types: Vec::new() }
27    }
28
29    /// Insert `ty` if it is not already present. Returns whether it was inserted.
30    ///
31    /// `Type::Unknown` is skipped — it carries no type information and should
32    /// not widen the union (e.g. an `Expr::Error` branch in a Match).
33    pub fn insert(&mut self, ty: Type) -> bool {
34        if matches!(ty, Type::Unknown) {
35            return false;
36        }
37        if self.types.contains(&ty) {
38            return false;
39        }
40        self.types.push(ty);
41        true
42    }
43
44    /// Returns `true` if this union contains `ty`.
45    pub fn contains(&self, ty: &Type) -> bool {
46        self.types.contains(ty)
47    }
48
49    /// Returns the number of types in the union.
50    pub fn len(&self) -> usize {
51        self.types.len()
52    }
53
54    /// Returns `true` if the union contains no types.
55    pub fn is_empty(&self) -> bool {
56        self.types.is_empty()
57    }
58
59    /// Returns an iterator over the types in the union.
60    pub fn iter(&self) -> impl Iterator<Item = &Type> {
61        self.types.iter()
62    }
63
64    /// Collapse the union into a single `Type`.
65    ///
66    /// - 0 members → `Type::Null` (no type information)
67    /// - 1 member  → that type directly (no union needed)
68    /// - 2+ members → `Type::Union(self)`
69    pub fn simplify(self) -> Type {
70        match self.types.len() {
71            0 => Type::Null,
72            1 => self.types.into_iter().next().unwrap(),
73            _ => Type::Union(self),
74        }
75    }
76}
77
78impl Default for TypeUnion {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84/// Set equality: two unions are equal iff they contain the same types,
85/// regardless of insertion order.
86impl PartialEq for TypeUnion {
87    fn eq(&self, other: &Self) -> bool {
88        self.types.len() == other.types.len() && self.types.iter().all(|t| other.types.contains(t))
89    }
90}
91
92impl Eq for TypeUnion {}
93
94impl IntoIterator for TypeUnion {
95    type Item = Type;
96    type IntoIter = std::vec::IntoIter<Type>;
97
98    fn into_iter(self) -> Self::IntoIter {
99        self.types.into_iter()
100    }
101}
102
103impl<'a> IntoIterator for &'a TypeUnion {
104    type Item = &'a Type;
105    type IntoIter = std::slice::Iter<'a, Type>;
106
107    fn into_iter(self) -> Self::IntoIter {
108        self.types.iter()
109    }
110}
111
112impl From<TypeUnion> for Type {
113    fn from(value: TypeUnion) -> Self {
114        Type::Union(value)
115    }
116}