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