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
246 | Atomic::TArray { .. }
247 | Atomic::TList { .. }
248 | Atomic::TNonEmptyArray { .. }
249 | Atomic::TNonEmptyList { .. } => true,
250
251 Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
252
253 Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
254
255 _ => false,
256 }
257 }
258
259 pub fn can_be_truthy(&self) -> bool {
261 match self {
262 Atomic::TNever
263 | Atomic::TVoid
264 | Atomic::TNull
265 | Atomic::TFalse
266 | Atomic::TLiteralInt(0)
267 | Atomic::TLiteralFloat(0, 0) => false,
268 Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
269 _ => true,
270 }
271 }
272
273 pub fn is_numeric(&self) -> bool {
275 matches!(
276 self,
277 Atomic::TInt
278 | Atomic::TLiteralInt(_)
279 | Atomic::TIntRange { .. }
280 | Atomic::TPositiveInt
281 | Atomic::TNegativeInt
282 | Atomic::TNonNegativeInt
283 | Atomic::TFloat
284 | Atomic::TLiteralFloat(..)
285 | Atomic::TNumeric
286 | Atomic::TNumericString
287 )
288 }
289
290 pub fn is_int(&self) -> bool {
292 matches!(
293 self,
294 Atomic::TInt
295 | Atomic::TLiteralInt(_)
296 | Atomic::TIntRange { .. }
297 | Atomic::TPositiveInt
298 | Atomic::TNegativeInt
299 | Atomic::TNonNegativeInt
300 )
301 }
302
303 pub fn is_string(&self) -> bool {
305 matches!(
306 self,
307 Atomic::TString
308 | Atomic::TLiteralString(_)
309 | Atomic::TCallableString
310 | Atomic::TClassString(_)
311 | Atomic::TNonEmptyString
312 | Atomic::TNumericString
313 | Atomic::TInterfaceString
314 | Atomic::TEnumString
315 | Atomic::TTraitString
316 )
317 }
318
319 pub fn is_array(&self) -> bool {
321 matches!(
322 self,
323 Atomic::TArray { .. }
324 | Atomic::TList { .. }
325 | Atomic::TNonEmptyArray { .. }
326 | Atomic::TNonEmptyList { .. }
327 | Atomic::TKeyedArray { .. }
328 )
329 }
330
331 pub fn is_object(&self) -> bool {
333 matches!(
334 self,
335 Atomic::TObject
336 | Atomic::TNamedObject { .. }
337 | Atomic::TStaticObject { .. }
338 | Atomic::TSelf { .. }
339 | Atomic::TParent { .. }
340 | Atomic::TIntersection { .. }
341 )
342 }
343
344 pub fn is_definitely_non_object(&self) -> bool {
349 matches!(
350 self,
351 Atomic::TString
352 | Atomic::TLiteralString(_)
353 | Atomic::TCallableString
354 | Atomic::TClassString(_)
355 | Atomic::TNonEmptyString
356 | Atomic::TNumericString
357 | Atomic::TInterfaceString
358 | Atomic::TEnumString
359 | Atomic::TTraitString
360 | Atomic::TInt
361 | Atomic::TLiteralInt(_)
362 | Atomic::TIntRange { .. }
363 | Atomic::TPositiveInt
364 | Atomic::TNegativeInt
365 | Atomic::TNonNegativeInt
366 | Atomic::TFloat
367 | Atomic::TLiteralFloat(..)
368 | Atomic::TBool
369 | Atomic::TTrue
370 | Atomic::TFalse
371 | Atomic::TNull
372 | Atomic::TVoid
373 | Atomic::TArray { .. }
374 | Atomic::TList { .. }
375 | Atomic::TNonEmptyArray { .. }
376 | Atomic::TNonEmptyList { .. }
377 | Atomic::TKeyedArray { .. }
378 | Atomic::TScalar
379 | Atomic::TNumeric
380 )
381 }
382
383 pub fn is_callable(&self) -> bool {
385 matches!(self, Atomic::TCallable { .. } | Atomic::TClosure { .. })
386 }
387
388 pub fn named_object_fqcn(&self) -> Option<&str> {
390 match self {
391 Atomic::TNamedObject { fqcn, .. }
392 | Atomic::TStaticObject { fqcn }
393 | Atomic::TSelf { fqcn }
394 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
395 _ => None,
396 }
397 }
398
399 pub fn type_name(&self) -> &'static str {
401 match self {
402 Atomic::TString
403 | Atomic::TLiteralString(_)
404 | Atomic::TNonEmptyString
405 | Atomic::TNumericString => "string",
406 Atomic::TCallableString => "callable-string",
407 Atomic::TClassString(_) => "class-string",
408 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
409 Atomic::TPositiveInt => "positive-int",
410 Atomic::TNegativeInt => "negative-int",
411 Atomic::TNonNegativeInt => "non-negative-int",
412 Atomic::TFloat | Atomic::TLiteralFloat(..) => "float",
413 Atomic::TBool => "bool",
414 Atomic::TTrue => "true",
415 Atomic::TFalse => "false",
416 Atomic::TNull => "null",
417 Atomic::TVoid => "void",
418 Atomic::TNever => "never",
419 Atomic::TMixed => "mixed",
420 Atomic::TScalar => "scalar",
421 Atomic::TNumeric => "numeric",
422 Atomic::TObject => "object",
423 Atomic::TNamedObject { .. } => "object",
424 Atomic::TStaticObject { .. } => "static",
425 Atomic::TSelf { .. } => "self",
426 Atomic::TParent { .. } => "parent",
427 Atomic::TCallable { .. } => "callable",
428 Atomic::TClosure { .. } => "Closure",
429 Atomic::TArray { .. } => "array",
430 Atomic::TList { .. } => "list",
431 Atomic::TNonEmptyArray { .. } => "non-empty-array",
432 Atomic::TNonEmptyList { .. } => "non-empty-list",
433 Atomic::TKeyedArray { .. } => "array",
434 Atomic::TTemplateParam { .. } => "template-param",
435 Atomic::TConditional { .. } => "conditional-type",
436 Atomic::TInterfaceString => "interface-string",
437 Atomic::TEnumString => "enum-string",
438 Atomic::TTraitString => "trait-string",
439 Atomic::TLiteralEnumCase { .. } => "enum-case",
440 Atomic::TIntersection { .. } => "intersection",
441 }
442 }
443}
444
445impl Hash for FnParam {
450 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
451 self.name.hash(state);
452 self.ty.hash(state);
453 self.default.hash(state);
454 self.is_variadic.hash(state);
455 self.is_byref.hash(state);
456 self.is_optional.hash(state);
457 }
458}
459
460#[allow(non_camel_case_types, clippy::enum_variant_names)]
462#[repr(u8)]
463enum AtomicTag {
464 TString = 0,
465 TLiteralString,
466 TCallableString,
467 TClassString,
468 TNonEmptyString,
469 TNumericString,
470 TInt,
471 TLiteralInt,
472 TIntRange,
473 TPositiveInt,
474 TNegativeInt,
475 TNonNegativeInt,
476 TFloat,
477 TLiteralFloat,
478 TBool,
479 TTrue,
480 TFalse,
481 TNull,
482 TVoid,
483 TNever,
484 TMixed,
485 TScalar,
486 TNumeric,
487 TObject,
488 TNamedObject,
489 TStaticObject,
490 TSelf,
491 TParent,
492 TCallable,
493 TClosure,
494 TArray,
495 TList,
496 TNonEmptyArray,
497 TNonEmptyList,
498 TKeyedArray,
499 TTemplateParam,
500 TConditional,
501 TInterfaceString,
502 TEnumString,
503 TTraitString,
504 TLiteralEnumCase,
505 TIntersection,
506}
507
508impl Hash for Atomic {
509 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
510 use AtomicTag as T;
511 match self {
512 Atomic::TString => (T::TString as u8).hash(state),
514 Atomic::TCallableString => (T::TCallableString as u8).hash(state),
515 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
516 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
517 Atomic::TInt => (T::TInt as u8).hash(state),
518 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
519 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
520 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
521 Atomic::TFloat => (T::TFloat as u8).hash(state),
522 Atomic::TBool => (T::TBool as u8).hash(state),
523 Atomic::TTrue => (T::TTrue as u8).hash(state),
524 Atomic::TFalse => (T::TFalse as u8).hash(state),
525 Atomic::TNull => (T::TNull as u8).hash(state),
526 Atomic::TVoid => (T::TVoid as u8).hash(state),
527 Atomic::TNever => (T::TNever as u8).hash(state),
528 Atomic::TMixed => (T::TMixed as u8).hash(state),
529 Atomic::TScalar => (T::TScalar as u8).hash(state),
530 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
531 Atomic::TObject => (T::TObject as u8).hash(state),
532 Atomic::TInterfaceString => (T::TInterfaceString as u8).hash(state),
533 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
534 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
535
536 Atomic::TLiteralString(s) => {
538 (T::TLiteralString as u8).hash(state);
539 s.hash(state);
540 }
541 Atomic::TClassString(opt) => {
542 (T::TClassString as u8).hash(state);
543 opt.hash(state);
544 }
545 Atomic::TLiteralInt(n) => {
546 (T::TLiteralInt as u8).hash(state);
547 n.hash(state);
548 }
549 Atomic::TIntRange { min, max } => {
550 (T::TIntRange as u8).hash(state);
551 min.hash(state);
552 max.hash(state);
553 }
554 Atomic::TLiteralFloat(int_bits, frac_bits) => {
555 (T::TLiteralFloat as u8).hash(state);
556 int_bits.hash(state);
557 frac_bits.hash(state);
558 }
559 Atomic::TNamedObject { fqcn, type_params } => {
560 (T::TNamedObject as u8).hash(state);
561 fqcn.hash(state);
562 type_params.hash(state);
563 }
564 Atomic::TStaticObject { fqcn } => {
565 (T::TStaticObject as u8).hash(state);
566 fqcn.hash(state);
567 }
568 Atomic::TSelf { fqcn } => {
569 (T::TSelf as u8).hash(state);
570 fqcn.hash(state);
571 }
572 Atomic::TParent { fqcn } => {
573 (T::TParent as u8).hash(state);
574 fqcn.hash(state);
575 }
576 Atomic::TCallable {
577 params,
578 return_type,
579 } => {
580 (T::TCallable as u8).hash(state);
581 params.hash(state);
582 return_type.hash(state);
583 }
584 Atomic::TClosure {
585 params,
586 return_type,
587 this_type,
588 } => {
589 (T::TClosure as u8).hash(state);
590 params.hash(state);
591 return_type.hash(state);
592 this_type.hash(state);
593 }
594 Atomic::TArray { key, value } => {
595 (T::TArray as u8).hash(state);
596 key.hash(state);
597 value.hash(state);
598 }
599 Atomic::TList { value } => {
600 (T::TList as u8).hash(state);
601 value.hash(state);
602 }
603 Atomic::TNonEmptyArray { key, value } => {
604 (T::TNonEmptyArray as u8).hash(state);
605 key.hash(state);
606 value.hash(state);
607 }
608 Atomic::TNonEmptyList { value } => {
609 (T::TNonEmptyList as u8).hash(state);
610 value.hash(state);
611 }
612 Atomic::TKeyedArray {
613 properties,
614 is_open,
615 is_list,
616 } => {
617 (T::TKeyedArray as u8).hash(state);
618 let mut pairs: Vec<_> = properties.iter().collect();
621 pairs.sort_by_key(|(a, _)| *a);
622 for (k, v) in pairs {
623 k.hash(state);
624 v.hash(state);
625 }
626 is_open.hash(state);
627 is_list.hash(state);
628 }
629 Atomic::TTemplateParam {
630 name,
631 as_type,
632 defining_entity,
633 } => {
634 (T::TTemplateParam as u8).hash(state);
635 name.hash(state);
636 as_type.hash(state);
637 defining_entity.hash(state);
638 }
639 Atomic::TConditional {
640 param_name,
641 subject,
642 if_true,
643 if_false,
644 } => {
645 (T::TConditional as u8).hash(state);
646 param_name.hash(state);
647 subject.hash(state);
648 if_true.hash(state);
649 if_false.hash(state);
650 }
651 Atomic::TLiteralEnumCase {
652 enum_fqcn,
653 case_name,
654 } => {
655 (T::TLiteralEnumCase as u8).hash(state);
656 enum_fqcn.hash(state);
657 case_name.hash(state);
658 }
659 Atomic::TIntersection { parts } => {
660 (T::TIntersection as u8).hash(state);
661 parts.hash(state);
662 }
663 }
664 }
665}