1use std::hash::Hash;
2use std::sync::Arc;
3
4use indexmap::IndexMap;
5use serde::{Deserialize, Serialize};
6
7use crate::symbol::Name;
8use crate::Type;
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct FnParam {
16 pub name: Name,
17 pub ty: Option<crate::compact::SimpleType>,
20 #[serde(default)]
25 pub out_ty: Option<crate::compact::SimpleType>,
26 pub default: Option<crate::compact::SimpleType>,
28 pub is_variadic: bool,
29 pub is_byref: bool,
30 pub is_optional: bool,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
38pub enum Variance {
39 #[default]
41 Invariant,
42 Covariant,
44 Contravariant,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub struct TemplateParam {
54 pub name: Name,
55 pub bound: Option<Type>,
56 pub variance: Variance,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
64pub enum ArrayKey {
65 String(Arc<str>),
66 Int(i64),
67}
68
69impl PartialOrd for ArrayKey {
70 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
71 Some(self.cmp(other))
72 }
73}
74
75impl Ord for ArrayKey {
76 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
77 match (self, other) {
78 (ArrayKey::Int(a), ArrayKey::Int(b)) => a.cmp(b),
79 (ArrayKey::String(a), ArrayKey::String(b)) => a.cmp(b),
80 (ArrayKey::Int(_), ArrayKey::String(_)) => std::cmp::Ordering::Less,
82 (ArrayKey::String(_), ArrayKey::Int(_)) => std::cmp::Ordering::Greater,
83 }
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub struct KeyedProperty {
89 pub ty: Type,
90 pub optional: bool,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
99pub struct ClosureData {
100 pub params: Box<[FnParam]>,
101 pub return_type: Type,
102 pub this_type: Option<Type>,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
107pub struct ConditionalData {
108 pub param_name: Option<Name>,
111 pub subject: Type,
112 pub if_true: Type,
113 pub if_false: Type,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
121pub enum Atomic {
122 TString,
125 TLiteralString(Arc<str>),
127 TCallableString,
129 TClassString(Option<Name>),
131 TNonEmptyString,
133 TNumericString,
135
136 TInt,
138 TLiteralInt(i64),
140 TIntRange { min: Option<i64>, max: Option<i64> },
142 TPositiveInt,
144 TNegativeInt,
146 TNonNegativeInt,
148
149 TFloat,
151 TIntegralFloat,
155 TLiteralFloat(i64, i64), TBool,
160 TTrue,
162 TFalse,
164
165 TNull,
167
168 TVoid,
171 TNever,
173 TMixed,
175 TScalar,
177 TNumeric,
179
180 TObject,
183 TNamedObject {
185 fqcn: Name,
186 type_params: Arc<[Type]>,
188 },
189 TStaticObject { fqcn: Name },
191 TSelf { fqcn: Name },
193 TParent { fqcn: Name },
195
196 TCallable {
199 params: Option<Box<[FnParam]>>,
202 return_type: Option<Box<Type>>,
203 },
204 TClosure { data: Box<ClosureData> },
207
208 TArray { key: Box<Type>, value: Box<Type> },
211 TList { value: Box<Type> },
213 TNonEmptyArray { key: Box<Type>, value: Box<Type> },
215 TNonEmptyList { value: Box<Type> },
217 TKeyedArray {
219 properties: Box<IndexMap<ArrayKey, KeyedProperty>>,
222 is_open: bool,
224 is_list: bool,
226 },
227
228 TTemplateParam {
231 name: Name,
232 as_type: Box<Type>,
233 defining_entity: Name,
235 },
236 TKeyOf { target: Box<Type> },
238 TValueOf { target: Box<Type> },
240 TConditional { data: Box<ConditionalData> },
243
244 TInterfaceString(Option<Name>),
247 TEnumString,
249 TTraitString,
251
252 TLiteralEnumCase { enum_fqcn: Name, case_name: Name },
255
256 TIntersection { parts: Arc<[Type]> },
259}
260
261impl Atomic {
262 pub fn can_be_falsy(&self) -> bool {
264 match self {
265 Atomic::TNull
266 | Atomic::TFalse
267 | Atomic::TBool
268 | Atomic::TNever
269 | Atomic::TLiteralInt(0)
270 | Atomic::TLiteralFloat(0, 0)
271 | Atomic::TInt
272 | Atomic::TFloat
273 | Atomic::TNumeric
274 | Atomic::TScalar
275 | Atomic::TMixed
276 | Atomic::TString
277 | Atomic::TNonEmptyString | Atomic::TNumericString | Atomic::TKeyOf { .. }
280 | Atomic::TValueOf { .. }
281 | Atomic::TArray { .. }
282 | Atomic::TList { .. } => true,
283 Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
287
288 Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
289
290 Atomic::TIntRange { min, max } => {
292 min.is_none_or(|lo| lo <= 0) && max.is_none_or(|hi| hi >= 0)
293 }
294 Atomic::TNonNegativeInt => true,
296
297 _ => false,
298 }
299 }
300
301 pub fn can_be_truthy(&self) -> bool {
303 match self {
304 Atomic::TNever
305 | Atomic::TVoid
306 | Atomic::TNull
307 | Atomic::TFalse
308 | Atomic::TLiteralInt(0)
309 | Atomic::TLiteralFloat(0, 0) => false,
310 Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
311 Atomic::TIntRange {
313 min: Some(0),
314 max: Some(0),
315 } => false,
316 Atomic::TKeyedArray {
318 properties,
319 is_open,
320 ..
321 } if !is_open && properties.is_empty() => false,
322 _ => true,
323 }
324 }
325
326 pub fn is_numeric(&self) -> bool {
328 matches!(
329 self,
330 Atomic::TInt
331 | Atomic::TLiteralInt(_)
332 | Atomic::TIntRange { .. }
333 | Atomic::TPositiveInt
334 | Atomic::TNegativeInt
335 | Atomic::TNonNegativeInt
336 | Atomic::TFloat
337 | Atomic::TIntegralFloat
338 | Atomic::TLiteralFloat(..)
339 | Atomic::TNumeric
340 | Atomic::TNumericString
341 )
342 }
343
344 pub fn is_int(&self) -> bool {
346 matches!(
347 self,
348 Atomic::TInt
349 | Atomic::TLiteralInt(_)
350 | Atomic::TIntRange { .. }
351 | Atomic::TPositiveInt
352 | Atomic::TNegativeInt
353 | Atomic::TNonNegativeInt
354 )
355 }
356
357 pub fn is_string(&self) -> bool {
359 matches!(
360 self,
361 Atomic::TString
362 | Atomic::TLiteralString(_)
363 | Atomic::TCallableString
364 | Atomic::TClassString(_)
365 | Atomic::TNonEmptyString
366 | Atomic::TNumericString
367 | Atomic::TInterfaceString(_)
368 | Atomic::TEnumString
369 | Atomic::TTraitString
370 )
371 }
372
373 pub fn is_array(&self) -> bool {
375 matches!(
376 self,
377 Atomic::TArray { .. }
378 | Atomic::TList { .. }
379 | Atomic::TNonEmptyArray { .. }
380 | Atomic::TNonEmptyList { .. }
381 | Atomic::TKeyedArray { .. }
382 )
383 }
384
385 pub fn is_object(&self) -> bool {
387 matches!(
388 self,
389 Atomic::TObject
390 | Atomic::TNamedObject { .. }
391 | Atomic::TStaticObject { .. }
392 | Atomic::TSelf { .. }
393 | Atomic::TParent { .. }
394 | Atomic::TIntersection { .. }
395 )
396 }
397
398 pub fn is_definitely_non_object(&self) -> bool {
403 matches!(
404 self,
405 Atomic::TString
406 | Atomic::TLiteralString(_)
407 | Atomic::TCallableString
408 | Atomic::TClassString(_)
409 | Atomic::TNonEmptyString
410 | Atomic::TNumericString
411 | Atomic::TInterfaceString(_)
412 | Atomic::TEnumString
413 | Atomic::TTraitString
414 | Atomic::TInt
415 | Atomic::TLiteralInt(_)
416 | Atomic::TIntRange { .. }
417 | Atomic::TPositiveInt
418 | Atomic::TNegativeInt
419 | Atomic::TNonNegativeInt
420 | Atomic::TFloat
421 | Atomic::TIntegralFloat
422 | Atomic::TLiteralFloat(..)
423 | Atomic::TBool
424 | Atomic::TTrue
425 | Atomic::TFalse
426 | Atomic::TNull
427 | Atomic::TVoid
428 | Atomic::TArray { .. }
429 | Atomic::TList { .. }
430 | Atomic::TNonEmptyArray { .. }
431 | Atomic::TNonEmptyList { .. }
432 | Atomic::TKeyedArray { .. }
433 | Atomic::TScalar
434 | Atomic::TNumeric
435 )
436 }
437
438 pub fn is_callable(&self) -> bool {
443 matches!(
444 self,
445 Atomic::TCallable { .. } | Atomic::TClosure { .. } | Atomic::TCallableString
446 )
447 }
448
449 pub fn named_object_fqcn(&self) -> Option<&str> {
451 match self {
452 Atomic::TNamedObject { fqcn, .. }
453 | Atomic::TStaticObject { fqcn }
454 | Atomic::TSelf { fqcn }
455 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
456 _ => None,
457 }
458 }
459
460 pub fn type_name(&self) -> &'static str {
462 match self {
463 Atomic::TString
464 | Atomic::TLiteralString(_)
465 | Atomic::TNonEmptyString
466 | Atomic::TNumericString => "string",
467 Atomic::TCallableString => "callable-string",
468 Atomic::TClassString(_) => "class-string",
469 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
470 Atomic::TPositiveInt => "positive-int",
471 Atomic::TNegativeInt => "negative-int",
472 Atomic::TNonNegativeInt => "non-negative-int",
473 Atomic::TFloat | Atomic::TIntegralFloat | Atomic::TLiteralFloat(..) => "float",
474 Atomic::TBool => "bool",
475 Atomic::TTrue => "true",
476 Atomic::TFalse => "false",
477 Atomic::TNull => "null",
478 Atomic::TVoid => "void",
479 Atomic::TNever => "never",
480 Atomic::TMixed => "mixed",
481 Atomic::TScalar => "scalar",
482 Atomic::TNumeric => "numeric",
483 Atomic::TObject => "object",
484 Atomic::TNamedObject { .. } => "object",
485 Atomic::TStaticObject { .. } => "static",
486 Atomic::TSelf { .. } => "self",
487 Atomic::TParent { .. } => "parent",
488 Atomic::TCallable { .. } => "callable",
489 Atomic::TClosure { .. } => "Closure",
490 Atomic::TArray { .. } => "array",
491 Atomic::TList { .. } => "list",
492 Atomic::TNonEmptyArray { .. } => "non-empty-array",
493 Atomic::TNonEmptyList { .. } => "non-empty-list",
494 Atomic::TKeyedArray { .. } => "array",
495 Atomic::TTemplateParam { .. } => "template-param",
496 Atomic::TKeyOf { .. } => "key-of",
497 Atomic::TValueOf { .. } => "value-of",
498 Atomic::TConditional { .. } => "conditional-type",
499 Atomic::TInterfaceString(_) => "interface-string",
500 Atomic::TEnumString => "enum-string",
501 Atomic::TTraitString => "trait-string",
502 Atomic::TLiteralEnumCase { .. } => "enum-case",
503 Atomic::TIntersection { .. } => "intersection",
504 }
505 }
506}
507
508impl Hash for FnParam {
513 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
514 self.name.hash(state);
515 self.ty.hash(state);
516 self.out_ty.hash(state);
517 self.default.hash(state);
518 self.is_variadic.hash(state);
519 self.is_byref.hash(state);
520 self.is_optional.hash(state);
521 }
522}
523
524#[allow(non_camel_case_types, clippy::enum_variant_names)]
526#[repr(u8)]
527enum AtomicTag {
528 TString = 0,
529 TLiteralString,
530 TCallableString,
531 TClassString,
532 TNonEmptyString,
533 TNumericString,
534 TInt,
535 TLiteralInt,
536 TIntRange,
537 TPositiveInt,
538 TNegativeInt,
539 TNonNegativeInt,
540 TFloat,
541 TIntegralFloat,
542 TLiteralFloat,
543 TBool,
544 TTrue,
545 TFalse,
546 TNull,
547 TVoid,
548 TNever,
549 TMixed,
550 TScalar,
551 TNumeric,
552 TObject,
553 TNamedObject,
554 TStaticObject,
555 TSelf,
556 TParent,
557 TCallable,
558 TClosure,
559 TArray,
560 TList,
561 TNonEmptyArray,
562 TNonEmptyList,
563 TKeyedArray,
564 TTemplateParam,
565 TKeyOf,
566 TValueOf,
567 TConditional,
568 TInterfaceString,
569 TEnumString,
570 TTraitString,
571 TLiteralEnumCase,
572 TIntersection,
573}
574
575impl Hash for Atomic {
576 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
577 use AtomicTag as T;
578 match self {
579 Atomic::TString => (T::TString as u8).hash(state),
581 Atomic::TCallableString => (T::TCallableString as u8).hash(state),
582 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
583 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
584 Atomic::TInt => (T::TInt as u8).hash(state),
585 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
586 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
587 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
588 Atomic::TFloat => (T::TFloat as u8).hash(state),
589 Atomic::TIntegralFloat => (T::TIntegralFloat as u8).hash(state),
590 Atomic::TBool => (T::TBool as u8).hash(state),
591 Atomic::TTrue => (T::TTrue as u8).hash(state),
592 Atomic::TFalse => (T::TFalse as u8).hash(state),
593 Atomic::TNull => (T::TNull as u8).hash(state),
594 Atomic::TVoid => (T::TVoid as u8).hash(state),
595 Atomic::TNever => (T::TNever as u8).hash(state),
596 Atomic::TMixed => (T::TMixed as u8).hash(state),
597 Atomic::TScalar => (T::TScalar as u8).hash(state),
598 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
599 Atomic::TObject => (T::TObject as u8).hash(state),
600 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
601 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
602
603 Atomic::TLiteralString(s) => {
605 (T::TLiteralString as u8).hash(state);
606 s.hash(state);
607 }
608 Atomic::TClassString(opt) => {
609 (T::TClassString as u8).hash(state);
610 opt.hash(state);
611 }
612 Atomic::TInterfaceString(opt) => {
613 (T::TInterfaceString as u8).hash(state);
614 opt.hash(state);
615 }
616 Atomic::TLiteralInt(n) => {
617 (T::TLiteralInt as u8).hash(state);
618 n.hash(state);
619 }
620 Atomic::TIntRange { min, max } => {
621 (T::TIntRange as u8).hash(state);
622 min.hash(state);
623 max.hash(state);
624 }
625 Atomic::TLiteralFloat(int_bits, frac_bits) => {
626 (T::TLiteralFloat as u8).hash(state);
627 int_bits.hash(state);
628 frac_bits.hash(state);
629 }
630 Atomic::TNamedObject { fqcn, type_params } => {
631 (T::TNamedObject as u8).hash(state);
632 fqcn.hash(state);
633 type_params.hash(state);
634 }
635 Atomic::TStaticObject { fqcn } => {
636 (T::TStaticObject as u8).hash(state);
637 fqcn.hash(state);
638 }
639 Atomic::TSelf { fqcn } => {
640 (T::TSelf as u8).hash(state);
641 fqcn.hash(state);
642 }
643 Atomic::TParent { fqcn } => {
644 (T::TParent as u8).hash(state);
645 fqcn.hash(state);
646 }
647 Atomic::TCallable {
648 params,
649 return_type,
650 } => {
651 (T::TCallable as u8).hash(state);
652 params.hash(state);
653 return_type.hash(state);
654 }
655 Atomic::TClosure { data } => {
656 (T::TClosure as u8).hash(state);
657 data.hash(state);
658 }
659 Atomic::TArray { key, value } => {
660 (T::TArray as u8).hash(state);
661 key.hash(state);
662 value.hash(state);
663 }
664 Atomic::TList { value } => {
665 (T::TList as u8).hash(state);
666 value.hash(state);
667 }
668 Atomic::TNonEmptyArray { key, value } => {
669 (T::TNonEmptyArray as u8).hash(state);
670 key.hash(state);
671 value.hash(state);
672 }
673 Atomic::TNonEmptyList { value } => {
674 (T::TNonEmptyList as u8).hash(state);
675 value.hash(state);
676 }
677 Atomic::TKeyedArray {
678 properties,
679 is_open,
680 is_list,
681 } => {
682 (T::TKeyedArray as u8).hash(state);
683 let mut combined: u64 = 0;
687 for (k, v) in properties.iter() {
688 let mut entry_hasher = rustc_hash::FxHasher::default();
689 k.hash(&mut entry_hasher);
690 v.hash(&mut entry_hasher);
691 combined = combined.wrapping_add(std::hash::Hasher::finish(&entry_hasher));
692 }
693 combined.hash(state);
694 properties.len().hash(state);
695 is_open.hash(state);
696 is_list.hash(state);
697 }
698 Atomic::TTemplateParam {
699 name,
700 as_type,
701 defining_entity,
702 } => {
703 (T::TTemplateParam as u8).hash(state);
704 name.hash(state);
705 as_type.hash(state);
706 defining_entity.hash(state);
707 }
708 Atomic::TKeyOf { target } => {
709 (T::TKeyOf as u8).hash(state);
710 target.hash(state);
711 }
712 Atomic::TValueOf { target } => {
713 (T::TValueOf as u8).hash(state);
714 target.hash(state);
715 }
716 Atomic::TConditional { data } => {
717 (T::TConditional as u8).hash(state);
718 data.hash(state);
719 }
720 Atomic::TLiteralEnumCase {
721 enum_fqcn,
722 case_name,
723 } => {
724 (T::TLiteralEnumCase as u8).hash(state);
725 enum_fqcn.hash(state);
726 case_name.hash(state);
727 }
728 Atomic::TIntersection { parts } => {
729 (T::TIntersection as u8).hash(state);
730 parts.hash(state);
731 }
732 }
733 }
734}