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