mago_codex/ttype/
combination.rs1use std::collections::BTreeMap;
2
3use foldhash::HashSet;
4use ordered_float::OrderedFloat;
5
6use mago_atom::Atom;
7use mago_atom::AtomMap;
8use mago_atom::AtomSet;
9
10use crate::ttype::atomic::TAtomic;
11use crate::ttype::atomic::array::TArray;
12use crate::ttype::atomic::array::key::ArrayKey;
13use crate::ttype::atomic::derived::TDerived;
14use crate::ttype::atomic::scalar::int::TInteger;
15use crate::ttype::union::TUnion;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
18#[repr(transparent)]
19pub struct CombinationFlags(u32);
20
21impl CombinationFlags {
22 pub const HAS_OBJECT_TOP_TYPE: CombinationFlags = CombinationFlags(1 << 0);
23 pub const LIST_ARRAY_SOMETIMES_FILLED: CombinationFlags = CombinationFlags(1 << 1);
24 pub const LIST_ARRAY_ALWAYS_FILLED: CombinationFlags = CombinationFlags(1 << 2);
25 pub const KEYED_ARRAY_SOMETIMES_FILLED: CombinationFlags = CombinationFlags(1 << 3);
26 pub const KEYED_ARRAY_ALWAYS_FILLED: CombinationFlags = CombinationFlags(1 << 4);
27 pub const HAS_EMPTY_ARRAY: CombinationFlags = CombinationFlags(1 << 5);
28 pub const HAS_KEYED_ARRAY: CombinationFlags = CombinationFlags(1 << 6);
29 pub const GENERIC_MIXED: CombinationFlags = CombinationFlags(1 << 7);
30 pub const HAS_MIXED: CombinationFlags = CombinationFlags(1 << 8);
31 pub const RESOURCE: CombinationFlags = CombinationFlags(1 << 9);
32 pub const OPEN_RESOURCE: CombinationFlags = CombinationFlags(1 << 10);
33 pub const CLOSED_RESOURCE: CombinationFlags = CombinationFlags(1 << 11);
34 const FALSY_MIXED_SET: CombinationFlags = CombinationFlags(1 << 12);
36 const FALSY_MIXED_VALUE: CombinationFlags = CombinationFlags(1 << 13);
37 const TRUTHY_MIXED_SET: CombinationFlags = CombinationFlags(1 << 14);
38 const TRUTHY_MIXED_VALUE: CombinationFlags = CombinationFlags(1 << 15);
39 const NONNULL_MIXED_SET: CombinationFlags = CombinationFlags(1 << 16);
40 const NONNULL_MIXED_VALUE: CombinationFlags = CombinationFlags(1 << 17);
41 const MIXED_FROM_LOOP_ISSET_SET: CombinationFlags = CombinationFlags(1 << 18);
42 const MIXED_FROM_LOOP_ISSET_VALUE: CombinationFlags = CombinationFlags(1 << 19);
43}
44
45impl CombinationFlags {
46 #[inline]
47 pub const fn insert(&mut self, other: CombinationFlags) {
48 self.0 |= other.0;
49 }
50
51 #[inline]
52 pub const fn remove(&mut self, other: CombinationFlags) {
53 self.0 &= !other.0;
54 }
55
56 #[inline]
57 #[must_use]
58 pub const fn contains(self, other: CombinationFlags) -> bool {
59 (self.0 & other.0) == other.0
60 }
61
62 #[inline]
63 #[must_use]
64 pub const fn intersects(self, other: CombinationFlags) -> bool {
65 (self.0 & other.0) != 0
66 }
67
68 #[inline]
70 #[must_use]
71 pub fn get_tristate(self, set_bit: CombinationFlags, value_bit: CombinationFlags) -> Option<bool> {
72 if self.contains(set_bit) { Some(self.contains(value_bit)) } else { None }
73 }
74
75 #[inline]
77 pub fn set_tristate(&mut self, set_bit: CombinationFlags, value_bit: CombinationFlags, value: Option<bool>) {
78 match value {
79 None => {
80 self.remove(set_bit);
81 self.remove(value_bit);
82 }
83 Some(false) => {
84 self.insert(set_bit);
85 self.remove(value_bit);
86 }
87 Some(true) => {
88 self.insert(set_bit);
89 self.insert(value_bit);
90 }
91 }
92 }
93
94 #[inline]
95 #[must_use]
96 pub fn falsy_mixed(self) -> Option<bool> {
97 self.get_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE)
98 }
99
100 #[inline]
101 pub fn set_falsy_mixed(&mut self, value: Option<bool>) {
102 self.set_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE, value);
103 }
104
105 #[inline]
106 #[must_use]
107 pub fn truthy_mixed(self) -> Option<bool> {
108 self.get_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE)
109 }
110
111 #[inline]
112 pub fn set_truthy_mixed(&mut self, value: Option<bool>) {
113 self.set_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE, value);
114 }
115
116 #[inline]
117 #[must_use]
118 pub fn nonnull_mixed(self) -> Option<bool> {
119 self.get_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE)
120 }
121
122 #[inline]
123 pub fn set_nonnull_mixed(&mut self, value: Option<bool>) {
124 self.set_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE, value);
125 }
126
127 #[inline]
128 #[must_use]
129 pub fn mixed_from_loop_isset(self) -> Option<bool> {
130 self.get_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE)
131 }
132
133 #[inline]
134 pub fn set_mixed_from_loop_isset(&mut self, value: Option<bool>) {
135 self.set_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE, value);
136 }
137}
138
139#[derive(Debug)]
140pub struct TypeCombination {
141 pub flags: CombinationFlags,
142 pub value_types: AtomMap<TAtomic>,
143 pub enum_names: HashSet<(Atom, Option<Atom>)>,
144 pub object_type_params: AtomMap<(Atom, Vec<TUnion>)>,
145 pub object_static: AtomMap<bool>,
146 pub list_array_counts: Option<HashSet<usize>>,
147 pub keyed_array_entries: BTreeMap<ArrayKey, (bool, TUnion)>,
148 pub list_array_entries: BTreeMap<usize, (bool, TUnion)>,
149 pub keyed_array_parameters: Option<(TUnion, TUnion)>,
150 pub list_array_parameter: Option<TUnion>,
151 pub sealed_arrays: Vec<TArray>,
152 pub sealed_keyed_budget_exhausted: bool,
153 pub integers: Vec<TInteger>,
154 pub literal_strings: AtomSet,
155 pub literal_floats: Vec<OrderedFloat<f64>>,
156 pub class_string_types: AtomMap<TAtomic>,
157 pub derived_types: HashSet<TDerived>,
158}
159
160impl Default for TypeCombination {
161 fn default() -> Self {
162 Self::new()
163 }
164}
165
166impl TypeCombination {
167 #[must_use]
168 pub fn new() -> Self {
169 let flags = CombinationFlags::LIST_ARRAY_ALWAYS_FILLED | CombinationFlags::KEYED_ARRAY_ALWAYS_FILLED;
170
171 Self {
172 flags,
173 value_types: AtomMap::default(),
174 object_type_params: AtomMap::default(),
175 object_static: AtomMap::default(),
176 list_array_counts: Some(HashSet::default()),
177 keyed_array_entries: BTreeMap::new(),
178 list_array_entries: BTreeMap::new(),
179 keyed_array_parameters: None,
180 list_array_parameter: None,
181 sealed_arrays: Vec::new(),
182 sealed_keyed_budget_exhausted: false,
183 literal_strings: AtomSet::default(),
184 integers: Vec::new(),
185 literal_floats: Vec::new(),
186 class_string_types: AtomMap::default(),
187 enum_names: HashSet::default(),
188 derived_types: HashSet::default(),
189 }
190 }
191
192 #[inline]
193 #[must_use]
194 pub fn is_simple(&self) -> bool {
195 if self.value_types.len() == 1
196 && self.sealed_arrays.is_empty()
197 && !self.flags.contains(CombinationFlags::HAS_KEYED_ARRAY)
198 && !self.flags.contains(CombinationFlags::HAS_EMPTY_ARRAY)
199 && !self.flags.intersects(
200 CombinationFlags::RESOURCE | CombinationFlags::OPEN_RESOURCE | CombinationFlags::CLOSED_RESOURCE,
201 )
202 && self.keyed_array_parameters.is_none()
203 && self.list_array_parameter.is_none()
204 {
205 return self.object_type_params.is_empty()
206 && self.enum_names.is_empty()
207 && self.literal_strings.is_empty()
208 && self.literal_floats.is_empty()
209 && self.class_string_types.is_empty()
210 && self.integers.is_empty()
211 && self.derived_types.is_empty();
212 }
213
214 false
215 }
216}
217
218impl std::ops::BitOr for CombinationFlags {
219 type Output = Self;
220
221 #[inline]
222 fn bitor(self, rhs: Self) -> Self::Output {
223 CombinationFlags(self.0 | rhs.0)
224 }
225}
226
227impl std::ops::BitAnd for CombinationFlags {
228 type Output = Self;
229
230 #[inline]
231 fn bitand(self, rhs: Self) -> Self::Output {
232 CombinationFlags(self.0 & rhs.0)
233 }
234}
235
236impl std::ops::BitXor for CombinationFlags {
237 type Output = Self;
238
239 #[inline]
240 fn bitxor(self, rhs: Self) -> Self::Output {
241 CombinationFlags(self.0 ^ rhs.0)
242 }
243}
244
245impl std::ops::Not for CombinationFlags {
246 type Output = Self;
247
248 #[inline]
249 fn not(self) -> Self::Output {
250 CombinationFlags(!self.0)
251 }
252}