1use std::hash::Hash;
2use std::sync::Arc;
3
4use indexmap::IndexMap;
5use serde::{Deserialize, Serialize};
6
7use crate::Union;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct FnParam {
15 pub name: Arc<str>,
16 pub ty: Option<crate::compact::SimpleType>,
19 pub default: Option<crate::compact::SimpleType>,
21 pub is_variadic: bool,
22 pub is_byref: bool,
23 pub is_optional: bool,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
31pub enum Variance {
32 #[default]
34 Invariant,
35 Covariant,
37 Contravariant,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
46pub struct TemplateParam {
47 pub name: Arc<str>,
48 pub bound: Option<Union>,
49 pub variance: Variance,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
57pub enum ArrayKey {
58 String(Arc<str>),
59 Int(i64),
60}
61
62impl PartialOrd for ArrayKey {
63 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
64 Some(self.cmp(other))
65 }
66}
67
68impl Ord for ArrayKey {
69 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
70 match (self, other) {
71 (ArrayKey::Int(a), ArrayKey::Int(b)) => a.cmp(b),
72 (ArrayKey::String(a), ArrayKey::String(b)) => a.cmp(b),
73 (ArrayKey::Int(_), ArrayKey::String(_)) => std::cmp::Ordering::Less,
75 (ArrayKey::String(_), ArrayKey::Int(_)) => std::cmp::Ordering::Greater,
76 }
77 }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
81pub struct KeyedProperty {
82 pub ty: Union,
83 pub optional: bool,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
91pub enum Atomic {
92 TString,
95 TLiteralString(Arc<str>),
97 TClassString(Option<Arc<str>>),
99 TNonEmptyString,
101 TNumericString,
103
104 TInt,
106 TLiteralInt(i64),
108 TIntRange { min: Option<i64>, max: Option<i64> },
110 TPositiveInt,
112 TNegativeInt,
114 TNonNegativeInt,
116
117 TFloat,
119 TLiteralFloat(i64, i64), TBool,
124 TTrue,
126 TFalse,
128
129 TNull,
131
132 TVoid,
135 TNever,
137 TMixed,
139 TScalar,
141 TNumeric,
143
144 TObject,
147 TNamedObject {
149 fqcn: Arc<str>,
150 type_params: Vec<Union>,
152 },
153 TStaticObject { fqcn: Arc<str> },
155 TSelf { fqcn: Arc<str> },
157 TParent { fqcn: Arc<str> },
159
160 TCallable {
163 params: Option<Vec<FnParam>>,
164 return_type: Option<Box<Union>>,
165 },
166 TClosure {
168 params: Vec<FnParam>,
169 return_type: Box<Union>,
170 this_type: Option<Box<Union>>,
171 },
172
173 TArray { key: Box<Union>, value: Box<Union> },
176 TList { value: Box<Union> },
178 TNonEmptyArray { key: Box<Union>, value: Box<Union> },
180 TNonEmptyList { value: Box<Union> },
182 TKeyedArray {
184 properties: IndexMap<ArrayKey, KeyedProperty>,
185 is_open: bool,
187 is_list: bool,
189 },
190
191 TTemplateParam {
194 name: Arc<str>,
195 as_type: Box<Union>,
196 defining_entity: Arc<str>,
198 },
199 TConditional {
201 subject: Box<Union>,
202 if_true: Box<Union>,
203 if_false: Box<Union>,
204 },
205
206 TInterfaceString,
209 TEnumString,
211 TTraitString,
213
214 TLiteralEnumCase {
217 enum_fqcn: Arc<str>,
218 case_name: Arc<str>,
219 },
220
221 TIntersection { parts: Vec<Union> },
224}
225
226impl Atomic {
227 pub fn can_be_falsy(&self) -> bool {
229 match self {
230 Atomic::TNull
231 | Atomic::TFalse
232 | Atomic::TBool
233 | Atomic::TNever
234 | Atomic::TLiteralInt(0)
235 | Atomic::TLiteralFloat(0, 0)
236 | Atomic::TInt
237 | Atomic::TFloat
238 | Atomic::TNumeric
239 | Atomic::TScalar
240 | Atomic::TMixed
241 | Atomic::TString
242 | Atomic::TNonEmptyString
243 | Atomic::TArray { .. }
244 | Atomic::TList { .. }
245 | Atomic::TNonEmptyArray { .. }
246 | Atomic::TNonEmptyList { .. } => true,
247
248 Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
249
250 Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
251
252 _ => false,
253 }
254 }
255
256 pub fn can_be_truthy(&self) -> bool {
258 match self {
259 Atomic::TNever
260 | Atomic::TVoid
261 | Atomic::TNull
262 | Atomic::TFalse
263 | Atomic::TLiteralInt(0)
264 | Atomic::TLiteralFloat(0, 0) => false,
265 Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
266 _ => true,
267 }
268 }
269
270 pub fn is_numeric(&self) -> bool {
272 matches!(
273 self,
274 Atomic::TInt
275 | Atomic::TLiteralInt(_)
276 | Atomic::TIntRange { .. }
277 | Atomic::TPositiveInt
278 | Atomic::TNegativeInt
279 | Atomic::TNonNegativeInt
280 | Atomic::TFloat
281 | Atomic::TLiteralFloat(..)
282 | Atomic::TNumeric
283 | Atomic::TNumericString
284 )
285 }
286
287 pub fn is_int(&self) -> bool {
289 matches!(
290 self,
291 Atomic::TInt
292 | Atomic::TLiteralInt(_)
293 | Atomic::TIntRange { .. }
294 | Atomic::TPositiveInt
295 | Atomic::TNegativeInt
296 | Atomic::TNonNegativeInt
297 )
298 }
299
300 pub fn is_string(&self) -> bool {
302 matches!(
303 self,
304 Atomic::TString
305 | Atomic::TLiteralString(_)
306 | Atomic::TClassString(_)
307 | Atomic::TNonEmptyString
308 | Atomic::TNumericString
309 | Atomic::TInterfaceString
310 | Atomic::TEnumString
311 | Atomic::TTraitString
312 )
313 }
314
315 pub fn is_array(&self) -> bool {
317 matches!(
318 self,
319 Atomic::TArray { .. }
320 | Atomic::TList { .. }
321 | Atomic::TNonEmptyArray { .. }
322 | Atomic::TNonEmptyList { .. }
323 | Atomic::TKeyedArray { .. }
324 )
325 }
326
327 pub fn is_object(&self) -> bool {
329 matches!(
330 self,
331 Atomic::TObject
332 | Atomic::TNamedObject { .. }
333 | Atomic::TStaticObject { .. }
334 | Atomic::TSelf { .. }
335 | Atomic::TParent { .. }
336 | Atomic::TIntersection { .. }
337 )
338 }
339
340 pub fn is_callable(&self) -> bool {
342 matches!(self, Atomic::TCallable { .. } | Atomic::TClosure { .. })
343 }
344
345 pub fn named_object_fqcn(&self) -> Option<&str> {
347 match self {
348 Atomic::TNamedObject { fqcn, .. }
349 | Atomic::TStaticObject { fqcn }
350 | Atomic::TSelf { fqcn }
351 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
352 _ => None,
353 }
354 }
355
356 pub fn type_name(&self) -> &'static str {
358 match self {
359 Atomic::TString
360 | Atomic::TLiteralString(_)
361 | Atomic::TNonEmptyString
362 | Atomic::TNumericString => "string",
363 Atomic::TClassString(_) => "class-string",
364 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
365 Atomic::TPositiveInt => "positive-int",
366 Atomic::TNegativeInt => "negative-int",
367 Atomic::TNonNegativeInt => "non-negative-int",
368 Atomic::TFloat | Atomic::TLiteralFloat(..) => "float",
369 Atomic::TBool => "bool",
370 Atomic::TTrue => "true",
371 Atomic::TFalse => "false",
372 Atomic::TNull => "null",
373 Atomic::TVoid => "void",
374 Atomic::TNever => "never",
375 Atomic::TMixed => "mixed",
376 Atomic::TScalar => "scalar",
377 Atomic::TNumeric => "numeric",
378 Atomic::TObject => "object",
379 Atomic::TNamedObject { .. } => "object",
380 Atomic::TStaticObject { .. } => "static",
381 Atomic::TSelf { .. } => "self",
382 Atomic::TParent { .. } => "parent",
383 Atomic::TCallable { .. } => "callable",
384 Atomic::TClosure { .. } => "Closure",
385 Atomic::TArray { .. } => "array",
386 Atomic::TList { .. } => "list",
387 Atomic::TNonEmptyArray { .. } => "non-empty-array",
388 Atomic::TNonEmptyList { .. } => "non-empty-list",
389 Atomic::TKeyedArray { .. } => "array",
390 Atomic::TTemplateParam { .. } => "template-param",
391 Atomic::TConditional { .. } => "conditional-type",
392 Atomic::TInterfaceString => "interface-string",
393 Atomic::TEnumString => "enum-string",
394 Atomic::TTraitString => "trait-string",
395 Atomic::TLiteralEnumCase { .. } => "enum-case",
396 Atomic::TIntersection { .. } => "intersection",
397 }
398 }
399}
400
401impl Hash for FnParam {
406 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
407 self.name.hash(state);
408 self.ty.hash(state);
409 self.default.hash(state);
410 self.is_variadic.hash(state);
411 self.is_byref.hash(state);
412 self.is_optional.hash(state);
413 }
414}
415
416#[allow(non_camel_case_types, clippy::enum_variant_names)]
418#[repr(u8)]
419enum AtomicTag {
420 TString = 0,
421 TLiteralString,
422 TClassString,
423 TNonEmptyString,
424 TNumericString,
425 TInt,
426 TLiteralInt,
427 TIntRange,
428 TPositiveInt,
429 TNegativeInt,
430 TNonNegativeInt,
431 TFloat,
432 TLiteralFloat,
433 TBool,
434 TTrue,
435 TFalse,
436 TNull,
437 TVoid,
438 TNever,
439 TMixed,
440 TScalar,
441 TNumeric,
442 TObject,
443 TNamedObject,
444 TStaticObject,
445 TSelf,
446 TParent,
447 TCallable,
448 TClosure,
449 TArray,
450 TList,
451 TNonEmptyArray,
452 TNonEmptyList,
453 TKeyedArray,
454 TTemplateParam,
455 TConditional,
456 TInterfaceString,
457 TEnumString,
458 TTraitString,
459 TLiteralEnumCase,
460 TIntersection,
461}
462
463impl Hash for Atomic {
464 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
465 use AtomicTag as T;
466 match self {
467 Atomic::TString => (T::TString as u8).hash(state),
469 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
470 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
471 Atomic::TInt => (T::TInt as u8).hash(state),
472 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
473 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
474 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
475 Atomic::TFloat => (T::TFloat as u8).hash(state),
476 Atomic::TBool => (T::TBool as u8).hash(state),
477 Atomic::TTrue => (T::TTrue as u8).hash(state),
478 Atomic::TFalse => (T::TFalse as u8).hash(state),
479 Atomic::TNull => (T::TNull as u8).hash(state),
480 Atomic::TVoid => (T::TVoid as u8).hash(state),
481 Atomic::TNever => (T::TNever as u8).hash(state),
482 Atomic::TMixed => (T::TMixed as u8).hash(state),
483 Atomic::TScalar => (T::TScalar as u8).hash(state),
484 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
485 Atomic::TObject => (T::TObject as u8).hash(state),
486 Atomic::TInterfaceString => (T::TInterfaceString as u8).hash(state),
487 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
488 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
489
490 Atomic::TLiteralString(s) => {
492 (T::TLiteralString as u8).hash(state);
493 s.hash(state);
494 }
495 Atomic::TClassString(opt) => {
496 (T::TClassString as u8).hash(state);
497 opt.hash(state);
498 }
499 Atomic::TLiteralInt(n) => {
500 (T::TLiteralInt as u8).hash(state);
501 n.hash(state);
502 }
503 Atomic::TIntRange { min, max } => {
504 (T::TIntRange as u8).hash(state);
505 min.hash(state);
506 max.hash(state);
507 }
508 Atomic::TLiteralFloat(int_bits, frac_bits) => {
509 (T::TLiteralFloat as u8).hash(state);
510 int_bits.hash(state);
511 frac_bits.hash(state);
512 }
513 Atomic::TNamedObject { fqcn, type_params } => {
514 (T::TNamedObject as u8).hash(state);
515 fqcn.hash(state);
516 type_params.hash(state);
517 }
518 Atomic::TStaticObject { fqcn } => {
519 (T::TStaticObject as u8).hash(state);
520 fqcn.hash(state);
521 }
522 Atomic::TSelf { fqcn } => {
523 (T::TSelf as u8).hash(state);
524 fqcn.hash(state);
525 }
526 Atomic::TParent { fqcn } => {
527 (T::TParent as u8).hash(state);
528 fqcn.hash(state);
529 }
530 Atomic::TCallable {
531 params,
532 return_type,
533 } => {
534 (T::TCallable as u8).hash(state);
535 params.hash(state);
536 return_type.hash(state);
537 }
538 Atomic::TClosure {
539 params,
540 return_type,
541 this_type,
542 } => {
543 (T::TClosure as u8).hash(state);
544 params.hash(state);
545 return_type.hash(state);
546 this_type.hash(state);
547 }
548 Atomic::TArray { key, value } => {
549 (T::TArray as u8).hash(state);
550 key.hash(state);
551 value.hash(state);
552 }
553 Atomic::TList { value } => {
554 (T::TList as u8).hash(state);
555 value.hash(state);
556 }
557 Atomic::TNonEmptyArray { key, value } => {
558 (T::TNonEmptyArray as u8).hash(state);
559 key.hash(state);
560 value.hash(state);
561 }
562 Atomic::TNonEmptyList { value } => {
563 (T::TNonEmptyList as u8).hash(state);
564 value.hash(state);
565 }
566 Atomic::TKeyedArray {
567 properties,
568 is_open,
569 is_list,
570 } => {
571 (T::TKeyedArray as u8).hash(state);
572 let mut pairs: Vec<_> = properties.iter().collect();
575 pairs.sort_by_key(|(a, _)| *a);
576 for (k, v) in pairs {
577 k.hash(state);
578 v.hash(state);
579 }
580 is_open.hash(state);
581 is_list.hash(state);
582 }
583 Atomic::TTemplateParam {
584 name,
585 as_type,
586 defining_entity,
587 } => {
588 (T::TTemplateParam as u8).hash(state);
589 name.hash(state);
590 as_type.hash(state);
591 defining_entity.hash(state);
592 }
593 Atomic::TConditional {
594 subject,
595 if_true,
596 if_false,
597 } => {
598 (T::TConditional as u8).hash(state);
599 subject.hash(state);
600 if_true.hash(state);
601 if_false.hash(state);
602 }
603 Atomic::TLiteralEnumCase {
604 enum_fqcn,
605 case_name,
606 } => {
607 (T::TLiteralEnumCase as u8).hash(state);
608 enum_fqcn.hash(state);
609 case_name.hash(state);
610 }
611 Atomic::TIntersection { parts } => {
612 (T::TIntersection as u8).hash(state);
613 parts.hash(state);
614 }
615 }
616 }
617}