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 pub default: Option<crate::compact::SimpleType>,
22 pub is_variadic: bool,
23 pub is_byref: bool,
24 pub is_optional: bool,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
32pub enum Variance {
33 #[default]
35 Invariant,
36 Covariant,
38 Contravariant,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
47pub struct TemplateParam {
48 pub name: Name,
49 pub bound: Option<Type>,
50 pub variance: Variance,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
58pub enum ArrayKey {
59 String(Arc<str>),
60 Int(i64),
61}
62
63impl PartialOrd for ArrayKey {
64 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
65 Some(self.cmp(other))
66 }
67}
68
69impl Ord for ArrayKey {
70 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
71 match (self, other) {
72 (ArrayKey::Int(a), ArrayKey::Int(b)) => a.cmp(b),
73 (ArrayKey::String(a), ArrayKey::String(b)) => a.cmp(b),
74 (ArrayKey::Int(_), ArrayKey::String(_)) => std::cmp::Ordering::Less,
76 (ArrayKey::String(_), ArrayKey::Int(_)) => std::cmp::Ordering::Greater,
77 }
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
82pub struct KeyedProperty {
83 pub ty: Type,
84 pub optional: bool,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
92pub enum Atomic {
93 TString,
96 TLiteralString(Arc<str>),
98 TCallableString,
100 TClassString(Option<Name>),
102 TNonEmptyString,
104 TNumericString,
106
107 TInt,
109 TLiteralInt(i64),
111 TIntRange { min: Option<i64>, max: Option<i64> },
113 TPositiveInt,
115 TNegativeInt,
117 TNonNegativeInt,
119
120 TFloat,
122 TLiteralFloat(i64, i64), TBool,
127 TTrue,
129 TFalse,
131
132 TNull,
134
135 TVoid,
138 TNever,
140 TMixed,
142 TScalar,
144 TNumeric,
146
147 TObject,
150 TNamedObject {
152 fqcn: Name,
153 type_params: Arc<[Type]>,
155 },
156 TStaticObject { fqcn: Name },
158 TSelf { fqcn: Name },
160 TParent { fqcn: Name },
162
163 TCallable {
166 params: Option<Vec<FnParam>>,
167 return_type: Option<Box<Type>>,
168 },
169 TClosure {
171 params: Vec<FnParam>,
172 return_type: Box<Type>,
173 this_type: Option<Box<Type>>,
174 },
175
176 TArray { key: Box<Type>, value: Box<Type> },
179 TList { value: Box<Type> },
181 TNonEmptyArray { key: Box<Type>, value: Box<Type> },
183 TNonEmptyList { value: Box<Type> },
185 TKeyedArray {
187 properties: IndexMap<ArrayKey, KeyedProperty>,
188 is_open: bool,
190 is_list: bool,
192 },
193
194 TTemplateParam {
197 name: Name,
198 as_type: Box<Type>,
199 defining_entity: Name,
201 },
202 TConditional {
204 param_name: Option<Name>,
207 subject: Box<Type>,
208 if_true: Box<Type>,
209 if_false: Box<Type>,
210 },
211
212 TInterfaceString,
215 TEnumString,
217 TTraitString,
219
220 TLiteralEnumCase { enum_fqcn: Name, case_name: Name },
223
224 TIntersection { parts: Arc<[Type]> },
227}
228
229impl Atomic {
230 pub fn can_be_falsy(&self) -> bool {
232 match self {
233 Atomic::TNull
234 | Atomic::TFalse
235 | Atomic::TBool
236 | Atomic::TNever
237 | Atomic::TLiteralInt(0)
238 | Atomic::TLiteralFloat(0, 0)
239 | Atomic::TInt
240 | Atomic::TFloat
241 | Atomic::TNumeric
242 | Atomic::TScalar
243 | Atomic::TMixed
244 | Atomic::TString
245 | Atomic::TNonEmptyString | Atomic::TNumericString | Atomic::TArray { .. }
248 | Atomic::TList { .. } => true,
249 Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
253
254 Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
255
256 Atomic::TIntRange { min, max } => {
258 min.is_none_or(|lo| lo <= 0) && max.is_none_or(|hi| hi >= 0)
259 }
260 Atomic::TNonNegativeInt => true,
262
263 _ => false,
264 }
265 }
266
267 pub fn can_be_truthy(&self) -> bool {
269 match self {
270 Atomic::TNever
271 | Atomic::TVoid
272 | Atomic::TNull
273 | Atomic::TFalse
274 | Atomic::TLiteralInt(0)
275 | Atomic::TLiteralFloat(0, 0) => false,
276 Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
277 Atomic::TIntRange {
279 min: Some(0),
280 max: Some(0),
281 } => false,
282 Atomic::TKeyedArray {
284 properties,
285 is_open,
286 ..
287 } if !is_open && properties.is_empty() => false,
288 _ => true,
289 }
290 }
291
292 pub fn is_numeric(&self) -> bool {
294 matches!(
295 self,
296 Atomic::TInt
297 | Atomic::TLiteralInt(_)
298 | Atomic::TIntRange { .. }
299 | Atomic::TPositiveInt
300 | Atomic::TNegativeInt
301 | Atomic::TNonNegativeInt
302 | Atomic::TFloat
303 | Atomic::TLiteralFloat(..)
304 | Atomic::TNumeric
305 | Atomic::TNumericString
306 )
307 }
308
309 pub fn is_int(&self) -> bool {
311 matches!(
312 self,
313 Atomic::TInt
314 | Atomic::TLiteralInt(_)
315 | Atomic::TIntRange { .. }
316 | Atomic::TPositiveInt
317 | Atomic::TNegativeInt
318 | Atomic::TNonNegativeInt
319 )
320 }
321
322 pub fn is_string(&self) -> bool {
324 matches!(
325 self,
326 Atomic::TString
327 | Atomic::TLiteralString(_)
328 | Atomic::TCallableString
329 | Atomic::TClassString(_)
330 | Atomic::TNonEmptyString
331 | Atomic::TNumericString
332 | Atomic::TInterfaceString
333 | Atomic::TEnumString
334 | Atomic::TTraitString
335 )
336 }
337
338 pub fn is_array(&self) -> bool {
340 matches!(
341 self,
342 Atomic::TArray { .. }
343 | Atomic::TList { .. }
344 | Atomic::TNonEmptyArray { .. }
345 | Atomic::TNonEmptyList { .. }
346 | Atomic::TKeyedArray { .. }
347 )
348 }
349
350 pub fn is_object(&self) -> bool {
352 matches!(
353 self,
354 Atomic::TObject
355 | Atomic::TNamedObject { .. }
356 | Atomic::TStaticObject { .. }
357 | Atomic::TSelf { .. }
358 | Atomic::TParent { .. }
359 | Atomic::TIntersection { .. }
360 )
361 }
362
363 pub fn is_definitely_non_object(&self) -> bool {
368 matches!(
369 self,
370 Atomic::TString
371 | Atomic::TLiteralString(_)
372 | Atomic::TCallableString
373 | Atomic::TClassString(_)
374 | Atomic::TNonEmptyString
375 | Atomic::TNumericString
376 | Atomic::TInterfaceString
377 | Atomic::TEnumString
378 | Atomic::TTraitString
379 | Atomic::TInt
380 | Atomic::TLiteralInt(_)
381 | Atomic::TIntRange { .. }
382 | Atomic::TPositiveInt
383 | Atomic::TNegativeInt
384 | Atomic::TNonNegativeInt
385 | Atomic::TFloat
386 | Atomic::TLiteralFloat(..)
387 | Atomic::TBool
388 | Atomic::TTrue
389 | Atomic::TFalse
390 | Atomic::TNull
391 | Atomic::TVoid
392 | Atomic::TArray { .. }
393 | Atomic::TList { .. }
394 | Atomic::TNonEmptyArray { .. }
395 | Atomic::TNonEmptyList { .. }
396 | Atomic::TKeyedArray { .. }
397 | Atomic::TScalar
398 | Atomic::TNumeric
399 )
400 }
401
402 pub fn is_callable(&self) -> bool {
404 matches!(self, Atomic::TCallable { .. } | Atomic::TClosure { .. })
405 }
406
407 pub fn named_object_fqcn(&self) -> Option<&str> {
409 match self {
410 Atomic::TNamedObject { fqcn, .. }
411 | Atomic::TStaticObject { fqcn }
412 | Atomic::TSelf { fqcn }
413 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
414 _ => None,
415 }
416 }
417
418 pub fn type_name(&self) -> &'static str {
420 match self {
421 Atomic::TString
422 | Atomic::TLiteralString(_)
423 | Atomic::TNonEmptyString
424 | Atomic::TNumericString => "string",
425 Atomic::TCallableString => "callable-string",
426 Atomic::TClassString(_) => "class-string",
427 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
428 Atomic::TPositiveInt => "positive-int",
429 Atomic::TNegativeInt => "negative-int",
430 Atomic::TNonNegativeInt => "non-negative-int",
431 Atomic::TFloat | Atomic::TLiteralFloat(..) => "float",
432 Atomic::TBool => "bool",
433 Atomic::TTrue => "true",
434 Atomic::TFalse => "false",
435 Atomic::TNull => "null",
436 Atomic::TVoid => "void",
437 Atomic::TNever => "never",
438 Atomic::TMixed => "mixed",
439 Atomic::TScalar => "scalar",
440 Atomic::TNumeric => "numeric",
441 Atomic::TObject => "object",
442 Atomic::TNamedObject { .. } => "object",
443 Atomic::TStaticObject { .. } => "static",
444 Atomic::TSelf { .. } => "self",
445 Atomic::TParent { .. } => "parent",
446 Atomic::TCallable { .. } => "callable",
447 Atomic::TClosure { .. } => "Closure",
448 Atomic::TArray { .. } => "array",
449 Atomic::TList { .. } => "list",
450 Atomic::TNonEmptyArray { .. } => "non-empty-array",
451 Atomic::TNonEmptyList { .. } => "non-empty-list",
452 Atomic::TKeyedArray { .. } => "array",
453 Atomic::TTemplateParam { .. } => "template-param",
454 Atomic::TConditional { .. } => "conditional-type",
455 Atomic::TInterfaceString => "interface-string",
456 Atomic::TEnumString => "enum-string",
457 Atomic::TTraitString => "trait-string",
458 Atomic::TLiteralEnumCase { .. } => "enum-case",
459 Atomic::TIntersection { .. } => "intersection",
460 }
461 }
462}
463
464impl Hash for FnParam {
469 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
470 self.name.hash(state);
471 self.ty.hash(state);
472 self.default.hash(state);
473 self.is_variadic.hash(state);
474 self.is_byref.hash(state);
475 self.is_optional.hash(state);
476 }
477}
478
479#[allow(non_camel_case_types, clippy::enum_variant_names)]
481#[repr(u8)]
482enum AtomicTag {
483 TString = 0,
484 TLiteralString,
485 TCallableString,
486 TClassString,
487 TNonEmptyString,
488 TNumericString,
489 TInt,
490 TLiteralInt,
491 TIntRange,
492 TPositiveInt,
493 TNegativeInt,
494 TNonNegativeInt,
495 TFloat,
496 TLiteralFloat,
497 TBool,
498 TTrue,
499 TFalse,
500 TNull,
501 TVoid,
502 TNever,
503 TMixed,
504 TScalar,
505 TNumeric,
506 TObject,
507 TNamedObject,
508 TStaticObject,
509 TSelf,
510 TParent,
511 TCallable,
512 TClosure,
513 TArray,
514 TList,
515 TNonEmptyArray,
516 TNonEmptyList,
517 TKeyedArray,
518 TTemplateParam,
519 TConditional,
520 TInterfaceString,
521 TEnumString,
522 TTraitString,
523 TLiteralEnumCase,
524 TIntersection,
525}
526
527impl Hash for Atomic {
528 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
529 use AtomicTag as T;
530 match self {
531 Atomic::TString => (T::TString as u8).hash(state),
533 Atomic::TCallableString => (T::TCallableString as u8).hash(state),
534 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
535 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
536 Atomic::TInt => (T::TInt as u8).hash(state),
537 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
538 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
539 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
540 Atomic::TFloat => (T::TFloat as u8).hash(state),
541 Atomic::TBool => (T::TBool as u8).hash(state),
542 Atomic::TTrue => (T::TTrue as u8).hash(state),
543 Atomic::TFalse => (T::TFalse as u8).hash(state),
544 Atomic::TNull => (T::TNull as u8).hash(state),
545 Atomic::TVoid => (T::TVoid as u8).hash(state),
546 Atomic::TNever => (T::TNever as u8).hash(state),
547 Atomic::TMixed => (T::TMixed as u8).hash(state),
548 Atomic::TScalar => (T::TScalar as u8).hash(state),
549 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
550 Atomic::TObject => (T::TObject as u8).hash(state),
551 Atomic::TInterfaceString => (T::TInterfaceString as u8).hash(state),
552 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
553 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
554
555 Atomic::TLiteralString(s) => {
557 (T::TLiteralString as u8).hash(state);
558 s.hash(state);
559 }
560 Atomic::TClassString(opt) => {
561 (T::TClassString as u8).hash(state);
562 opt.hash(state);
563 }
564 Atomic::TLiteralInt(n) => {
565 (T::TLiteralInt as u8).hash(state);
566 n.hash(state);
567 }
568 Atomic::TIntRange { min, max } => {
569 (T::TIntRange as u8).hash(state);
570 min.hash(state);
571 max.hash(state);
572 }
573 Atomic::TLiteralFloat(int_bits, frac_bits) => {
574 (T::TLiteralFloat as u8).hash(state);
575 int_bits.hash(state);
576 frac_bits.hash(state);
577 }
578 Atomic::TNamedObject { fqcn, type_params } => {
579 (T::TNamedObject as u8).hash(state);
580 fqcn.hash(state);
581 type_params.hash(state);
582 }
583 Atomic::TStaticObject { fqcn } => {
584 (T::TStaticObject as u8).hash(state);
585 fqcn.hash(state);
586 }
587 Atomic::TSelf { fqcn } => {
588 (T::TSelf as u8).hash(state);
589 fqcn.hash(state);
590 }
591 Atomic::TParent { fqcn } => {
592 (T::TParent as u8).hash(state);
593 fqcn.hash(state);
594 }
595 Atomic::TCallable {
596 params,
597 return_type,
598 } => {
599 (T::TCallable as u8).hash(state);
600 params.hash(state);
601 return_type.hash(state);
602 }
603 Atomic::TClosure {
604 params,
605 return_type,
606 this_type,
607 } => {
608 (T::TClosure as u8).hash(state);
609 params.hash(state);
610 return_type.hash(state);
611 this_type.hash(state);
612 }
613 Atomic::TArray { key, value } => {
614 (T::TArray as u8).hash(state);
615 key.hash(state);
616 value.hash(state);
617 }
618 Atomic::TList { value } => {
619 (T::TList as u8).hash(state);
620 value.hash(state);
621 }
622 Atomic::TNonEmptyArray { key, value } => {
623 (T::TNonEmptyArray as u8).hash(state);
624 key.hash(state);
625 value.hash(state);
626 }
627 Atomic::TNonEmptyList { value } => {
628 (T::TNonEmptyList as u8).hash(state);
629 value.hash(state);
630 }
631 Atomic::TKeyedArray {
632 properties,
633 is_open,
634 is_list,
635 } => {
636 (T::TKeyedArray as u8).hash(state);
637 let mut pairs: Vec<_> = properties.iter().collect();
640 pairs.sort_by_key(|(a, _)| *a);
641 for (k, v) in pairs {
642 k.hash(state);
643 v.hash(state);
644 }
645 is_open.hash(state);
646 is_list.hash(state);
647 }
648 Atomic::TTemplateParam {
649 name,
650 as_type,
651 defining_entity,
652 } => {
653 (T::TTemplateParam as u8).hash(state);
654 name.hash(state);
655 as_type.hash(state);
656 defining_entity.hash(state);
657 }
658 Atomic::TConditional {
659 param_name,
660 subject,
661 if_true,
662 if_false,
663 } => {
664 (T::TConditional as u8).hash(state);
665 param_name.hash(state);
666 subject.hash(state);
667 if_true.hash(state);
668 if_false.hash(state);
669 }
670 Atomic::TLiteralEnumCase {
671 enum_fqcn,
672 case_name,
673 } => {
674 (T::TLiteralEnumCase as u8).hash(state);
675 enum_fqcn.hash(state);
676 case_name.hash(state);
677 }
678 Atomic::TIntersection { parts } => {
679 (T::TIntersection as u8).hash(state);
680 parts.hash(state);
681 }
682 }
683 }
684}