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`
224    TInterfaceString,
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::TInterfaceString => (T::TInterfaceString as u8).hash(state),
573            Atomic::TEnumString => (T::TEnumString as u8).hash(state),
574            Atomic::TTraitString => (T::TTraitString as u8).hash(state),
575
576            // --- variants with fields ---
577            Atomic::TLiteralString(s) => {
578                (T::TLiteralString as u8).hash(state);
579                s.hash(state);
580            }
581            Atomic::TClassString(opt) => {
582                (T::TClassString as u8).hash(state);
583                opt.hash(state);
584            }
585            Atomic::TLiteralInt(n) => {
586                (T::TLiteralInt as u8).hash(state);
587                n.hash(state);
588            }
589            Atomic::TIntRange { min, max } => {
590                (T::TIntRange as u8).hash(state);
591                min.hash(state);
592                max.hash(state);
593            }
594            Atomic::TLiteralFloat(int_bits, frac_bits) => {
595                (T::TLiteralFloat as u8).hash(state);
596                int_bits.hash(state);
597                frac_bits.hash(state);
598            }
599            Atomic::TNamedObject { fqcn, type_params } => {
600                (T::TNamedObject as u8).hash(state);
601                fqcn.hash(state);
602                type_params.hash(state);
603            }
604            Atomic::TStaticObject { fqcn } => {
605                (T::TStaticObject as u8).hash(state);
606                fqcn.hash(state);
607            }
608            Atomic::TSelf { fqcn } => {
609                (T::TSelf as u8).hash(state);
610                fqcn.hash(state);
611            }
612            Atomic::TParent { fqcn } => {
613                (T::TParent as u8).hash(state);
614                fqcn.hash(state);
615            }
616            Atomic::TCallable {
617                params,
618                return_type,
619            } => {
620                (T::TCallable as u8).hash(state);
621                params.hash(state);
622                return_type.hash(state);
623            }
624            Atomic::TClosure {
625                params,
626                return_type,
627                this_type,
628            } => {
629                (T::TClosure as u8).hash(state);
630                params.hash(state);
631                return_type.hash(state);
632                this_type.hash(state);
633            }
634            Atomic::TArray { key, value } => {
635                (T::TArray as u8).hash(state);
636                key.hash(state);
637                value.hash(state);
638            }
639            Atomic::TList { value } => {
640                (T::TList as u8).hash(state);
641                value.hash(state);
642            }
643            Atomic::TNonEmptyArray { key, value } => {
644                (T::TNonEmptyArray as u8).hash(state);
645                key.hash(state);
646                value.hash(state);
647            }
648            Atomic::TNonEmptyList { value } => {
649                (T::TNonEmptyList as u8).hash(state);
650                value.hash(state);
651            }
652            Atomic::TKeyedArray {
653                properties,
654                is_open,
655                is_list,
656            } => {
657                (T::TKeyedArray as u8).hash(state);
658                // Sort by key before hashing so the hash is order-independent,
659                // consistent with PartialEq which uses IndexMap value equality.
660                let mut pairs: Vec<_> = properties.iter().collect();
661                pairs.sort_by_key(|(a, _)| *a);
662                for (k, v) in pairs {
663                    k.hash(state);
664                    v.hash(state);
665                }
666                is_open.hash(state);
667                is_list.hash(state);
668            }
669            Atomic::TTemplateParam {
670                name,
671                as_type,
672                defining_entity,
673            } => {
674                (T::TTemplateParam as u8).hash(state);
675                name.hash(state);
676                as_type.hash(state);
677                defining_entity.hash(state);
678            }
679            Atomic::TConditional {
680                param_name,
681                subject,
682                if_true,
683                if_false,
684            } => {
685                (T::TConditional as u8).hash(state);
686                param_name.hash(state);
687                subject.hash(state);
688                if_true.hash(state);
689                if_false.hash(state);
690            }
691            Atomic::TLiteralEnumCase {
692                enum_fqcn,
693                case_name,
694            } => {
695                (T::TLiteralEnumCase as u8).hash(state);
696                enum_fqcn.hash(state);
697                case_name.hash(state);
698            }
699            Atomic::TIntersection { parts } => {
700                (T::TIntersection as u8).hash(state);
701                parts.hash(state);
702            }
703        }
704    }
705}