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 pub is_pure: bool,
105 pub is_static: bool,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
111pub struct ConditionalData {
112 pub param_name: Option<Name>,
115 pub subject: Type,
116 pub if_true: Type,
117 pub if_false: Type,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125pub enum Atomic {
126 TString,
129 TLiteralString(Arc<str>),
131 TCallableString,
133 TClassString(Option<Name>),
135 TNonEmptyString,
137 TNumericString,
139
140 TInt,
142 TLiteralInt(i64),
144 TIntRange { min: Option<i64>, max: Option<i64> },
146 TPositiveInt,
148 TNegativeInt,
150 TNonNegativeInt,
152
153 TFloat,
155 TIntegralFloat,
159 TLiteralFloat(i64, i64), TBool,
164 TTrue,
166 TFalse,
168
169 TNull,
171
172 TVoid,
175 TNever,
177 TMixed,
179 TScalar,
181 TNumeric,
183
184 TObject,
187 TNamedObject {
189 fqcn: Name,
190 type_params: Arc<[Type]>,
192 },
193 TStaticObject { fqcn: Name },
195 TSelf { fqcn: Name },
197 TParent { fqcn: Name },
199
200 TCallable {
203 params: Option<Box<Vec<FnParam>>>,
207 return_type: Option<Box<Type>>,
208 is_pure: bool,
210 },
211 TClosure { data: Box<ClosureData> },
214
215 TArray { key: Box<Type>, value: Box<Type> },
218 TList { value: Box<Type> },
220 TNonEmptyArray { key: Box<Type>, value: Box<Type> },
222 TNonEmptyList { value: Box<Type> },
224 TKeyedArray {
226 properties: Box<IndexMap<ArrayKey, KeyedProperty>>,
229 is_open: bool,
231 is_list: bool,
233 },
234
235 TTemplateParam {
238 name: Name,
239 as_type: Box<Type>,
240 defining_entity: Name,
242 },
243 TKeyOf { target: Box<Type> },
245 TValueOf { target: Box<Type> },
247 TConditional { data: Box<ConditionalData> },
250
251 TInterfaceString(Option<Name>),
254 TEnumString,
256 TTraitString,
258
259 TLiteralEnumCase { enum_fqcn: Name, case_name: Name },
262
263 TIntersection { parts: Arc<[Type]> },
266}
267
268impl Atomic {
269 pub fn can_be_falsy(&self) -> bool {
271 match self {
272 Atomic::TNull
273 | Atomic::TFalse
274 | Atomic::TBool
275 | Atomic::TNever
276 | Atomic::TLiteralInt(0)
277 | Atomic::TLiteralFloat(0, 0)
278 | Atomic::TInt
279 | Atomic::TFloat
280 | Atomic::TNumeric
281 | Atomic::TScalar
282 | Atomic::TMixed
283 | Atomic::TString
284 | Atomic::TNonEmptyString | Atomic::TNumericString | Atomic::TKeyOf { .. }
287 | Atomic::TValueOf { .. }
288 | Atomic::TArray { .. }
289 | Atomic::TList { .. } => true,
290 Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
294
295 Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
296
297 Atomic::TIntRange { min, max } => {
299 min.is_none_or(|lo| lo <= 0) && max.is_none_or(|hi| hi >= 0)
300 }
301 Atomic::TNonNegativeInt => true,
303
304 _ => false,
305 }
306 }
307
308 pub fn can_be_truthy(&self) -> bool {
310 match self {
311 Atomic::TNever
312 | Atomic::TVoid
313 | Atomic::TNull
314 | Atomic::TFalse
315 | Atomic::TLiteralInt(0)
316 | Atomic::TLiteralFloat(0, 0) => false,
317 Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
318 Atomic::TIntRange {
320 min: Some(0),
321 max: Some(0),
322 } => false,
323 Atomic::TKeyedArray {
325 properties,
326 is_open,
327 ..
328 } if !is_open && properties.is_empty() => false,
329 _ => true,
330 }
331 }
332
333 pub fn is_numeric(&self) -> bool {
335 matches!(
336 self,
337 Atomic::TInt
338 | Atomic::TLiteralInt(_)
339 | Atomic::TIntRange { .. }
340 | Atomic::TPositiveInt
341 | Atomic::TNegativeInt
342 | Atomic::TNonNegativeInt
343 | Atomic::TFloat
344 | Atomic::TIntegralFloat
345 | Atomic::TLiteralFloat(..)
346 | Atomic::TNumeric
347 | Atomic::TNumericString
348 )
349 }
350
351 pub fn is_int(&self) -> bool {
353 matches!(
354 self,
355 Atomic::TInt
356 | Atomic::TLiteralInt(_)
357 | Atomic::TIntRange { .. }
358 | Atomic::TPositiveInt
359 | Atomic::TNegativeInt
360 | Atomic::TNonNegativeInt
361 )
362 }
363
364 pub fn is_string(&self) -> bool {
366 matches!(
367 self,
368 Atomic::TString
369 | Atomic::TLiteralString(_)
370 | Atomic::TCallableString
371 | Atomic::TClassString(_)
372 | Atomic::TNonEmptyString
373 | Atomic::TNumericString
374 | Atomic::TInterfaceString(_)
375 | Atomic::TEnumString
376 | Atomic::TTraitString
377 )
378 }
379
380 pub fn is_array(&self) -> bool {
382 matches!(
383 self,
384 Atomic::TArray { .. }
385 | Atomic::TList { .. }
386 | Atomic::TNonEmptyArray { .. }
387 | Atomic::TNonEmptyList { .. }
388 | Atomic::TKeyedArray { .. }
389 )
390 }
391
392 pub fn is_object(&self) -> bool {
394 matches!(
395 self,
396 Atomic::TObject
397 | Atomic::TNamedObject { .. }
398 | Atomic::TStaticObject { .. }
399 | Atomic::TSelf { .. }
400 | Atomic::TParent { .. }
401 | Atomic::TIntersection { .. }
402 )
403 }
404
405 pub fn is_definitely_non_object(&self) -> bool {
410 matches!(
411 self,
412 Atomic::TString
413 | Atomic::TLiteralString(_)
414 | Atomic::TCallableString
415 | Atomic::TClassString(_)
416 | Atomic::TNonEmptyString
417 | Atomic::TNumericString
418 | Atomic::TInterfaceString(_)
419 | Atomic::TEnumString
420 | Atomic::TTraitString
421 | Atomic::TInt
422 | Atomic::TLiteralInt(_)
423 | Atomic::TIntRange { .. }
424 | Atomic::TPositiveInt
425 | Atomic::TNegativeInt
426 | Atomic::TNonNegativeInt
427 | Atomic::TFloat
428 | Atomic::TIntegralFloat
429 | Atomic::TLiteralFloat(..)
430 | Atomic::TBool
431 | Atomic::TTrue
432 | Atomic::TFalse
433 | Atomic::TNull
434 | Atomic::TVoid
435 | Atomic::TArray { .. }
436 | Atomic::TList { .. }
437 | Atomic::TNonEmptyArray { .. }
438 | Atomic::TNonEmptyList { .. }
439 | Atomic::TKeyedArray { .. }
440 | Atomic::TScalar
441 | Atomic::TNumeric
442 )
443 }
444
445 pub fn is_callable(&self) -> bool {
450 matches!(
451 self,
452 Atomic::TCallable { .. } | Atomic::TClosure { .. } | Atomic::TCallableString
453 )
454 }
455
456 pub fn named_object_fqcn(&self) -> Option<&str> {
458 match self {
459 Atomic::TNamedObject { fqcn, .. }
460 | Atomic::TStaticObject { fqcn }
461 | Atomic::TSelf { fqcn }
462 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
463 _ => None,
464 }
465 }
466
467 pub fn type_name(&self) -> &'static str {
469 match self {
470 Atomic::TString
471 | Atomic::TLiteralString(_)
472 | Atomic::TNonEmptyString
473 | Atomic::TNumericString => "string",
474 Atomic::TCallableString => "callable-string",
475 Atomic::TClassString(_) => "class-string",
476 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
477 Atomic::TPositiveInt => "positive-int",
478 Atomic::TNegativeInt => "negative-int",
479 Atomic::TNonNegativeInt => "non-negative-int",
480 Atomic::TFloat | Atomic::TIntegralFloat | Atomic::TLiteralFloat(..) => "float",
481 Atomic::TBool => "bool",
482 Atomic::TTrue => "true",
483 Atomic::TFalse => "false",
484 Atomic::TNull => "null",
485 Atomic::TVoid => "void",
486 Atomic::TNever => "never",
487 Atomic::TMixed => "mixed",
488 Atomic::TScalar => "scalar",
489 Atomic::TNumeric => "numeric",
490 Atomic::TObject => "object",
491 Atomic::TNamedObject { .. } => "object",
492 Atomic::TStaticObject { .. } => "static",
493 Atomic::TSelf { .. } => "self",
494 Atomic::TParent { .. } => "parent",
495 Atomic::TCallable { .. } => "callable",
496 Atomic::TClosure { .. } => "Closure",
497 Atomic::TArray { .. } => "array",
498 Atomic::TList { .. } => "list",
499 Atomic::TNonEmptyArray { .. } => "non-empty-array",
500 Atomic::TNonEmptyList { .. } => "non-empty-list",
501 Atomic::TKeyedArray { .. } => "array",
502 Atomic::TTemplateParam { .. } => "template-param",
503 Atomic::TKeyOf { .. } => "key-of",
504 Atomic::TValueOf { .. } => "value-of",
505 Atomic::TConditional { .. } => "conditional-type",
506 Atomic::TInterfaceString(_) => "interface-string",
507 Atomic::TEnumString => "enum-string",
508 Atomic::TTraitString => "trait-string",
509 Atomic::TLiteralEnumCase { .. } => "enum-case",
510 Atomic::TIntersection { .. } => "intersection",
511 }
512 }
513}
514
515impl Hash for FnParam {
520 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
521 self.name.hash(state);
522 self.ty.hash(state);
523 self.out_ty.hash(state);
524 self.default.hash(state);
525 self.is_variadic.hash(state);
526 self.is_byref.hash(state);
527 self.is_optional.hash(state);
528 }
529}
530
531#[allow(non_camel_case_types, clippy::enum_variant_names)]
533#[repr(u8)]
534enum AtomicTag {
535 TString = 0,
536 TLiteralString,
537 TCallableString,
538 TClassString,
539 TNonEmptyString,
540 TNumericString,
541 TInt,
542 TLiteralInt,
543 TIntRange,
544 TPositiveInt,
545 TNegativeInt,
546 TNonNegativeInt,
547 TFloat,
548 TIntegralFloat,
549 TLiteralFloat,
550 TBool,
551 TTrue,
552 TFalse,
553 TNull,
554 TVoid,
555 TNever,
556 TMixed,
557 TScalar,
558 TNumeric,
559 TObject,
560 TNamedObject,
561 TStaticObject,
562 TSelf,
563 TParent,
564 TCallable,
565 TClosure,
566 TArray,
567 TList,
568 TNonEmptyArray,
569 TNonEmptyList,
570 TKeyedArray,
571 TTemplateParam,
572 TKeyOf,
573 TValueOf,
574 TConditional,
575 TInterfaceString,
576 TEnumString,
577 TTraitString,
578 TLiteralEnumCase,
579 TIntersection,
580}
581
582impl Hash for Atomic {
583 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
584 use AtomicTag as T;
585 match self {
586 Atomic::TString => (T::TString as u8).hash(state),
588 Atomic::TCallableString => (T::TCallableString as u8).hash(state),
589 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
590 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
591 Atomic::TInt => (T::TInt as u8).hash(state),
592 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
593 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
594 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
595 Atomic::TFloat => (T::TFloat as u8).hash(state),
596 Atomic::TIntegralFloat => (T::TIntegralFloat as u8).hash(state),
597 Atomic::TBool => (T::TBool as u8).hash(state),
598 Atomic::TTrue => (T::TTrue as u8).hash(state),
599 Atomic::TFalse => (T::TFalse as u8).hash(state),
600 Atomic::TNull => (T::TNull as u8).hash(state),
601 Atomic::TVoid => (T::TVoid as u8).hash(state),
602 Atomic::TNever => (T::TNever as u8).hash(state),
603 Atomic::TMixed => (T::TMixed as u8).hash(state),
604 Atomic::TScalar => (T::TScalar as u8).hash(state),
605 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
606 Atomic::TObject => (T::TObject as u8).hash(state),
607 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
608 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
609
610 Atomic::TLiteralString(s) => {
612 (T::TLiteralString as u8).hash(state);
613 s.hash(state);
614 }
615 Atomic::TClassString(opt) => {
616 (T::TClassString as u8).hash(state);
617 opt.hash(state);
618 }
619 Atomic::TInterfaceString(opt) => {
620 (T::TInterfaceString as u8).hash(state);
621 opt.hash(state);
622 }
623 Atomic::TLiteralInt(n) => {
624 (T::TLiteralInt as u8).hash(state);
625 n.hash(state);
626 }
627 Atomic::TIntRange { min, max } => {
628 (T::TIntRange as u8).hash(state);
629 min.hash(state);
630 max.hash(state);
631 }
632 Atomic::TLiteralFloat(int_bits, frac_bits) => {
633 (T::TLiteralFloat as u8).hash(state);
634 int_bits.hash(state);
635 frac_bits.hash(state);
636 }
637 Atomic::TNamedObject { fqcn, type_params } => {
638 (T::TNamedObject as u8).hash(state);
639 fqcn.hash(state);
640 type_params.hash(state);
641 }
642 Atomic::TStaticObject { fqcn } => {
643 (T::TStaticObject as u8).hash(state);
644 fqcn.hash(state);
645 }
646 Atomic::TSelf { fqcn } => {
647 (T::TSelf as u8).hash(state);
648 fqcn.hash(state);
649 }
650 Atomic::TParent { fqcn } => {
651 (T::TParent as u8).hash(state);
652 fqcn.hash(state);
653 }
654 Atomic::TCallable {
655 params,
656 return_type,
657 is_pure,
658 } => {
659 (T::TCallable as u8).hash(state);
660 params.hash(state);
661 return_type.hash(state);
662 is_pure.hash(state);
663 }
664 Atomic::TClosure { data } => {
665 (T::TClosure as u8).hash(state);
666 data.hash(state);
667 }
668 Atomic::TArray { key, value } => {
669 (T::TArray as u8).hash(state);
670 key.hash(state);
671 value.hash(state);
672 }
673 Atomic::TList { value } => {
674 (T::TList as u8).hash(state);
675 value.hash(state);
676 }
677 Atomic::TNonEmptyArray { key, value } => {
678 (T::TNonEmptyArray as u8).hash(state);
679 key.hash(state);
680 value.hash(state);
681 }
682 Atomic::TNonEmptyList { value } => {
683 (T::TNonEmptyList as u8).hash(state);
684 value.hash(state);
685 }
686 Atomic::TKeyedArray {
687 properties,
688 is_open,
689 is_list,
690 } => {
691 (T::TKeyedArray as u8).hash(state);
692 let mut combined: u64 = 0;
696 for (k, v) in properties.iter() {
697 let mut entry_hasher = rustc_hash::FxHasher::default();
698 k.hash(&mut entry_hasher);
699 v.hash(&mut entry_hasher);
700 combined = combined.wrapping_add(std::hash::Hasher::finish(&entry_hasher));
701 }
702 combined.hash(state);
703 properties.len().hash(state);
704 is_open.hash(state);
705 is_list.hash(state);
706 }
707 Atomic::TTemplateParam {
708 name,
709 as_type,
710 defining_entity,
711 } => {
712 (T::TTemplateParam as u8).hash(state);
713 name.hash(state);
714 as_type.hash(state);
715 defining_entity.hash(state);
716 }
717 Atomic::TKeyOf { target } => {
718 (T::TKeyOf as u8).hash(state);
719 target.hash(state);
720 }
721 Atomic::TValueOf { target } => {
722 (T::TValueOf as u8).hash(state);
723 target.hash(state);
724 }
725 Atomic::TConditional { data } => {
726 (T::TConditional as u8).hash(state);
727 data.hash(state);
728 }
729 Atomic::TLiteralEnumCase {
730 enum_fqcn,
731 case_name,
732 } => {
733 (T::TLiteralEnumCase as u8).hash(state);
734 enum_fqcn.hash(state);
735 case_name.hash(state);
736 }
737 Atomic::TIntersection { parts } => {
738 (T::TIntersection as u8).hash(state);
739 parts.hash(state);
740 }
741 }
742 }
743}