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_callable(&self) -> bool {
346 matches!(self, Atomic::TCallable { .. } | Atomic::TClosure { .. })
347 }
348
349 pub fn named_object_fqcn(&self) -> Option<&str> {
351 match self {
352 Atomic::TNamedObject { fqcn, .. }
353 | Atomic::TStaticObject { fqcn }
354 | Atomic::TSelf { fqcn }
355 | Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
356 _ => None,
357 }
358 }
359
360 pub fn type_name(&self) -> &'static str {
362 match self {
363 Atomic::TString
364 | Atomic::TLiteralString(_)
365 | Atomic::TNonEmptyString
366 | Atomic::TNumericString => "string",
367 Atomic::TCallableString => "callable-string",
368 Atomic::TClassString(_) => "class-string",
369 Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
370 Atomic::TPositiveInt => "positive-int",
371 Atomic::TNegativeInt => "negative-int",
372 Atomic::TNonNegativeInt => "non-negative-int",
373 Atomic::TFloat | Atomic::TLiteralFloat(..) => "float",
374 Atomic::TBool => "bool",
375 Atomic::TTrue => "true",
376 Atomic::TFalse => "false",
377 Atomic::TNull => "null",
378 Atomic::TVoid => "void",
379 Atomic::TNever => "never",
380 Atomic::TMixed => "mixed",
381 Atomic::TScalar => "scalar",
382 Atomic::TNumeric => "numeric",
383 Atomic::TObject => "object",
384 Atomic::TNamedObject { .. } => "object",
385 Atomic::TStaticObject { .. } => "static",
386 Atomic::TSelf { .. } => "self",
387 Atomic::TParent { .. } => "parent",
388 Atomic::TCallable { .. } => "callable",
389 Atomic::TClosure { .. } => "Closure",
390 Atomic::TArray { .. } => "array",
391 Atomic::TList { .. } => "list",
392 Atomic::TNonEmptyArray { .. } => "non-empty-array",
393 Atomic::TNonEmptyList { .. } => "non-empty-list",
394 Atomic::TKeyedArray { .. } => "array",
395 Atomic::TTemplateParam { .. } => "template-param",
396 Atomic::TConditional { .. } => "conditional-type",
397 Atomic::TInterfaceString => "interface-string",
398 Atomic::TEnumString => "enum-string",
399 Atomic::TTraitString => "trait-string",
400 Atomic::TLiteralEnumCase { .. } => "enum-case",
401 Atomic::TIntersection { .. } => "intersection",
402 }
403 }
404}
405
406impl Hash for FnParam {
411 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
412 self.name.hash(state);
413 self.ty.hash(state);
414 self.default.hash(state);
415 self.is_variadic.hash(state);
416 self.is_byref.hash(state);
417 self.is_optional.hash(state);
418 }
419}
420
421#[allow(non_camel_case_types, clippy::enum_variant_names)]
423#[repr(u8)]
424enum AtomicTag {
425 TString = 0,
426 TLiteralString,
427 TCallableString,
428 TClassString,
429 TNonEmptyString,
430 TNumericString,
431 TInt,
432 TLiteralInt,
433 TIntRange,
434 TPositiveInt,
435 TNegativeInt,
436 TNonNegativeInt,
437 TFloat,
438 TLiteralFloat,
439 TBool,
440 TTrue,
441 TFalse,
442 TNull,
443 TVoid,
444 TNever,
445 TMixed,
446 TScalar,
447 TNumeric,
448 TObject,
449 TNamedObject,
450 TStaticObject,
451 TSelf,
452 TParent,
453 TCallable,
454 TClosure,
455 TArray,
456 TList,
457 TNonEmptyArray,
458 TNonEmptyList,
459 TKeyedArray,
460 TTemplateParam,
461 TConditional,
462 TInterfaceString,
463 TEnumString,
464 TTraitString,
465 TLiteralEnumCase,
466 TIntersection,
467}
468
469impl Hash for Atomic {
470 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
471 use AtomicTag as T;
472 match self {
473 Atomic::TString => (T::TString as u8).hash(state),
475 Atomic::TCallableString => (T::TCallableString as u8).hash(state),
476 Atomic::TNonEmptyString => (T::TNonEmptyString as u8).hash(state),
477 Atomic::TNumericString => (T::TNumericString as u8).hash(state),
478 Atomic::TInt => (T::TInt as u8).hash(state),
479 Atomic::TPositiveInt => (T::TPositiveInt as u8).hash(state),
480 Atomic::TNegativeInt => (T::TNegativeInt as u8).hash(state),
481 Atomic::TNonNegativeInt => (T::TNonNegativeInt as u8).hash(state),
482 Atomic::TFloat => (T::TFloat as u8).hash(state),
483 Atomic::TBool => (T::TBool as u8).hash(state),
484 Atomic::TTrue => (T::TTrue as u8).hash(state),
485 Atomic::TFalse => (T::TFalse as u8).hash(state),
486 Atomic::TNull => (T::TNull as u8).hash(state),
487 Atomic::TVoid => (T::TVoid as u8).hash(state),
488 Atomic::TNever => (T::TNever as u8).hash(state),
489 Atomic::TMixed => (T::TMixed as u8).hash(state),
490 Atomic::TScalar => (T::TScalar as u8).hash(state),
491 Atomic::TNumeric => (T::TNumeric as u8).hash(state),
492 Atomic::TObject => (T::TObject as u8).hash(state),
493 Atomic::TInterfaceString => (T::TInterfaceString as u8).hash(state),
494 Atomic::TEnumString => (T::TEnumString as u8).hash(state),
495 Atomic::TTraitString => (T::TTraitString as u8).hash(state),
496
497 Atomic::TLiteralString(s) => {
499 (T::TLiteralString as u8).hash(state);
500 s.hash(state);
501 }
502 Atomic::TClassString(opt) => {
503 (T::TClassString as u8).hash(state);
504 opt.hash(state);
505 }
506 Atomic::TLiteralInt(n) => {
507 (T::TLiteralInt as u8).hash(state);
508 n.hash(state);
509 }
510 Atomic::TIntRange { min, max } => {
511 (T::TIntRange as u8).hash(state);
512 min.hash(state);
513 max.hash(state);
514 }
515 Atomic::TLiteralFloat(int_bits, frac_bits) => {
516 (T::TLiteralFloat as u8).hash(state);
517 int_bits.hash(state);
518 frac_bits.hash(state);
519 }
520 Atomic::TNamedObject { fqcn, type_params } => {
521 (T::TNamedObject as u8).hash(state);
522 fqcn.hash(state);
523 type_params.hash(state);
524 }
525 Atomic::TStaticObject { fqcn } => {
526 (T::TStaticObject as u8).hash(state);
527 fqcn.hash(state);
528 }
529 Atomic::TSelf { fqcn } => {
530 (T::TSelf as u8).hash(state);
531 fqcn.hash(state);
532 }
533 Atomic::TParent { fqcn } => {
534 (T::TParent as u8).hash(state);
535 fqcn.hash(state);
536 }
537 Atomic::TCallable {
538 params,
539 return_type,
540 } => {
541 (T::TCallable as u8).hash(state);
542 params.hash(state);
543 return_type.hash(state);
544 }
545 Atomic::TClosure {
546 params,
547 return_type,
548 this_type,
549 } => {
550 (T::TClosure as u8).hash(state);
551 params.hash(state);
552 return_type.hash(state);
553 this_type.hash(state);
554 }
555 Atomic::TArray { key, value } => {
556 (T::TArray as u8).hash(state);
557 key.hash(state);
558 value.hash(state);
559 }
560 Atomic::TList { value } => {
561 (T::TList as u8).hash(state);
562 value.hash(state);
563 }
564 Atomic::TNonEmptyArray { key, value } => {
565 (T::TNonEmptyArray as u8).hash(state);
566 key.hash(state);
567 value.hash(state);
568 }
569 Atomic::TNonEmptyList { value } => {
570 (T::TNonEmptyList as u8).hash(state);
571 value.hash(state);
572 }
573 Atomic::TKeyedArray {
574 properties,
575 is_open,
576 is_list,
577 } => {
578 (T::TKeyedArray as u8).hash(state);
579 let mut pairs: Vec<_> = properties.iter().collect();
582 pairs.sort_by_key(|(a, _)| *a);
583 for (k, v) in pairs {
584 k.hash(state);
585 v.hash(state);
586 }
587 is_open.hash(state);
588 is_list.hash(state);
589 }
590 Atomic::TTemplateParam {
591 name,
592 as_type,
593 defining_entity,
594 } => {
595 (T::TTemplateParam as u8).hash(state);
596 name.hash(state);
597 as_type.hash(state);
598 defining_entity.hash(state);
599 }
600 Atomic::TConditional {
601 param_name,
602 subject,
603 if_true,
604 if_false,
605 } => {
606 (T::TConditional as u8).hash(state);
607 param_name.hash(state);
608 subject.hash(state);
609 if_true.hash(state);
610 if_false.hash(state);
611 }
612 Atomic::TLiteralEnumCase {
613 enum_fqcn,
614 case_name,
615 } => {
616 (T::TLiteralEnumCase as u8).hash(state);
617 enum_fqcn.hash(state);
618 case_name.hash(state);
619 }
620 Atomic::TIntersection { parts } => {
621 (T::TIntersection as u8).hash(state);
622 parts.hash(state);
623 }
624 }
625 }
626}