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, Serialize, Deserialize)]
98pub enum Atomic {
99 TString,
102 TLiteralString(Arc<str>),
104 TCallableString,
106 TClassString(Option<Name>),
108 TNonEmptyString,
110 TNumericString,
112
113 TInt,
115 TLiteralInt(i64),
117 TIntRange { min: Option<i64>, max: Option<i64> },
119 TPositiveInt,
121 TNegativeInt,
123 TNonNegativeInt,
125
126 TFloat,
128 TIntegralFloat,
132 TLiteralFloat(i64, i64), TBool,
137 TTrue,
139 TFalse,
141
142 TNull,
144
145 TVoid,
148 TNever,
150 TMixed,
152 TScalar,
154 TNumeric,
156
157 TObject,
160 TNamedObject {
162 fqcn: Name,
163 type_params: Arc<[Type]>,
165 },
166 TStaticObject { fqcn: Name },
168 TSelf { fqcn: Name },
170 TParent { fqcn: Name },
172
173 TCallable {
176 params: Option<Vec<FnParam>>,
177 return_type: Option<Box<Type>>,
178 },
179 TClosure {
181 params: Vec<FnParam>,
182 return_type: Box<Type>,
183 this_type: Option<Box<Type>>,
184 },
185
186 TArray { key: Box<Type>, value: Box<Type> },
189 TList { value: Box<Type> },
191 TNonEmptyArray { key: Box<Type>, value: Box<Type> },
193 TNonEmptyList { value: Box<Type> },
195 TKeyedArray {
197 properties: IndexMap<ArrayKey, KeyedProperty>,
198 is_open: bool,
200 is_list: bool,
202 },
203
204 TTemplateParam {
207 name: Name,
208 as_type: Box<Type>,
209 defining_entity: Name,
211 },
212 TConditional {
214 param_name: Option<Name>,
217 subject: Box<Type>,
218 if_true: Box<Type>,
219 if_false: Box<Type>,
220 },
221
222 TInterfaceString(Option<Name>),
225 TEnumString,
227 TTraitString,
229
230 TLiteralEnumCase { enum_fqcn: Name, case_name: Name },
233
234 TIntersection { parts: Arc<[Type]> },
237}
238
239impl Atomic {
240 pub fn can_be_falsy(&self) -> bool {
242 match self {
243 Atomic::TNull
244 | Atomic::TFalse
245 | Atomic::TBool
246 | Atomic::TNever
247 | Atomic::TLiteralInt(0)
248 | Atomic::TLiteralFloat(0, 0)
249 | Atomic::TInt
250 | Atomic::TFloat
251 | Atomic::TNumeric
252 | Atomic::TScalar
253 | Atomic::TMixed
254 | Atomic::TString
255 | Atomic::TNonEmptyString | Atomic::TNumericString | Atomic::TArray { .. }
258 | Atomic::TList { .. } => true,
259 Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
263
264 Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
265
266 Atomic::TIntRange { min, max } => {
268 min.is_none_or(|lo| lo <= 0) && max.is_none_or(|hi| hi >= 0)
269 }
270 Atomic::TNonNegativeInt => true,
272
273 _ => false,
274 }
275 }
276
277 pub fn can_be_truthy(&self) -> bool {
279 match self {
280 Atomic::TNever
281 | Atomic::TVoid
282 | Atomic::TNull
283 | Atomic::TFalse
284 | Atomic::TLiteralInt(0)
285 | Atomic::TLiteralFloat(0, 0) => false,
286 Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
287 Atomic::TIntRange {
289 min: Some(0),
290 max: Some(0),
291 } => false,
292 Atomic::TKeyedArray {
294 properties,
295 is_open,
296 ..
297 } if !is_open && properties.is_empty() => false,
298 _ => true,
299 }
300 }
301
302 pub fn is_numeric(&self) -> bool {
304 matches!(
305 self,
306 Atomic::TInt
307 | Atomic::TLiteralInt(_)
308 | Atomic::TIntRange { .. }
309 | Atomic::TPositiveInt
310 | Atomic::TNegativeInt
311 | Atomic::TNonNegativeInt
312 | Atomic::TFloat
313 | Atomic::TIntegralFloat
314 | Atomic::TLiteralFloat(..)
315 | Atomic::TNumeric
316 | Atomic::TNumericString
317 )
318 }
319
320 pub fn is_int(&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 )
331 }
332
333 pub fn is_string(&self) -> bool {
335 matches!(
336 self,
337 Atomic::TString
338 | Atomic::TLiteralString(_)
339 | Atomic::TCallableString
340 | Atomic::TClassString(_)
341 | Atomic::TNonEmptyString
342 | Atomic::TNumericString
343 | Atomic::TInterfaceString(_)
344 | Atomic::TEnumString
345 | Atomic::TTraitString
346 )
347 }
348
349 pub fn is_array(&self) -> bool {
351 matches!(
352 self,
353 Atomic::TArray { .. }
354 | Atomic::TList { .. }
355 | Atomic::TNonEmptyArray { .. }
356 | Atomic::TNonEmptyList { .. }
357 | Atomic::TKeyedArray { .. }
358 )
359 }
360
361 pub fn is_object(&self) -> bool {
363 matches!(
364 self,
365 Atomic::TObject
366 | Atomic::TNamedObject { .. }
367 | Atomic::TStaticObject { .. }
368 | Atomic::TSelf { .. }
369 | Atomic::TParent { .. }
370 | Atomic::TIntersection { .. }
371 )
372 }
373
374 pub fn is_definitely_non_object(&self) -> bool {
379 matches!(
380 self,
381 Atomic::TString
382 | Atomic::TLiteralString(_)
383 | Atomic::TCallableString
384 | Atomic::TClassString(_)
385 | Atomic::TNonEmptyString
386 | Atomic::TNumericString
387 | Atomic::TInterfaceString(_)
388 | Atomic::TEnumString
389 | Atomic::TTraitString
390 | Atomic::TInt
391 | Atomic::TLiteralInt(_)
392 | Atomic::TIntRange { .. }
393 | Atomic::TPositiveInt
394 | Atomic::TNegativeInt
395 | Atomic::TNonNegativeInt
396 | Atomic::TFloat
397 | Atomic::TIntegralFloat
398 | Atomic::TLiteralFloat(..)
399 | Atomic::TBool
400 | Atomic::TTrue
401 | Atomic::TFalse
402 | Atomic::TNull
403 | Atomic::TVoid
404 | Atomic::TArray { .. }
405 | Atomic::TList { .. }
406 | Atomic::TNonEmptyArray { .. }
407 | Atomic::TNonEmptyList { .. }
408 | Atomic::TKeyedArray { .. }
409 | Atomic::TScalar
410 | Atomic::TNumeric
411 )
412 }
413
414 pub fn is_callable(&self) -> bool {
419 matches!(
420 self,
421 Atomic::TCallable { .. } | Atomic::TClosure { .. } | Atomic::TCallableString
422 )
423 }
424
425 pub fn named_object_fqcn(&self) -> Option<&str> {
427 match self {
428 Atomic::TNamedObject { fqcn, .. }
429 | Atomic::TStaticObject { fqcn }
430 | Atomic::TSelf { fqcn }
431 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
432 _ => None,
433 }
434 }
435
436 pub fn type_name(&self) -> &'static str {
438 match self {
439 Atomic::TString
440 | Atomic::TLiteralString(_)
441 | Atomic::TNonEmptyString
442 | Atomic::TNumericString => "string",
443 Atomic::TCallableString => "callable-string",
444 Atomic::TClassString(_) => "class-string",
445 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
446 Atomic::TPositiveInt => "positive-int",
447 Atomic::TNegativeInt => "negative-int",
448 Atomic::TNonNegativeInt => "non-negative-int",
449 Atomic::TFloat | Atomic::TIntegralFloat | Atomic::TLiteralFloat(..) => "float",
450 Atomic::TBool => "bool",
451 Atomic::TTrue => "true",
452 Atomic::TFalse => "false",
453 Atomic::TNull => "null",
454 Atomic::TVoid => "void",
455 Atomic::TNever => "never",
456 Atomic::TMixed => "mixed",
457 Atomic::TScalar => "scalar",
458 Atomic::TNumeric => "numeric",
459 Atomic::TObject => "object",
460 Atomic::TNamedObject { .. } => "object",
461 Atomic::TStaticObject { .. } => "static",
462 Atomic::TSelf { .. } => "self",
463 Atomic::TParent { .. } => "parent",
464 Atomic::TCallable { .. } => "callable",
465 Atomic::TClosure { .. } => "Closure",
466 Atomic::TArray { .. } => "array",
467 Atomic::TList { .. } => "list",
468 Atomic::TNonEmptyArray { .. } => "non-empty-array",
469 Atomic::TNonEmptyList { .. } => "non-empty-list",
470 Atomic::TKeyedArray { .. } => "array",
471 Atomic::TTemplateParam { .. } => "template-param",
472 Atomic::TConditional { .. } => "conditional-type",
473 Atomic::TInterfaceString(_) => "interface-string",
474 Atomic::TEnumString => "enum-string",
475 Atomic::TTraitString => "trait-string",
476 Atomic::TLiteralEnumCase { .. } => "enum-case",
477 Atomic::TIntersection { .. } => "intersection",
478 }
479 }
480}
481
482impl Hash for FnParam {
487 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
488 self.name.hash(state);
489 self.ty.hash(state);
490 self.out_ty.hash(state);
491 self.default.hash(state);
492 self.is_variadic.hash(state);
493 self.is_byref.hash(state);
494 self.is_optional.hash(state);
495 }
496}
497
498#[allow(non_camel_case_types, clippy::enum_variant_names)]
500#[repr(u8)]
501enum AtomicTag {
502 TString = 0,
503 TLiteralString,
504 TCallableString,
505 TClassString,
506 TNonEmptyString,
507 TNumericString,
508 TInt,
509 TLiteralInt,
510 TIntRange,
511 TPositiveInt,
512 TNegativeInt,
513 TNonNegativeInt,
514 TFloat,
515 TIntegralFloat,
516 TLiteralFloat,
517 TBool,
518 TTrue,
519 TFalse,
520 TNull,
521 TVoid,
522 TNever,
523 TMixed,
524 TScalar,
525 TNumeric,
526 TObject,
527 TNamedObject,
528 TStaticObject,
529 TSelf,
530 TParent,
531 TCallable,
532 TClosure,
533 TArray,
534 TList,
535 TNonEmptyArray,
536 TNonEmptyList,
537 TKeyedArray,
538 TTemplateParam,
539 TConditional,
540 TInterfaceString,
541 TEnumString,
542 TTraitString,
543 TLiteralEnumCase,
544 TIntersection,
545}
546
547impl Hash for Atomic {
548 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
549 use AtomicTag as T;
550 match self {
551 Atomic::TString => (T::TString as u8).hash(state),
553 Atomic::TCallableString => (T::TCallableString as u8).hash(state),
554 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
555 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
556 Atomic::TInt => (T::TInt as u8).hash(state),
557 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
558 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
559 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
560 Atomic::TFloat => (T::TFloat as u8).hash(state),
561 Atomic::TIntegralFloat => (T::TIntegralFloat as u8).hash(state),
562 Atomic::TBool => (T::TBool as u8).hash(state),
563 Atomic::TTrue => (T::TTrue as u8).hash(state),
564 Atomic::TFalse => (T::TFalse as u8).hash(state),
565 Atomic::TNull => (T::TNull as u8).hash(state),
566 Atomic::TVoid => (T::TVoid as u8).hash(state),
567 Atomic::TNever => (T::TNever as u8).hash(state),
568 Atomic::TMixed => (T::TMixed as u8).hash(state),
569 Atomic::TScalar => (T::TScalar as u8).hash(state),
570 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
571 Atomic::TObject => (T::TObject as u8).hash(state),
572 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
573 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
574
575 Atomic::TLiteralString(s) => {
577 (T::TLiteralString as u8).hash(state);
578 s.hash(state);
579 }
580 Atomic::TClassString(opt) => {
581 (T::TClassString as u8).hash(state);
582 opt.hash(state);
583 }
584 Atomic::TInterfaceString(opt) => {
585 (T::TInterfaceString as u8).hash(state);
586 opt.hash(state);
587 }
588 Atomic::TLiteralInt(n) => {
589 (T::TLiteralInt as u8).hash(state);
590 n.hash(state);
591 }
592 Atomic::TIntRange { min, max } => {
593 (T::TIntRange as u8).hash(state);
594 min.hash(state);
595 max.hash(state);
596 }
597 Atomic::TLiteralFloat(int_bits, frac_bits) => {
598 (T::TLiteralFloat as u8).hash(state);
599 int_bits.hash(state);
600 frac_bits.hash(state);
601 }
602 Atomic::TNamedObject { fqcn, type_params } => {
603 (T::TNamedObject as u8).hash(state);
604 fqcn.hash(state);
605 type_params.hash(state);
606 }
607 Atomic::TStaticObject { fqcn } => {
608 (T::TStaticObject as u8).hash(state);
609 fqcn.hash(state);
610 }
611 Atomic::TSelf { fqcn } => {
612 (T::TSelf as u8).hash(state);
613 fqcn.hash(state);
614 }
615 Atomic::TParent { fqcn } => {
616 (T::TParent as u8).hash(state);
617 fqcn.hash(state);
618 }
619 Atomic::TCallable {
620 params,
621 return_type,
622 } => {
623 (T::TCallable as u8).hash(state);
624 params.hash(state);
625 return_type.hash(state);
626 }
627 Atomic::TClosure {
628 params,
629 return_type,
630 this_type,
631 } => {
632 (T::TClosure as u8).hash(state);
633 params.hash(state);
634 return_type.hash(state);
635 this_type.hash(state);
636 }
637 Atomic::TArray { key, value } => {
638 (T::TArray as u8).hash(state);
639 key.hash(state);
640 value.hash(state);
641 }
642 Atomic::TList { value } => {
643 (T::TList as u8).hash(state);
644 value.hash(state);
645 }
646 Atomic::TNonEmptyArray { key, value } => {
647 (T::TNonEmptyArray as u8).hash(state);
648 key.hash(state);
649 value.hash(state);
650 }
651 Atomic::TNonEmptyList { value } => {
652 (T::TNonEmptyList as u8).hash(state);
653 value.hash(state);
654 }
655 Atomic::TKeyedArray {
656 properties,
657 is_open,
658 is_list,
659 } => {
660 (T::TKeyedArray as u8).hash(state);
661 let mut pairs: Vec<_> = properties.iter().collect();
664 pairs.sort_by_key(|(a, _)| *a);
665 for (k, v) in pairs {
666 k.hash(state);
667 v.hash(state);
668 }
669 is_open.hash(state);
670 is_list.hash(state);
671 }
672 Atomic::TTemplateParam {
673 name,
674 as_type,
675 defining_entity,
676 } => {
677 (T::TTemplateParam as u8).hash(state);
678 name.hash(state);
679 as_type.hash(state);
680 defining_entity.hash(state);
681 }
682 Atomic::TConditional {
683 param_name,
684 subject,
685 if_true,
686 if_false,
687 } => {
688 (T::TConditional as u8).hash(state);
689 param_name.hash(state);
690 subject.hash(state);
691 if_true.hash(state);
692 if_false.hash(state);
693 }
694 Atomic::TLiteralEnumCase {
695 enum_fqcn,
696 case_name,
697 } => {
698 (T::TLiteralEnumCase as u8).hash(state);
699 enum_fqcn.hash(state);
700 case_name.hash(state);
701 }
702 Atomic::TIntersection { parts } => {
703 (T::TIntersection as u8).hash(state);
704 parts.hash(state);
705 }
706 }
707 }
708}