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 TConditional { data: Box<ConditionalData> },
239
240 TInterfaceString(Option<Name>),
243 TEnumString,
245 TTraitString,
247
248 TLiteralEnumCase { enum_fqcn: Name, case_name: Name },
251
252 TIntersection { parts: Arc<[Type]> },
255}
256
257impl Atomic {
258 pub fn can_be_falsy(&self) -> bool {
260 match self {
261 Atomic::TNull
262 | Atomic::TFalse
263 | Atomic::TBool
264 | Atomic::TNever
265 | Atomic::TLiteralInt(0)
266 | Atomic::TLiteralFloat(0, 0)
267 | Atomic::TInt
268 | Atomic::TFloat
269 | Atomic::TNumeric
270 | Atomic::TScalar
271 | Atomic::TMixed
272 | Atomic::TString
273 | Atomic::TNonEmptyString | Atomic::TNumericString | Atomic::TArray { .. }
276 | Atomic::TList { .. } => true,
277 Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
281
282 Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
283
284 Atomic::TIntRange { min, max } => {
286 min.is_none_or(|lo| lo <= 0) && max.is_none_or(|hi| hi >= 0)
287 }
288 Atomic::TNonNegativeInt => true,
290
291 _ => false,
292 }
293 }
294
295 pub fn can_be_truthy(&self) -> bool {
297 match self {
298 Atomic::TNever
299 | Atomic::TVoid
300 | Atomic::TNull
301 | Atomic::TFalse
302 | Atomic::TLiteralInt(0)
303 | Atomic::TLiteralFloat(0, 0) => false,
304 Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
305 Atomic::TIntRange {
307 min: Some(0),
308 max: Some(0),
309 } => false,
310 Atomic::TKeyedArray {
312 properties,
313 is_open,
314 ..
315 } if !is_open && properties.is_empty() => false,
316 _ => true,
317 }
318 }
319
320 pub fn is_numeric(&self) -> bool {
322 matches!(
323 self,
324 Atomic::TInt
325 | Atomic::TLiteralInt(_)
326 | Atomic::TIntRange { .. }
327 | Atomic::TPositiveInt
328 | Atomic::TNegativeInt
329 | Atomic::TNonNegativeInt
330 | Atomic::TFloat
331 | Atomic::TIntegralFloat
332 | Atomic::TLiteralFloat(..)
333 | Atomic::TNumeric
334 | Atomic::TNumericString
335 )
336 }
337
338 pub fn is_int(&self) -> bool {
340 matches!(
341 self,
342 Atomic::TInt
343 | Atomic::TLiteralInt(_)
344 | Atomic::TIntRange { .. }
345 | Atomic::TPositiveInt
346 | Atomic::TNegativeInt
347 | Atomic::TNonNegativeInt
348 )
349 }
350
351 pub fn is_string(&self) -> bool {
353 matches!(
354 self,
355 Atomic::TString
356 | Atomic::TLiteralString(_)
357 | Atomic::TCallableString
358 | Atomic::TClassString(_)
359 | Atomic::TNonEmptyString
360 | Atomic::TNumericString
361 | Atomic::TInterfaceString(_)
362 | Atomic::TEnumString
363 | Atomic::TTraitString
364 )
365 }
366
367 pub fn is_array(&self) -> bool {
369 matches!(
370 self,
371 Atomic::TArray { .. }
372 | Atomic::TList { .. }
373 | Atomic::TNonEmptyArray { .. }
374 | Atomic::TNonEmptyList { .. }
375 | Atomic::TKeyedArray { .. }
376 )
377 }
378
379 pub fn is_object(&self) -> bool {
381 matches!(
382 self,
383 Atomic::TObject
384 | Atomic::TNamedObject { .. }
385 | Atomic::TStaticObject { .. }
386 | Atomic::TSelf { .. }
387 | Atomic::TParent { .. }
388 | Atomic::TIntersection { .. }
389 )
390 }
391
392 pub fn is_definitely_non_object(&self) -> bool {
397 matches!(
398 self,
399 Atomic::TString
400 | Atomic::TLiteralString(_)
401 | Atomic::TCallableString
402 | Atomic::TClassString(_)
403 | Atomic::TNonEmptyString
404 | Atomic::TNumericString
405 | Atomic::TInterfaceString(_)
406 | Atomic::TEnumString
407 | Atomic::TTraitString
408 | Atomic::TInt
409 | Atomic::TLiteralInt(_)
410 | Atomic::TIntRange { .. }
411 | Atomic::TPositiveInt
412 | Atomic::TNegativeInt
413 | Atomic::TNonNegativeInt
414 | Atomic::TFloat
415 | Atomic::TIntegralFloat
416 | Atomic::TLiteralFloat(..)
417 | Atomic::TBool
418 | Atomic::TTrue
419 | Atomic::TFalse
420 | Atomic::TNull
421 | Atomic::TVoid
422 | Atomic::TArray { .. }
423 | Atomic::TList { .. }
424 | Atomic::TNonEmptyArray { .. }
425 | Atomic::TNonEmptyList { .. }
426 | Atomic::TKeyedArray { .. }
427 | Atomic::TScalar
428 | Atomic::TNumeric
429 )
430 }
431
432 pub fn is_callable(&self) -> bool {
437 matches!(
438 self,
439 Atomic::TCallable { .. } | Atomic::TClosure { .. } | Atomic::TCallableString
440 )
441 }
442
443 pub fn named_object_fqcn(&self) -> Option<&str> {
445 match self {
446 Atomic::TNamedObject { fqcn, .. }
447 | Atomic::TStaticObject { fqcn }
448 | Atomic::TSelf { fqcn }
449 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
450 _ => None,
451 }
452 }
453
454 pub fn type_name(&self) -> &'static str {
456 match self {
457 Atomic::TString
458 | Atomic::TLiteralString(_)
459 | Atomic::TNonEmptyString
460 | Atomic::TNumericString => "string",
461 Atomic::TCallableString => "callable-string",
462 Atomic::TClassString(_) => "class-string",
463 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
464 Atomic::TPositiveInt => "positive-int",
465 Atomic::TNegativeInt => "negative-int",
466 Atomic::TNonNegativeInt => "non-negative-int",
467 Atomic::TFloat | Atomic::TIntegralFloat | Atomic::TLiteralFloat(..) => "float",
468 Atomic::TBool => "bool",
469 Atomic::TTrue => "true",
470 Atomic::TFalse => "false",
471 Atomic::TNull => "null",
472 Atomic::TVoid => "void",
473 Atomic::TNever => "never",
474 Atomic::TMixed => "mixed",
475 Atomic::TScalar => "scalar",
476 Atomic::TNumeric => "numeric",
477 Atomic::TObject => "object",
478 Atomic::TNamedObject { .. } => "object",
479 Atomic::TStaticObject { .. } => "static",
480 Atomic::TSelf { .. } => "self",
481 Atomic::TParent { .. } => "parent",
482 Atomic::TCallable { .. } => "callable",
483 Atomic::TClosure { .. } => "Closure",
484 Atomic::TArray { .. } => "array",
485 Atomic::TList { .. } => "list",
486 Atomic::TNonEmptyArray { .. } => "non-empty-array",
487 Atomic::TNonEmptyList { .. } => "non-empty-list",
488 Atomic::TKeyedArray { .. } => "array",
489 Atomic::TTemplateParam { .. } => "template-param",
490 Atomic::TConditional { .. } => "conditional-type",
491 Atomic::TInterfaceString(_) => "interface-string",
492 Atomic::TEnumString => "enum-string",
493 Atomic::TTraitString => "trait-string",
494 Atomic::TLiteralEnumCase { .. } => "enum-case",
495 Atomic::TIntersection { .. } => "intersection",
496 }
497 }
498}
499
500impl Hash for FnParam {
505 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
506 self.name.hash(state);
507 self.ty.hash(state);
508 self.out_ty.hash(state);
509 self.default.hash(state);
510 self.is_variadic.hash(state);
511 self.is_byref.hash(state);
512 self.is_optional.hash(state);
513 }
514}
515
516#[allow(non_camel_case_types, clippy::enum_variant_names)]
518#[repr(u8)]
519enum AtomicTag {
520 TString = 0,
521 TLiteralString,
522 TCallableString,
523 TClassString,
524 TNonEmptyString,
525 TNumericString,
526 TInt,
527 TLiteralInt,
528 TIntRange,
529 TPositiveInt,
530 TNegativeInt,
531 TNonNegativeInt,
532 TFloat,
533 TIntegralFloat,
534 TLiteralFloat,
535 TBool,
536 TTrue,
537 TFalse,
538 TNull,
539 TVoid,
540 TNever,
541 TMixed,
542 TScalar,
543 TNumeric,
544 TObject,
545 TNamedObject,
546 TStaticObject,
547 TSelf,
548 TParent,
549 TCallable,
550 TClosure,
551 TArray,
552 TList,
553 TNonEmptyArray,
554 TNonEmptyList,
555 TKeyedArray,
556 TTemplateParam,
557 TConditional,
558 TInterfaceString,
559 TEnumString,
560 TTraitString,
561 TLiteralEnumCase,
562 TIntersection,
563}
564
565impl Hash for Atomic {
566 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
567 use AtomicTag as T;
568 match self {
569 Atomic::TString => (T::TString as u8).hash(state),
571 Atomic::TCallableString => (T::TCallableString as u8).hash(state),
572 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
573 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
574 Atomic::TInt => (T::TInt as u8).hash(state),
575 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
576 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
577 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
578 Atomic::TFloat => (T::TFloat as u8).hash(state),
579 Atomic::TIntegralFloat => (T::TIntegralFloat as u8).hash(state),
580 Atomic::TBool => (T::TBool as u8).hash(state),
581 Atomic::TTrue => (T::TTrue as u8).hash(state),
582 Atomic::TFalse => (T::TFalse as u8).hash(state),
583 Atomic::TNull => (T::TNull as u8).hash(state),
584 Atomic::TVoid => (T::TVoid as u8).hash(state),
585 Atomic::TNever => (T::TNever as u8).hash(state),
586 Atomic::TMixed => (T::TMixed as u8).hash(state),
587 Atomic::TScalar => (T::TScalar as u8).hash(state),
588 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
589 Atomic::TObject => (T::TObject as u8).hash(state),
590 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
591 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
592
593 Atomic::TLiteralString(s) => {
595 (T::TLiteralString as u8).hash(state);
596 s.hash(state);
597 }
598 Atomic::TClassString(opt) => {
599 (T::TClassString as u8).hash(state);
600 opt.hash(state);
601 }
602 Atomic::TInterfaceString(opt) => {
603 (T::TInterfaceString as u8).hash(state);
604 opt.hash(state);
605 }
606 Atomic::TLiteralInt(n) => {
607 (T::TLiteralInt as u8).hash(state);
608 n.hash(state);
609 }
610 Atomic::TIntRange { min, max } => {
611 (T::TIntRange as u8).hash(state);
612 min.hash(state);
613 max.hash(state);
614 }
615 Atomic::TLiteralFloat(int_bits, frac_bits) => {
616 (T::TLiteralFloat as u8).hash(state);
617 int_bits.hash(state);
618 frac_bits.hash(state);
619 }
620 Atomic::TNamedObject { fqcn, type_params } => {
621 (T::TNamedObject as u8).hash(state);
622 fqcn.hash(state);
623 type_params.hash(state);
624 }
625 Atomic::TStaticObject { fqcn } => {
626 (T::TStaticObject as u8).hash(state);
627 fqcn.hash(state);
628 }
629 Atomic::TSelf { fqcn } => {
630 (T::TSelf as u8).hash(state);
631 fqcn.hash(state);
632 }
633 Atomic::TParent { fqcn } => {
634 (T::TParent as u8).hash(state);
635 fqcn.hash(state);
636 }
637 Atomic::TCallable {
638 params,
639 return_type,
640 } => {
641 (T::TCallable as u8).hash(state);
642 params.hash(state);
643 return_type.hash(state);
644 }
645 Atomic::TClosure { data } => {
646 (T::TClosure as u8).hash(state);
647 data.hash(state);
648 }
649 Atomic::TArray { key, value } => {
650 (T::TArray as u8).hash(state);
651 key.hash(state);
652 value.hash(state);
653 }
654 Atomic::TList { value } => {
655 (T::TList as u8).hash(state);
656 value.hash(state);
657 }
658 Atomic::TNonEmptyArray { key, value } => {
659 (T::TNonEmptyArray as u8).hash(state);
660 key.hash(state);
661 value.hash(state);
662 }
663 Atomic::TNonEmptyList { value } => {
664 (T::TNonEmptyList as u8).hash(state);
665 value.hash(state);
666 }
667 Atomic::TKeyedArray {
668 properties,
669 is_open,
670 is_list,
671 } => {
672 (T::TKeyedArray as u8).hash(state);
673 let mut combined: u64 = 0;
677 for (k, v) in properties.iter() {
678 let mut entry_hasher = rustc_hash::FxHasher::default();
679 k.hash(&mut entry_hasher);
680 v.hash(&mut entry_hasher);
681 combined = combined.wrapping_add(std::hash::Hasher::finish(&entry_hasher));
682 }
683 combined.hash(state);
684 properties.len().hash(state);
685 is_open.hash(state);
686 is_list.hash(state);
687 }
688 Atomic::TTemplateParam {
689 name,
690 as_type,
691 defining_entity,
692 } => {
693 (T::TTemplateParam as u8).hash(state);
694 name.hash(state);
695 as_type.hash(state);
696 defining_entity.hash(state);
697 }
698 Atomic::TConditional { data } => {
699 (T::TConditional as u8).hash(state);
700 data.hash(state);
701 }
702 Atomic::TLiteralEnumCase {
703 enum_fqcn,
704 case_name,
705 } => {
706 (T::TLiteralEnumCase as u8).hash(state);
707 enum_fqcn.hash(state);
708 case_name.hash(state);
709 }
710 Atomic::TIntersection { parts } => {
711 (T::TIntersection as u8).hash(state);
712 parts.hash(state);
713 }
714 }
715 }
716}