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,
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::TInterfaceString => (T::TInterfaceString as u8).hash(state),
573 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
574 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
575
576 Atomic::TLiteralString(s) => {
578 (T::TLiteralString as u8).hash(state);
579 s.hash(state);
580 }
581 Atomic::TClassString(opt) => {
582 (T::TClassString as u8).hash(state);
583 opt.hash(state);
584 }
585 Atomic::TLiteralInt(n) => {
586 (T::TLiteralInt as u8).hash(state);
587 n.hash(state);
588 }
589 Atomic::TIntRange { min, max } => {
590 (T::TIntRange as u8).hash(state);
591 min.hash(state);
592 max.hash(state);
593 }
594 Atomic::TLiteralFloat(int_bits, frac_bits) => {
595 (T::TLiteralFloat as u8).hash(state);
596 int_bits.hash(state);
597 frac_bits.hash(state);
598 }
599 Atomic::TNamedObject { fqcn, type_params } => {
600 (T::TNamedObject as u8).hash(state);
601 fqcn.hash(state);
602 type_params.hash(state);
603 }
604 Atomic::TStaticObject { fqcn } => {
605 (T::TStaticObject as u8).hash(state);
606 fqcn.hash(state);
607 }
608 Atomic::TSelf { fqcn } => {
609 (T::TSelf as u8).hash(state);
610 fqcn.hash(state);
611 }
612 Atomic::TParent { fqcn } => {
613 (T::TParent as u8).hash(state);
614 fqcn.hash(state);
615 }
616 Atomic::TCallable {
617 params,
618 return_type,
619 } => {
620 (T::TCallable as u8).hash(state);
621 params.hash(state);
622 return_type.hash(state);
623 }
624 Atomic::TClosure {
625 params,
626 return_type,
627 this_type,
628 } => {
629 (T::TClosure as u8).hash(state);
630 params.hash(state);
631 return_type.hash(state);
632 this_type.hash(state);
633 }
634 Atomic::TArray { key, value } => {
635 (T::TArray as u8).hash(state);
636 key.hash(state);
637 value.hash(state);
638 }
639 Atomic::TList { value } => {
640 (T::TList as u8).hash(state);
641 value.hash(state);
642 }
643 Atomic::TNonEmptyArray { key, value } => {
644 (T::TNonEmptyArray as u8).hash(state);
645 key.hash(state);
646 value.hash(state);
647 }
648 Atomic::TNonEmptyList { value } => {
649 (T::TNonEmptyList as u8).hash(state);
650 value.hash(state);
651 }
652 Atomic::TKeyedArray {
653 properties,
654 is_open,
655 is_list,
656 } => {
657 (T::TKeyedArray as u8).hash(state);
658 let mut pairs: Vec<_> = properties.iter().collect();
661 pairs.sort_by_key(|(a, _)| *a);
662 for (k, v) in pairs {
663 k.hash(state);
664 v.hash(state);
665 }
666 is_open.hash(state);
667 is_list.hash(state);
668 }
669 Atomic::TTemplateParam {
670 name,
671 as_type,
672 defining_entity,
673 } => {
674 (T::TTemplateParam as u8).hash(state);
675 name.hash(state);
676 as_type.hash(state);
677 defining_entity.hash(state);
678 }
679 Atomic::TConditional {
680 param_name,
681 subject,
682 if_true,
683 if_false,
684 } => {
685 (T::TConditional as u8).hash(state);
686 param_name.hash(state);
687 subject.hash(state);
688 if_true.hash(state);
689 if_false.hash(state);
690 }
691 Atomic::TLiteralEnumCase {
692 enum_fqcn,
693 case_name,
694 } => {
695 (T::TLiteralEnumCase as u8).hash(state);
696 enum_fqcn.hash(state);
697 case_name.hash(state);
698 }
699 Atomic::TIntersection { parts } => {
700 (T::TIntersection as u8).hash(state);
701 parts.hash(state);
702 }
703 }
704 }
705}