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