Skip to main content

mir_types/
atomic.rs

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// ---------------------------------------------------------------------------
11// FnParam — used inside callable/closure atomics
12// ---------------------------------------------------------------------------
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct FnParam {
16    pub name: Name,
17    /// Parameter type stored as SimpleType for compact representation.
18    /// Most params are simple scalars (string, int, etc.) and fit inline.
19    pub ty: Option<crate::compact::SimpleType>,
20    /// `@param-out` / `@psalm-param-out` writeback type. When `Some`, the
21    /// variable passed for this by-ref param is set to this type after the call
22    /// instead of the declared in-type (`ty`). Preserved through `TClosure` so
23    /// first-class callables honour out-param annotations.
24    #[serde(default)]
25    pub out_ty: Option<crate::compact::SimpleType>,
26    /// Default value stored as SimpleType. Usually None or a simple scalar.
27    pub default: Option<crate::compact::SimpleType>,
28    pub is_variadic: bool,
29    pub is_byref: bool,
30    pub is_optional: bool,
31}
32
33// ---------------------------------------------------------------------------
34// Variance — covariance / contravariance for template type parameters
35// ---------------------------------------------------------------------------
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
38pub enum Variance {
39    /// Default: exact type match required (no `@template-covariant` / `@template-contravariant`).
40    #[default]
41    Invariant,
42    /// `@template-covariant T` — `C<Sub>` is assignable to `C<Super>`.
43    Covariant,
44    /// `@template-contravariant T` — `C<Super>` is assignable to `C<Sub>`.
45    Contravariant,
46}
47
48// ---------------------------------------------------------------------------
49// TemplateParam — `@template T` / `@template T of Bound`
50// ---------------------------------------------------------------------------
51
52#[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// ---------------------------------------------------------------------------
60// KeyedProperty — entry in TKeyedArray
61// ---------------------------------------------------------------------------
62
63#[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            // Int < String
81            (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// ---------------------------------------------------------------------------
94// Atomic — every distinct PHP type variant
95// ---------------------------------------------------------------------------
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98pub enum Atomic {
99    // --- Scalars ---
100    /// `string`
101    TString,
102    /// `"hello"` — a specific string literal
103    TLiteralString(Arc<str>),
104    /// `callable-string` — a string containing a callable name
105    TCallableString,
106    /// `class-string` or `class-string<T>`
107    TClassString(Option<Name>),
108    /// `non-empty-string`
109    TNonEmptyString,
110    /// `numeric-string`
111    TNumericString,
112
113    /// `int`
114    TInt,
115    /// `42` — a specific integer literal
116    TLiteralInt(i64),
117    /// `int<min, max>` — bounded integer range
118    TIntRange { min: Option<i64>, max: Option<i64> },
119    /// `positive-int`
120    TPositiveInt,
121    /// `negative-int`
122    TNegativeInt,
123    /// `non-negative-int`
124    TNonNegativeInt,
125
126    /// `float`
127    TFloat,
128    /// `float` that is always integral (whole-number value, no fractional part).
129    /// Returned by `floor()`, `ceil()`, and zero-precision `round()`. Can be passed to `int`
130    /// parameters without `ImplicitFloatToIntCast` because the conversion is lossless.
131    TIntegralFloat,
132    /// `3.14` — a specific float literal
133    TLiteralFloat(i64, i64), // stored as (int_bits, frac_bits) to be PartialEq+Hash-friendly
134    // We use ordered_float or just store as ordered pair for equality purposes.
135    /// `bool`
136    TBool,
137    /// `true`
138    TTrue,
139    /// `false`
140    TFalse,
141
142    /// `null`
143    TNull,
144
145    // --- Bottom / top ---
146    /// `void` — return-only; can't be used as a value
147    TVoid,
148    /// `never` — function that never returns (throws or infinite loop)
149    TNever,
150    /// `mixed` — top type; accepts anything
151    TMixed,
152    /// `scalar` — int | float | string | bool
153    TScalar,
154    /// `numeric` — int | float | numeric-string
155    TNumeric,
156
157    // --- Objects ---
158    /// `object` — any object
159    TObject,
160    /// `ClassName` / `ClassName<T1, T2>` — specific named class/interface
161    TNamedObject {
162        fqcn: Name,
163        /// Resolved generic type arguments (e.g. `Collection<int>`)
164        type_params: Arc<[Type]>,
165    },
166    /// `static` — late static binding type; resolved to calling class at call site
167    TStaticObject { fqcn: Name },
168    /// `self` — the class in whose body the type appears
169    TSelf { fqcn: Name },
170    /// `parent` — the parent class
171    TParent { fqcn: Name },
172
173    // --- Callables ---
174    /// `callable` or `callable(T): R`
175    TCallable {
176        params: Option<Vec<FnParam>>,
177        return_type: Option<Box<Type>>,
178    },
179    /// `Closure` or `Closure(T): R` — more specific than TCallable
180    TClosure {
181        params: Vec<FnParam>,
182        return_type: Box<Type>,
183        this_type: Option<Box<Type>>,
184    },
185
186    // --- Arrays ---
187    /// `array` or `array<K, V>`
188    TArray { key: Box<Type>, value: Box<Type> },
189    /// `list<T>` — integer-keyed sequential array (keys 0, 1, 2, …)
190    TList { value: Box<Type> },
191    /// `non-empty-array<K, V>`
192    TNonEmptyArray { key: Box<Type>, value: Box<Type> },
193    /// `non-empty-list<T>`
194    TNonEmptyList { value: Box<Type> },
195    /// `array{key: T, ...}` — shape / keyed array
196    TKeyedArray {
197        properties: IndexMap<ArrayKey, KeyedProperty>,
198        /// If true, additional keys beyond the declared ones may exist
199        is_open: bool,
200        /// If true, the shape represents a list (integer keys only)
201        is_list: bool,
202    },
203
204    // --- Generics / meta-types ---
205    /// `T` — a template type parameter
206    TTemplateParam {
207        name: Name,
208        as_type: Box<Type>,
209        /// The entity (class or function FQN) that declared this template
210        defining_entity: Name,
211    },
212    /// `($param is TypeName ? A : B)` — conditional type
213    TConditional {
214        /// The parameter name being tested (without `$`), e.g. `"classOrInterface"`.
215        /// `None` for conditionals that were not parsed from a `$param is` form.
216        param_name: Option<Name>,
217        subject: Box<Type>,
218        if_true: Box<Type>,
219        if_false: Box<Type>,
220    },
221
222    // --- Special object strings ---
223    /// `interface-string` or `interface-string<T>`
224    TInterfaceString(Option<Name>),
225    /// `enum-string`
226    TEnumString,
227    /// `trait-string`
228    TTraitString,
229
230    // --- Enum cases ---
231    /// `EnumName::CaseName` — a specific enum case literal
232    TLiteralEnumCase { enum_fqcn: Name, case_name: Name },
233
234    // --- Intersection ---
235    /// `A&B&C` — PHP 8.1+ pure intersection type
236    TIntersection { parts: Arc<[Type]> },
237}
238
239impl Atomic {
240    /// Whether this atomic type can ever evaluate to a falsy value.
241    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  // "0" is both non-empty and falsy in PHP
256            | Atomic::TNumericString   // "0" is a valid numeric-string and is falsy
257            | Atomic::TArray { .. }
258            | Atomic::TList { .. } => true,
259            // Non-empty collections always have at least one element — never falsy in PHP.
260            // TNonEmptyArray and TNonEmptyList are intentionally excluded here.
261
262            Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
263
264            Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
265
266            // An int range can be falsy (== 0) when 0 is within the bounds.
267            Atomic::TIntRange { min, max } => {
268                min.is_none_or(|lo| lo <= 0) && max.is_none_or(|hi| hi >= 0)
269            }
270            // non-negative-int includes 0
271            Atomic::TNonNegativeInt => true,
272
273            _ => false,
274        }
275    }
276
277    /// Whether this atomic type can ever evaluate to a truthy value.
278    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            // int<0, 0> always equals zero — never truthy.
288            Atomic::TIntRange {
289                min: Some(0),
290                max: Some(0),
291            } => false,
292            // array{} — a closed empty keyed array — is always falsy.
293            Atomic::TKeyedArray {
294                properties,
295                is_open,
296                ..
297            } if !is_open && properties.is_empty() => false,
298            _ => true,
299        }
300    }
301
302    /// Whether this atomic represents a numeric type (int, float, or numeric-string).
303    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    /// Whether this atomic is an integer variant.
321    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    /// Whether this atomic is a string variant.
334    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    /// Whether this atomic is an array variant.
350    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    /// Whether this atomic is an object variant.
362    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    /// Whether this atomic can never be an object instance — so `clone`-ing it
375    /// is invalid. Conservative: ambiguous atomics (`mixed`, `callable`,
376    /// `never`, conditionals, template params, enum cases, intersections,
377    /// object types) all return `false` so they never trigger a false positive.
378    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    /// Whether this atomic is a callable variant.
415    ///
416    /// `TCallableString` is included: it denotes a string *known* to be a
417    /// valid callable, so `!is_callable()` must remove it from the type.
418    pub fn is_callable(&self) -> bool {
419        matches!(
420            self,
421            Atomic::TCallable { .. } | Atomic::TClosure { .. } | Atomic::TCallableString
422        )
423    }
424
425    /// Returns the FQCN if this is a named object type.
426    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    /// A human-readable name for this type (used in error messages).
437    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
482// ---------------------------------------------------------------------------
483// Hash impls
484// ---------------------------------------------------------------------------
485
486impl 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/// Discriminant tags for `Atomic` used in the manual `Hash` impl below.
499#[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            // --- tag-only variants ---
552            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::TEnumString => (T::TEnumString as u8).hash(state),
573            Atomic::TTraitString => (T::TTraitString as u8).hash(state),
574
575            // --- variants with fields ---
576            Atomic::TLiteralString(s) => {
577                (T::TLiteralString as u8).hash(state);
578                s.hash(state);
579            }
580            Atomic::TClassString(opt) => {
581                (T::TClassString as u8).hash(state);
582                opt.hash(state);
583            }
584            Atomic::TInterfaceString(opt) => {
585                (T::TInterfaceString as u8).hash(state);
586                opt.hash(state);
587            }
588            Atomic::TLiteralInt(n) => {
589                (T::TLiteralInt as u8).hash(state);
590                n.hash(state);
591            }
592            Atomic::TIntRange { min, max } => {
593                (T::TIntRange as u8).hash(state);
594                min.hash(state);
595                max.hash(state);
596            }
597            Atomic::TLiteralFloat(int_bits, frac_bits) => {
598                (T::TLiteralFloat as u8).hash(state);
599                int_bits.hash(state);
600                frac_bits.hash(state);
601            }
602            Atomic::TNamedObject { fqcn, type_params } => {
603                (T::TNamedObject as u8).hash(state);
604                fqcn.hash(state);
605                type_params.hash(state);
606            }
607            Atomic::TStaticObject { fqcn } => {
608                (T::TStaticObject as u8).hash(state);
609                fqcn.hash(state);
610            }
611            Atomic::TSelf { fqcn } => {
612                (T::TSelf as u8).hash(state);
613                fqcn.hash(state);
614            }
615            Atomic::TParent { fqcn } => {
616                (T::TParent as u8).hash(state);
617                fqcn.hash(state);
618            }
619            Atomic::TCallable {
620                params,
621                return_type,
622            } => {
623                (T::TCallable as u8).hash(state);
624                params.hash(state);
625                return_type.hash(state);
626            }
627            Atomic::TClosure {
628                params,
629                return_type,
630                this_type,
631            } => {
632                (T::TClosure as u8).hash(state);
633                params.hash(state);
634                return_type.hash(state);
635                this_type.hash(state);
636            }
637            Atomic::TArray { key, value } => {
638                (T::TArray as u8).hash(state);
639                key.hash(state);
640                value.hash(state);
641            }
642            Atomic::TList { value } => {
643                (T::TList as u8).hash(state);
644                value.hash(state);
645            }
646            Atomic::TNonEmptyArray { key, value } => {
647                (T::TNonEmptyArray as u8).hash(state);
648                key.hash(state);
649                value.hash(state);
650            }
651            Atomic::TNonEmptyList { value } => {
652                (T::TNonEmptyList as u8).hash(state);
653                value.hash(state);
654            }
655            Atomic::TKeyedArray {
656                properties,
657                is_open,
658                is_list,
659            } => {
660                (T::TKeyedArray as u8).hash(state);
661                // Sort by key before hashing so the hash is order-independent,
662                // consistent with PartialEq which uses IndexMap value equality.
663                let mut pairs: Vec<_> = properties.iter().collect();
664                pairs.sort_by_key(|(a, _)| *a);
665                for (k, v) in pairs {
666                    k.hash(state);
667                    v.hash(state);
668                }
669                is_open.hash(state);
670                is_list.hash(state);
671            }
672            Atomic::TTemplateParam {
673                name,
674                as_type,
675                defining_entity,
676            } => {
677                (T::TTemplateParam as u8).hash(state);
678                name.hash(state);
679                as_type.hash(state);
680                defining_entity.hash(state);
681            }
682            Atomic::TConditional {
683                param_name,
684                subject,
685                if_true,
686                if_false,
687            } => {
688                (T::TConditional as u8).hash(state);
689                param_name.hash(state);
690                subject.hash(state);
691                if_true.hash(state);
692                if_false.hash(state);
693            }
694            Atomic::TLiteralEnumCase {
695                enum_fqcn,
696                case_name,
697            } => {
698                (T::TLiteralEnumCase as u8).hash(state);
699                enum_fqcn.hash(state);
700                case_name.hash(state);
701            }
702            Atomic::TIntersection { parts } => {
703                (T::TIntersection as u8).hash(state);
704                parts.hash(state);
705            }
706        }
707    }
708}