tsz_solver/types.rs
1//! Type representation for the structural solver.
2//!
3//! Types are represented as lightweight `TypeId` handles that point into
4//! an interning table. The actual structure is stored in `TypeData`.
5
6use crate::def::DefId;
7use serde::Serialize;
8use tsz_binder::SymbolId;
9use tsz_common::interner::Atom;
10
11/// A lightweight handle to an interned type.
12/// Equality check is O(1) - just compare the u32 values.
13///
14/// # Sentinel Value Semantics
15///
16/// The following sentinel values have specific semantics for error handling and type inference:
17///
18/// ## `TypeId::ERROR`
19/// Used when type resolution **fails** due to an actual error:
20/// - Missing AST nodes or invalid syntax
21/// - Type annotation that cannot be resolved
22/// - Failed type inference with no fallback
23///
24/// **Error propagation**: ERROR is "contagious" - operations on ERROR types return ERROR.
25/// This prevents cascading errors from a single root cause. Property access on ERROR
26/// returns ERROR silently (no additional diagnostics emitted).
27///
28/// **Example uses:**
29/// - Missing type annotation: `let x;` -> ERROR (prevents "any poisoning")
30/// - Failed generic inference with no constraint/default
31/// - Invalid type syntax or unresolved type references
32///
33/// ## `TypeId::UNKNOWN`
34/// The TypeScript `unknown` type - a type-safe alternative to `any`.
35/// Use when the type is genuinely unknown at compile time, but should be
36/// checked before use.
37///
38/// **Strict behavior**: Property access on UNKNOWN returns `IsUnknown` result,
39/// which the checker reports as TS2571 "Object is of type 'unknown'".
40///
41/// **Example uses:**
42/// - Explicit `unknown` type annotation
43/// - Return type of functions that could return anything
44/// - Missing `this` parameter type (stricter than `any`)
45///
46/// ## `TypeId::ANY`
47/// The TypeScript `any` type - opts out of type checking entirely.
48/// Use for intentional any-typed values or interop with untyped code.
49///
50/// **Permissive behavior**: Property access on ANY succeeds and returns ANY.
51/// No type errors are produced for any-typed expressions.
52///
53/// **Example uses:**
54/// - Explicit `any` type annotation
55/// - Arrays with no element type context: `[]` defaults to `any[]`
56/// - Interop with JavaScript libraries without type definitions
57///
58/// ## `TypeId::NEVER`
59/// The bottom type - represents values that can never exist.
60/// Used for exhaustive checking and functions that never return.
61///
62/// **Example uses:**
63/// - Function that always throws or loops forever
64/// - Exhaustive switch/if narrowing (remaining type after all cases)
65/// - Intersection of incompatible types
66///
67/// ## Summary: When to Use Each
68///
69/// | Scenario | Use |
70/// |-----------------------------------|---------------|
71/// | Type resolution failed | `ERROR` |
72/// | Missing required type annotation | `ERROR` |
73/// | Failed inference (no fallback) | `ERROR` |
74/// | Explicit `unknown` annotation | `UNKNOWN` |
75/// | Missing `this` parameter type | `UNKNOWN` |
76/// | Explicit `any` annotation | `ANY` |
77/// | Empty array literal `[]` | `any[]` |
78/// | Function never returns | `NEVER` |
79/// | Exhaustive narrowing remainder | `NEVER` |
80#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Default)]
81pub struct TypeId(pub u32);
82
83impl TypeId {
84 /// Internal placeholder - no valid type.
85 pub const NONE: Self = Self(0);
86
87 /// Error sentinel - type resolution failed.
88 /// Propagates through operations to prevent cascading errors.
89 /// See struct-level docs for detailed semantics.
90 pub const ERROR: Self = Self(1);
91
92 /// The bottom type - represents values that can never exist.
93 /// Used for exhaustive checks and functions that never return.
94 pub const NEVER: Self = Self(2);
95
96 /// TypeScript's `unknown` type - type-safe top type.
97 /// Requires type narrowing before use. See struct-level docs.
98 pub const UNKNOWN: Self = Self(3);
99
100 /// TypeScript's `any` type - opts out of type checking.
101 /// All operations succeed, returning `any`. See struct-level docs.
102 pub const ANY: Self = Self(4);
103
104 /// The `void` type - used for functions with no meaningful return.
105 pub const VOID: Self = Self(5);
106
107 /// The `undefined` type - represents the undefined value.
108 pub const UNDEFINED: Self = Self(6);
109
110 /// The `null` type - represents the null value.
111 pub const NULL: Self = Self(7);
112
113 /// The `boolean` type - union of true | false.
114 pub const BOOLEAN: Self = Self(8);
115
116 /// The `number` type - all numeric values.
117 pub const NUMBER: Self = Self(9);
118
119 /// The `string` type - all string values.
120 pub const STRING: Self = Self(10);
121
122 /// The `bigint` type - arbitrary precision integers.
123 pub const BIGINT: Self = Self(11);
124
125 /// The `symbol` type - unique symbol values.
126 pub const SYMBOL: Self = Self(12);
127
128 /// The `object` type - any non-primitive value.
129 pub const OBJECT: Self = Self(13);
130
131 /// The literal type `true`.
132 pub const BOOLEAN_TRUE: Self = Self(14);
133
134 /// The literal type `false`.
135 pub const BOOLEAN_FALSE: Self = Self(15);
136
137 /// The `Function` type - any callable.
138 pub const FUNCTION: Self = Self(16);
139
140 /// Synthetic Promise base type for Promise<T> when Promise symbol is not resolved.
141 /// Used to allow `promise_like_return_type_argument` to extract T from await expressions.
142 pub const PROMISE_BASE: Self = Self(17);
143
144 /// Internal sentinel indicating that expression checking should be delegated
145 /// to `CheckerState` for complex cases that need full checker context.
146 /// This is NOT a real type and should never escape ExpressionChecker/CheckerState.
147 pub const DELEGATE: Self = Self(18);
148
149 /// Internal sentinel used to represent 'any' in strict mode (North Star Fix).
150 /// Behaves like 'any' but does NOT silence structural mismatches.
151 pub const STRICT_ANY: Self = Self(19);
152
153 /// First user-defined type ID (after built-in intrinsics)
154 pub const FIRST_USER: u32 = 100;
155
156 pub const fn is_intrinsic(self) -> bool {
157 self.0 < Self::FIRST_USER
158 }
159
160 pub fn is_error(self) -> bool {
161 self == Self::ERROR
162 }
163
164 pub fn is_any(self) -> bool {
165 self == Self::ANY
166 }
167
168 pub fn is_unknown(self) -> bool {
169 self == Self::UNKNOWN
170 }
171
172 pub fn is_never(self) -> bool {
173 self == Self::NEVER
174 }
175
176 /// Returns true if this type is nullish (null or undefined).
177 /// Useful for strict null checking logic.
178 #[inline]
179 pub fn is_nullish(self) -> bool {
180 self == Self::NULL || self == Self::UNDEFINED
181 }
182
183 /// Returns true if this type is nullable (null, undefined, or void).
184 /// VOID is considered nullable because it represents undefined in some contexts.
185 #[inline]
186 pub fn is_nullable(self) -> bool {
187 self == Self::NULL || self == Self::UNDEFINED || self == Self::VOID
188 }
189
190 /// Returns true if this type is a top type (any or unknown).
191 /// Top types are assignable from all other types.
192 #[inline]
193 pub fn is_top_type(self) -> bool {
194 self == Self::ANY || self == Self::UNKNOWN
195 }
196
197 /// Returns true if this type is any or unknown (types that accept anything).
198 /// Alias for `is_top_type` for clarity in some contexts.
199 #[inline]
200 pub fn is_any_or_unknown(self) -> bool {
201 self.is_top_type()
202 }
203
204 // =========================================================================
205 // Local/Global Partitioning (for ScopedTypeInterner GC)
206 // =========================================================================
207
208 /// Mask for the local bit (MSB of u32).
209 ///
210 /// Local IDs have MSB=1 (0x80000000+), Global IDs have MSB=0 (0x7FFFFFFF-).
211 /// This partitioning allows `ScopedTypeInterner` to create ephemeral types
212 /// that don't pollute the global `TypeId` space.
213 pub const LOCAL_MASK: u32 = 0x80000000;
214
215 /// Check if this `TypeId` is a local (ephemeral) type.
216 ///
217 /// Local types are created by `ScopedTypeInterner` and are only valid
218 /// for the current operation/request. They are automatically freed
219 /// when the `ScopedTypeInterner` is dropped.
220 ///
221 /// Returns `true` if MSB is set (0x80000000+).
222 pub const fn is_local(self) -> bool {
223 (self.0 & Self::LOCAL_MASK) != 0
224 }
225
226 /// Check if this `TypeId` is a global (persistent) type.
227 ///
228 /// Global types are managed by `TypeInterner` and persist for the lifetime
229 /// of the project/server. These include declarations and intrinsics.
230 ///
231 /// Returns `true` if MSB is clear (0x7FFFFFFF-).
232 pub const fn is_global(self) -> bool {
233 !self.is_local()
234 }
235}
236
237/// Cache key for type relation queries (subtype, assignability, etc.).
238///
239/// This key includes Lawyer-layer configuration flags to ensure that results
240/// computed under different rules (strict vs non-strict) don't contaminate each other.
241///
242/// ## Fields
243///
244/// - `source`: The source type being compared
245/// - `target`: The target type being compared
246/// - `relation`: Distinguishes between different relation types (0 = subtype, 1 = assignability, etc.)
247/// - `flags`: Bitmask for boolean compiler options (u16 to support current and future flags):
248/// - bit 0: `strict_null_checks`
249/// - bit 1: `strict_function_types`
250/// - bit 2: `exact_optional_property_types`
251/// - bit 3: `no_unchecked_indexed_access`
252/// - bit 4: `disable_method_bivariance` (Sound Mode)
253/// - bit 5: `allow_void_return`
254/// - bit 6: `allow_bivariant_rest`
255/// - bit 7: `allow_bivariant_param_count`
256/// - bits 8-15: Reserved for future flags (`strict_any_propagation`, `strict_structural_checking`, etc.)
257/// - `any_mode`: Controls how `any` is treated (0 = All, 1 = `TopLevelOnly`)
258#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
259pub struct RelationCacheKey {
260 pub source: TypeId,
261 pub target: TypeId,
262 pub relation: u8,
263 pub flags: u16,
264 pub any_mode: u8,
265}
266
267impl RelationCacheKey {
268 /// Relation type constants to prevent magic number errors.
269 pub const SUBTYPE: u8 = 0;
270 pub const ASSIGNABLE: u8 = 1;
271 pub const IDENTICAL: u8 = 2;
272
273 // Named flag constants for the `flags` bitmask.
274 // Each bit represents a compiler option that affects type relation results.
275 pub const FLAG_STRICT_NULL_CHECKS: u16 = 1 << 0;
276 pub const FLAG_STRICT_FUNCTION_TYPES: u16 = 1 << 1;
277 pub const FLAG_EXACT_OPTIONAL_PROPERTY_TYPES: u16 = 1 << 2;
278 pub const FLAG_NO_UNCHECKED_INDEXED_ACCESS: u16 = 1 << 3;
279 pub const FLAG_DISABLE_METHOD_BIVARIANCE: u16 = 1 << 4;
280 pub const FLAG_ALLOW_VOID_RETURN: u16 = 1 << 5;
281 pub const FLAG_ALLOW_BIVARIANT_REST: u16 = 1 << 6;
282 pub const FLAG_ALLOW_BIVARIANT_PARAM_COUNT: u16 = 1 << 7;
283
284 /// Create a new cache key for subtype checking.
285 pub const fn subtype(source: TypeId, target: TypeId, flags: u16, any_mode: u8) -> Self {
286 Self {
287 source,
288 target,
289 relation: Self::SUBTYPE,
290 flags,
291 any_mode,
292 }
293 }
294
295 /// Create a new cache key for assignability checking.
296 pub const fn assignability(source: TypeId, target: TypeId, flags: u16, any_mode: u8) -> Self {
297 Self {
298 source,
299 target,
300 relation: Self::ASSIGNABLE,
301 flags,
302 any_mode,
303 }
304 }
305}
306
307/// Priority levels for generic type inference constraints.
308///
309/// TypeScript uses a multi-pass inference algorithm where constraints are processed
310/// in priority order. Higher priority constraints (like explicit type annotations) are
311/// processed first, then lower priority constraints (like contextual types from return
312/// position) are processed in subsequent passes.
313///
314/// This prevents circular dependencies and `any` leakage in complex generic scenarios
315/// like `Array.prototype.map` or `Promise.then`.
316///
317/// ## Priority Order (Highest to Lowest)
318///
319/// 1. **`NakedTypeVariable`** - Direct type parameter with no constraints (highest)
320/// 2. **`HomomorphicMappedType`** - Mapped types that preserve structure
321/// 3. **`PartialHomomorphicMappedType`** - Partially homomorphic mapped types
322/// 4. **`MappedType`** - Generic mapped types
323/// 5. **`ContravariantConditional`** - Conditional types in contravariant position
324/// 6. **`ReturnType`** - Contextual type from return position (low priority)
325/// 7. **`LowPriority`** - Fallback inference (lowest)
326/// 8. **Circular** - Detected circular dependency (prevents infinite loops)
327///
328/// ## Example
329///
330/// ```typescript
331/// function map<U>(arr: T[], fn: (x: T) => U): U[];
332/// // When calling map(x => x.toString()):
333/// // 1. T is inferred from array element type (NakedTypeVariable)
334/// // 2. U is inferred from return type contextual type (ReturnType)
335/// // Processing T first prevents circular T <-> U dependency
336/// ```
337///
338/// Part of the Priority-Based Contextual Inference implementation.
339#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
340pub enum InferencePriority {
341 /// Naked type variable with no constraints (highest priority).
342 /// Example: `<T>` where T appears directly in parameter types.
343 NakedTypeVariable = 1 << 0,
344
345 /// Mapped type that preserves array/tuple structure.
346 /// Example: `Partial<T[]>` preserves array structure.
347 HomomorphicMappedType = 1 << 1,
348
349 /// Partially homomorphic mapped type.
350 /// Example: Mapped types with some mixed properties.
351 PartialHomomorphicMappedType = 1 << 2,
352
353 /// Generic mapped type.
354 /// Example: `{ [K in keyof T]: U }`
355 MappedType = 1 << 3,
356
357 /// Conditional type in contravariant position.
358 /// Example: Inference from function parameter types in conditional types.
359 ContravariantConditional = 1 << 4,
360
361 /// Contextual type from return position.
362 /// Example: `const x: number = fn()` where fn is generic.
363 ReturnType = 1 << 5,
364
365 /// Low priority fallback inference.
366 LowPriority = 1 << 6,
367
368 /// Detected circular dependency (prevents infinite loops).
369 /// Set when a type parameter depends on itself through constraints.
370 Circular = 1 << 7,
371}
372
373impl InferencePriority {
374 /// Check if this priority level should be processed in a given pass.
375 ///
376 /// Multi-pass inference processes constraints in increasing priority order.
377 /// Returns true if this priority matches or is lower than the current pass level.
378 pub fn should_process_in_pass(&self, current_pass: Self) -> bool {
379 *self >= current_pass && *self != Self::Circular
380 }
381
382 /// Get the next priority level for multi-pass inference.
383 pub const fn next_level(&self) -> Option<Self> {
384 match self {
385 Self::NakedTypeVariable => Some(Self::HomomorphicMappedType),
386 Self::HomomorphicMappedType => Some(Self::PartialHomomorphicMappedType),
387 Self::PartialHomomorphicMappedType => Some(Self::MappedType),
388 Self::MappedType => Some(Self::ContravariantConditional),
389 Self::ContravariantConditional => Some(Self::ReturnType),
390 Self::ReturnType => Some(Self::LowPriority),
391 Self::LowPriority | Self::Circular => None,
392 }
393 }
394
395 /// Default priority for normal constraint collection.
396 pub const NORMAL: Self = Self::ReturnType;
397
398 /// Highest priority for explicit type annotations.
399 pub const HIGHEST: Self = Self::NakedTypeVariable;
400
401 /// Lowest priority for fallback inference.
402 pub const LOWEST: Self = Self::LowPriority;
403}
404
405/// Interned list of `TypeId` values (e.g., unions/intersections).
406#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
407pub struct TypeListId(pub u32);
408
409/// Interned object shape (properties + index signatures).
410#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
411pub struct ObjectShapeId(pub u32);
412
413/// Interned tuple element list.
414#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
415pub struct TupleListId(pub u32);
416
417/// Interned function shape.
418#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
419pub struct FunctionShapeId(pub u32);
420
421/// Interned callable shape.
422#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
423pub struct CallableShapeId(pub u32);
424
425/// Interned type application (Base<Args>).
426#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
427pub struct TypeApplicationId(pub u32);
428
429/// Interned template literal span list.
430#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
431pub struct TemplateLiteralId(pub u32);
432
433/// Interned conditional type.
434#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
435pub struct ConditionalTypeId(pub u32);
436
437/// Interned mapped type.
438#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
439pub struct MappedTypeId(pub u32);
440
441/// Well-known Symbol property keys used in the iterator protocol.
442/// These are used to represent `[Symbol.iterator]` and `[Symbol.asyncIterator]` property names.
443#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
444pub enum WellKnownSymbolKey {
445 /// Symbol.iterator - used for sync iterables
446 Iterator,
447 /// Symbol.asyncIterator - used for async iterables
448 AsyncIterator,
449 /// Symbol.hasInstance - used for instanceof checks
450 HasInstance,
451 /// Symbol.isConcatSpreadable - used for array concat behavior
452 IsConcatSpreadable,
453 /// Symbol.match - used for String.match
454 Match,
455 /// Symbol.matchAll - used for String.matchAll
456 MatchAll,
457 /// Symbol.replace - used for String.replace
458 Replace,
459 /// Symbol.search - used for String.search
460 Search,
461 /// Symbol.split - used for String.split
462 Split,
463 /// Symbol.species - used for derived constructors
464 Species,
465 /// Symbol.toPrimitive - used for type coercion
466 ToPrimitive,
467 /// Symbol.toStringTag - used for Object.prototype.toString
468 ToStringTag,
469 /// Symbol.unscopables - used for with statement
470 Unscopables,
471 /// Symbol.dispose - used for using declarations
472 Dispose,
473 /// Symbol.asyncDispose - used for async using declarations
474 AsyncDispose,
475}
476
477impl WellKnownSymbolKey {
478 /// Returns the conventional string property name for this well-known symbol.
479 /// This follows the convention of using `"[Symbol.iterator]"` etc. as property names.
480 pub const fn as_property_name(&self) -> &'static str {
481 match self {
482 Self::Iterator => "[Symbol.iterator]",
483 Self::AsyncIterator => "[Symbol.asyncIterator]",
484 Self::HasInstance => "[Symbol.hasInstance]",
485 Self::IsConcatSpreadable => "[Symbol.isConcatSpreadable]",
486 Self::Match => "[Symbol.match]",
487 Self::MatchAll => "[Symbol.matchAll]",
488 Self::Replace => "[Symbol.replace]",
489 Self::Search => "[Symbol.search]",
490 Self::Split => "[Symbol.split]",
491 Self::Species => "[Symbol.species]",
492 Self::ToPrimitive => "[Symbol.toPrimitive]",
493 Self::ToStringTag => "[Symbol.toStringTag]",
494 Self::Unscopables => "[Symbol.unscopables]",
495 Self::Dispose => "[Symbol.dispose]",
496 Self::AsyncDispose => "[Symbol.asyncDispose]",
497 }
498 }
499
500 /// Parses a property name string into a well-known symbol key.
501 /// Returns `None` if the string is not a well-known symbol property name.
502 pub fn from_property_name(name: &str) -> Option<Self> {
503 match name {
504 "[Symbol.iterator]" => Some(Self::Iterator),
505 "[Symbol.asyncIterator]" => Some(Self::AsyncIterator),
506 "[Symbol.hasInstance]" => Some(Self::HasInstance),
507 "[Symbol.isConcatSpreadable]" => Some(Self::IsConcatSpreadable),
508 "[Symbol.match]" => Some(Self::Match),
509 "[Symbol.matchAll]" => Some(Self::MatchAll),
510 "[Symbol.replace]" => Some(Self::Replace),
511 "[Symbol.search]" => Some(Self::Search),
512 "[Symbol.split]" => Some(Self::Split),
513 "[Symbol.species]" => Some(Self::Species),
514 "[Symbol.toPrimitive]" => Some(Self::ToPrimitive),
515 "[Symbol.toStringTag]" => Some(Self::ToStringTag),
516 "[Symbol.unscopables]" => Some(Self::Unscopables),
517 "[Symbol.dispose]" => Some(Self::Dispose),
518 "[Symbol.asyncDispose]" => Some(Self::AsyncDispose),
519 _ => None,
520 }
521 }
522}
523
524/// The structural "shape" of a type.
525/// This is the key used for interning - structurally identical types
526/// will have the same `TypeData` and therefore the same `TypeId`.
527#[derive(Clone, Debug, PartialEq, Eq, Hash)]
528pub enum TypeData {
529 /// Intrinsic types (any, unknown, never, void, null, undefined, boolean, number, string, bigint, symbol, object)
530 Intrinsic(IntrinsicKind),
531
532 /// Literal types ("hello", 42, true, 123n)
533 Literal(LiteralValue),
534
535 /// Object type with sorted property list for structural identity
536 Object(ObjectShapeId),
537
538 /// Object type with index signatures
539 /// For objects like { [key: string]: number, foo: string }
540 ObjectWithIndex(ObjectShapeId),
541
542 /// Union type (A | B | C)
543 Union(TypeListId),
544
545 /// Intersection type (A & B & C)
546 Intersection(TypeListId),
547
548 /// Array type
549 Array(TypeId),
550
551 /// Tuple type
552 Tuple(TupleListId),
553
554 /// Function type
555 Function(FunctionShapeId),
556
557 /// Callable type with overloaded signatures
558 /// For interfaces with call/construct signatures
559 Callable(CallableShapeId),
560
561 /// Type parameter (generic)
562 TypeParameter(TypeParamInfo),
563
564 /// Bound type parameter using De Bruijn index for alpha-equivalence.
565 ///
566 /// Represents a type parameter relative to the current binding scope.
567 /// Used by the Canonicalizer to achieve alpha-equivalence, where
568 /// `type F<T> = T` and `type G<U> = U` are considered identical.
569 ///
570 /// ## Alpha-Equivalence (Task #32)
571 ///
572 /// When canonicalizing generic types, we replace named type parameters
573 /// with positional indices to achieve structural identity.
574 ///
575 /// ### Example
576 ///
577 /// ```typescript
578 /// type F<T> = { value: T }; // canonicalizes to Object({ value: BoundParameter(0) })
579 /// type G<U> = { value: U }; // also canonicalizes to Object({ value: BoundParameter(0) })
580 /// // Both get the same TypeId because they're structurally identical
581 /// ```
582 ///
583 /// ## De Bruijn Index Semantics
584 ///
585 /// - `BoundParameter(0)` = the most recently bound type parameter
586 /// - `BoundParameter(1)` = the second-most recently bound type parameter
587 /// - `BoundParameter(n)` = the (n+1)th-most recently bound type parameter
588 BoundParameter(u32),
589
590 /// Reference to a named type (interface, class, type alias)
591 /// Uses `SymbolId` to break infinite recursion
592 /// DEPRECATED: Use `Lazy(DefId)` for new code. This is kept for backward compatibility
593 /// during the migration from `SymbolRef` to `DefId`.
594 /// PHASE 4.2: REMOVED - Migration complete, all types now use Lazy(DefId)
595 // Ref(SymbolRef),
596
597 /// Lazy reference to a type definition.
598 ///
599 /// Unlike `Ref(SymbolRef)` which references Binder symbols, `Lazy(DefId)` uses
600 /// Solver-owned identifiers that:
601 /// - Don't require Binder context
602 /// - Support content-addressed hashing for LSP stability
603 /// - Enable Salsa integration for incremental compilation
604 ///
605 /// The type is evaluated lazily when first accessed, resolving to the actual
606 /// type stored in the `DefinitionStore`.
607 ///
608 /// ## Migration
609 ///
610 /// Eventually all `Ref(SymbolRef)` usages will be replaced with `Lazy(DefId)`.
611 Lazy(DefId),
612
613 /// Recursive type reference using De Bruijn index.
614 ///
615 /// Represents a back-reference to a type N levels up the nesting path.
616 /// This is used for canonicalizing recursive types to achieve O(1) equality.
617 ///
618 /// ## Graph Isomorphism (Task #32)
619 ///
620 /// When canonicalizing recursive type aliases, we replace cycles with
621 /// relative De Bruijn indices instead of absolute Lazy references.
622 ///
623 /// ### Example
624 ///
625 /// ```typescript
626 /// type A = { x: A }; // canonicalizes to Object({ x: Recursive(0) })
627 /// type B = { x: B }; // also canonicalizes to Object({ x: Recursive(0) })
628 /// // Both get the same TypeId because they're structurally identical
629 /// ```
630 ///
631 /// ## De Bruijn Index Semantics
632 ///
633 /// - `Recursive(0)` = the current type itself (immediate recursion)
634 /// - `Recursive(1)` = one level up (parent in the nesting chain)
635 /// - `Recursive(n)` = n levels up
636 ///
637 /// ## Nominal vs Structural
638 ///
639 /// This is ONLY used for structural types (type aliases). Nominal types
640 /// (classes, interfaces) preserve their Lazy(DefId) for nominal identity.
641 Recursive(u32),
642
643 /// Enum type with nominal identity and structural member types.
644 ///
645 /// Enums are nominal types - two different enums with the same member types
646 /// are NOT compatible (e.g., `enum E1 { A, B }` is not assignable to `enum E2 { A, B }`).
647 ///
648 /// - `DefId`: The unique identity of the enum (for E1 vs E2 nominal checking)
649 /// - `TypeId`: The structural union of member types (e.g., 0 | 1 for numeric enums),
650 /// used for assignability to primitives (e.g., E1 assignable to number)
651 Enum(DefId, TypeId),
652
653 /// Generic type application (Base<Args>)
654 Application(TypeApplicationId),
655
656 /// Conditional type (T extends U ? X : Y)
657 Conditional(ConditionalTypeId),
658
659 /// Mapped type ({ [K in Keys]: `ValueType` })
660 Mapped(MappedTypeId),
661
662 /// Index access type (T[K])
663 IndexAccess(TypeId, TypeId),
664
665 /// Template literal type (`hello${string}world`)
666 TemplateLiteral(TemplateLiteralId),
667
668 /// Type query (typeof expression in type position)
669 TypeQuery(SymbolRef),
670
671 /// `KeyOf` type operator (keyof T)
672 KeyOf(TypeId),
673
674 /// Readonly type modifier (readonly T[])
675 ReadonlyType(TypeId),
676
677 /// Unique symbol type
678 UniqueSymbol(SymbolRef),
679
680 /// Infer type (infer R in conditional types)
681 Infer(TypeParamInfo),
682
683 /// This type (polymorphic this)
684 ThisType,
685
686 /// String manipulation intrinsic types
687 /// Uppercase<T>, Lowercase<T>, Capitalize<T>, Uncapitalize<T>
688 StringIntrinsic {
689 kind: StringIntrinsicKind,
690 type_arg: TypeId,
691 },
692
693 /// Module namespace type (import * as ns from "module")
694 /// Uses `SymbolRef` for lazy evaluation to avoid circular dependency issues
695 ModuleNamespace(SymbolRef),
696
697 /// `NoInfer`<T> utility type (TypeScript 5.4+)
698 /// Prevents inference from flowing through this type position.
699 /// During inference, this blocks inference. During evaluation/subtyping,
700 /// it evaluates to the inner type (transparent).
701 NoInfer(TypeId),
702
703 /// Error type for recovery
704 Error,
705}
706
707/// Generic type application (Base<Args>)
708#[derive(Clone, Debug, PartialEq, Eq, Hash)]
709pub struct TypeApplication {
710 pub base: TypeId,
711 pub args: Vec<TypeId>,
712}
713
714/// Intrinsic type kinds
715#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
716pub enum IntrinsicKind {
717 Any,
718 Unknown,
719 Never,
720 Void,
721 Null,
722 Undefined,
723 Boolean,
724 Number,
725 String,
726 Bigint,
727 Symbol,
728 Object,
729 Function,
730}
731
732/// String manipulation intrinsic kinds
733#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
734pub enum StringIntrinsicKind {
735 Uppercase,
736 Lowercase,
737 Capitalize,
738 Uncapitalize,
739}
740
741impl IntrinsicKind {
742 pub const fn to_type_id(self) -> TypeId {
743 match self {
744 Self::Any => TypeId::ANY,
745 Self::Unknown => TypeId::UNKNOWN,
746 Self::Never => TypeId::NEVER,
747 Self::Void => TypeId::VOID,
748 Self::Null => TypeId::NULL,
749 Self::Undefined => TypeId::UNDEFINED,
750 Self::Boolean => TypeId::BOOLEAN,
751 Self::Number => TypeId::NUMBER,
752 Self::String => TypeId::STRING,
753 Self::Bigint => TypeId::BIGINT,
754 Self::Symbol => TypeId::SYMBOL,
755 Self::Object => TypeId::OBJECT,
756 Self::Function => TypeId::FUNCTION,
757 }
758 }
759}
760
761/// Literal values (for literal types)
762#[derive(Clone, Debug, PartialEq, Eq, Hash)]
763pub enum LiteralValue {
764 String(Atom),
765 Number(OrderedFloat),
766 BigInt(Atom),
767 Boolean(bool),
768}
769
770impl LiteralValue {
771 /// Returns the primitive `TypeId` that this literal widens to.
772 ///
773 /// - `String(_)` → `TypeId::STRING`
774 /// - `Number(_)` → `TypeId::NUMBER`
775 /// - `Boolean(_)` → `TypeId::BOOLEAN`
776 /// - `BigInt(_)` → `TypeId::BIGINT`
777 pub const fn primitive_type_id(&self) -> TypeId {
778 match self {
779 Self::String(_) => TypeId::STRING,
780 Self::Number(_) => TypeId::NUMBER,
781 Self::Boolean(_) => TypeId::BOOLEAN,
782 Self::BigInt(_) => TypeId::BIGINT,
783 }
784 }
785}
786
787/// Wrapper for f64 that implements Eq and Hash for use in `TypeData`
788#[derive(Clone, Copy, Debug)]
789pub struct OrderedFloat(pub f64);
790
791impl PartialEq for OrderedFloat {
792 fn eq(&self, other: &Self) -> bool {
793 self.0.to_bits() == other.0.to_bits()
794 }
795}
796
797impl Eq for OrderedFloat {}
798
799impl std::hash::Hash for OrderedFloat {
800 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
801 self.0.to_bits().hash(state);
802 }
803}
804
805/// Visibility modifier for class properties
806#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Default)]
807pub enum Visibility {
808 /// Public property - structural compatibility applies
809 #[default]
810 Public,
811 /// Private property - nominal compatibility only
812 Private,
813 /// Protected property - nominal compatibility only
814 Protected,
815}
816
817/// Property information for object types
818#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
819pub struct PropertyInfo {
820 pub name: Atom,
821 /// Read type (getter/lookup).
822 pub type_id: TypeId,
823 /// Write type (setter/assignment).
824 pub write_type: TypeId,
825 pub optional: bool,
826 pub readonly: bool,
827 pub is_method: bool,
828 /// Visibility modifier for nominal subtyping
829 pub visibility: Visibility,
830 /// Symbol that declared this property (for nominal identity checks)
831 pub parent_id: Option<SymbolId>,
832}
833
834impl PropertyInfo {
835 /// Create a property with default settings (non-optional, non-readonly, public).
836 /// Sets `write_type` equal to `type_id`.
837 pub const fn new(name: Atom, type_id: TypeId) -> Self {
838 Self {
839 name,
840 type_id,
841 write_type: type_id,
842 optional: false,
843 readonly: false,
844 is_method: false,
845 visibility: Visibility::Public,
846 parent_id: None,
847 }
848 }
849
850 /// Create a method property with default settings.
851 pub const fn method(name: Atom, type_id: TypeId) -> Self {
852 Self {
853 is_method: true,
854 ..Self::new(name, type_id)
855 }
856 }
857
858 /// Create an optional property with default settings.
859 pub const fn opt(name: Atom, type_id: TypeId) -> Self {
860 Self {
861 optional: true,
862 ..Self::new(name, type_id)
863 }
864 }
865
866 /// Create a readonly property with default settings.
867 pub const fn readonly(name: Atom, type_id: TypeId) -> Self {
868 Self {
869 readonly: true,
870 ..Self::new(name, type_id)
871 }
872 }
873
874 /// Find a property by name in a slice of properties.
875 pub fn find_in_slice(props: &[Self], name: Atom) -> Option<&Self> {
876 props.iter().find(|p| p.name == name)
877 }
878}
879
880#[derive(Copy, Clone, Debug, PartialEq, Eq)]
881pub enum PropertyLookup {
882 Found(usize),
883 NotFound,
884 Uncached,
885}
886
887/// Index signature information for object types
888/// Represents `{ [key: string]: ValueType }` or `{ [key: number]: ValueType }`
889#[derive(Clone, Debug, PartialEq, Eq, Hash)]
890pub struct IndexSignature {
891 /// The key type (usually string or number)
892 pub key_type: TypeId,
893 /// The value type for all indexed properties
894 pub value_type: TypeId,
895 /// Whether the index signature is readonly
896 pub readonly: bool,
897}
898
899/// Combined index signature information for a type
900/// Provides convenient access to both string and number index signatures
901#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
902pub struct IndexInfo {
903 /// String index signature: { [key: string]: T }
904 pub string_index: Option<IndexSignature>,
905 /// Number index signature: { [key: number]: T }
906 pub number_index: Option<IndexSignature>,
907}
908
909bitflags::bitflags! {
910 #[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug)]
911 pub struct ObjectFlags: u32 {
912 const FRESH_LITERAL = 1 << 0;
913 }
914}
915
916/// Object type with properties and optional index signatures
917///
918/// NOTE: The `symbol` field affects BOTH Hash and `PartialEq` for nominal discrimination.
919/// This ensures that different classes get different `TypeIds` in the interner.
920/// Structural subtyping is computed explicitly in the Solver, not via `PartialEq`.
921#[derive(Clone, Debug)]
922pub struct ObjectShape {
923 /// Object-level flags (e.g. fresh literal tracking).
924 pub flags: ObjectFlags,
925 /// Named properties (sorted by name for consistent hashing)
926 pub properties: Vec<PropertyInfo>,
927 /// String index signature: { [key: string]: T }
928 pub string_index: Option<IndexSignature>,
929 /// Number index signature: { [key: number]: T }
930 pub number_index: Option<IndexSignature>,
931 /// Nominal identity for class instance types (prevents structural interning of distinct classes)
932 pub symbol: Option<tsz_binder::SymbolId>,
933}
934
935impl PartialEq for ObjectShape {
936 fn eq(&self, other: &Self) -> bool {
937 // Include symbol in equality check to ensure different classes get different TypeIds
938 // The Solver does structural subtyping explicitly, not via PartialEq
939 self.flags == other.flags
940 && self.properties == other.properties
941 && self.string_index == other.string_index
942 && self.number_index == other.number_index
943 && self.symbol == other.symbol
944 }
945}
946
947impl Eq for ObjectShape {}
948
949impl std::hash::Hash for ObjectShape {
950 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
951 // Include the `symbol` field in hash for nominal interning
952 // This ensures different classes get different TypeIds
953 self.flags.hash(state);
954 self.properties.hash(state);
955 self.string_index.hash(state);
956 self.number_index.hash(state);
957 self.symbol.hash(state);
958 }
959}
960
961impl Default for ObjectShape {
962 fn default() -> Self {
963 Self {
964 flags: ObjectFlags::empty(),
965 properties: Vec::new(),
966 string_index: None,
967 number_index: None,
968 symbol: None,
969 }
970 }
971}
972
973/// Tuple element information
974#[derive(Clone, Debug, PartialEq, Eq, Hash)]
975pub struct TupleElement {
976 pub type_id: TypeId,
977 pub name: Option<Atom>,
978 pub optional: bool,
979 pub rest: bool,
980}
981
982/// Type predicate information (x is T / asserts x is T).
983#[derive(Clone, Debug, PartialEq, Eq, Hash)]
984pub struct TypePredicate {
985 pub asserts: bool,
986 pub target: TypePredicateTarget,
987 pub type_id: Option<TypeId>,
988 pub parameter_index: Option<usize>,
989}
990
991#[derive(Clone, Debug, PartialEq, Eq, Hash)]
992pub enum TypePredicateTarget {
993 This,
994 Identifier(Atom),
995}
996
997/// Function shape for function types
998#[derive(Clone, Debug, PartialEq, Eq, Hash)]
999pub struct FunctionShape {
1000 pub type_params: Vec<TypeParamInfo>,
1001 pub params: Vec<ParamInfo>,
1002 pub this_type: Option<TypeId>,
1003 pub return_type: TypeId,
1004 pub type_predicate: Option<TypePredicate>,
1005 pub is_constructor: bool,
1006 /// Whether this function is a method (bivariant parameters) vs a standalone function (contravariant when strictFunctionTypes)
1007 pub is_method: bool,
1008}
1009
1010impl FunctionShape {
1011 /// Create a simple function shape with params and return type.
1012 /// No type params, no this, no predicate, not a constructor or method.
1013 pub const fn new(params: Vec<ParamInfo>, return_type: TypeId) -> Self {
1014 Self {
1015 type_params: Vec::new(),
1016 params,
1017 this_type: None,
1018 return_type,
1019 type_predicate: None,
1020 is_constructor: false,
1021 is_method: false,
1022 }
1023 }
1024}
1025
1026/// Call signature for overloaded functions
1027/// Represents a single call signature in an overloaded type
1028#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1029pub struct CallSignature {
1030 pub type_params: Vec<TypeParamInfo>,
1031 pub params: Vec<ParamInfo>,
1032 pub this_type: Option<TypeId>,
1033 pub return_type: TypeId,
1034 pub type_predicate: Option<TypePredicate>,
1035 /// Whether this call signature is from a method (uses bivariant parameter checking).
1036 /// Methods in TypeScript are intentionally bivariant for compatibility reasons.
1037 pub is_method: bool,
1038}
1039
1040impl CallSignature {
1041 /// Create a simple call signature with params and return type.
1042 pub const fn new(params: Vec<ParamInfo>, return_type: TypeId) -> Self {
1043 Self {
1044 type_params: Vec::new(),
1045 params,
1046 this_type: None,
1047 return_type,
1048 type_predicate: None,
1049 is_method: false,
1050 }
1051 }
1052}
1053
1054/// Callable type with multiple overloaded call signatures
1055/// Represents types like:
1056/// ```typescript
1057/// interface Overloaded {
1058/// (x: string): number;
1059/// (x: number): string;
1060/// }
1061/// ```
1062/// NOTE: The `symbol` field affects BOTH Hash and `PartialEq` for nominal discrimination.
1063/// This ensures that different classes get different `TypeIds` in the interner.
1064/// Structural subtyping is computed explicitly in the Solver, not via `PartialEq`.
1065#[derive(Clone, Debug, Default)]
1066pub struct CallableShape {
1067 /// Call signatures (order matters for overload resolution)
1068 pub call_signatures: Vec<CallSignature>,
1069 /// Constructor signatures
1070 pub construct_signatures: Vec<CallSignature>,
1071 /// Optional properties on the callable (e.g., Function.prototype)
1072 pub properties: Vec<PropertyInfo>,
1073 /// String index signature (for static index signatures on classes)
1074 pub string_index: Option<IndexSignature>,
1075 /// Number index signature (for static index signatures on classes)
1076 pub number_index: Option<IndexSignature>,
1077 /// Nominal identity for class constructors (prevents structural interning of distinct classes)
1078 pub symbol: Option<tsz_binder::SymbolId>,
1079}
1080
1081impl PartialEq for CallableShape {
1082 fn eq(&self, other: &Self) -> bool {
1083 // Include symbol in equality check to ensure different classes get different TypeIds
1084 // The Solver does structural subtyping explicitly, not via PartialEq
1085 self.call_signatures == other.call_signatures
1086 && self.construct_signatures == other.construct_signatures
1087 && self.properties == other.properties
1088 && self.string_index == other.string_index
1089 && self.number_index == other.number_index
1090 && self.symbol == other.symbol
1091 }
1092}
1093
1094impl Eq for CallableShape {}
1095
1096impl std::hash::Hash for CallableShape {
1097 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1098 // Include the `symbol` field in hash for nominal interning
1099 // This ensures different classes get different TypeIds
1100 self.call_signatures.hash(state);
1101 self.construct_signatures.hash(state);
1102 self.properties.hash(state);
1103 self.string_index.hash(state);
1104 self.number_index.hash(state);
1105 self.symbol.hash(state);
1106 }
1107}
1108
1109/// Parameter information
1110#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
1111pub struct ParamInfo {
1112 pub name: Option<Atom>,
1113 pub type_id: TypeId,
1114 pub optional: bool,
1115 pub rest: bool,
1116}
1117
1118impl ParamInfo {
1119 /// Create a required parameter.
1120 pub const fn required(name: Atom, type_id: TypeId) -> Self {
1121 Self {
1122 name: Some(name),
1123 type_id,
1124 optional: false,
1125 rest: false,
1126 }
1127 }
1128
1129 /// Create an optional parameter.
1130 pub const fn optional(name: Atom, type_id: TypeId) -> Self {
1131 Self {
1132 optional: true,
1133 ..Self::required(name, type_id)
1134 }
1135 }
1136
1137 /// Create a rest parameter.
1138 pub const fn rest(name: Atom, type_id: TypeId) -> Self {
1139 Self {
1140 rest: true,
1141 ..Self::required(name, type_id)
1142 }
1143 }
1144
1145 /// Create an unnamed required parameter.
1146 pub const fn unnamed(type_id: TypeId) -> Self {
1147 Self {
1148 name: None,
1149 type_id,
1150 optional: false,
1151 rest: false,
1152 }
1153 }
1154}
1155
1156/// Type parameter information
1157#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1158pub struct TypeParamInfo {
1159 pub name: Atom,
1160 pub constraint: Option<TypeId>,
1161 pub default: Option<TypeId>,
1162 /// Whether this is a const type parameter (TS 5.0+)
1163 /// Const type parameters preserve literal types and infer readonly modifiers
1164 pub is_const: bool,
1165}
1166
1167/// Reference to a symbol (for named types)
1168#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1169pub struct SymbolRef(pub u32);
1170
1171/// Conditional type structure
1172#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1173pub struct ConditionalType {
1174 pub check_type: TypeId,
1175 pub extends_type: TypeId,
1176 pub true_type: TypeId,
1177 pub false_type: TypeId,
1178 pub is_distributive: bool,
1179}
1180
1181/// Mapped type structure
1182#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1183pub struct MappedType {
1184 pub type_param: TypeParamInfo,
1185 pub constraint: TypeId,
1186 pub name_type: Option<TypeId>,
1187 pub template: TypeId,
1188 pub readonly_modifier: Option<MappedModifier>,
1189 pub optional_modifier: Option<MappedModifier>,
1190}
1191
1192/// Mapped type modifier (+/-)
1193#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1194pub enum MappedModifier {
1195 Add,
1196 Remove,
1197}
1198
1199/// Template literal span
1200#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1201pub enum TemplateSpan {
1202 Text(Atom),
1203 Type(TypeId),
1204}
1205
1206impl TemplateSpan {
1207 /// Check if this span is a text span
1208 pub const fn is_text(&self) -> bool {
1209 matches!(self, Self::Text(_))
1210 }
1211
1212 /// Check if this span is a type interpolation
1213 pub const fn is_type(&self) -> bool {
1214 matches!(self, Self::Type(_))
1215 }
1216
1217 /// Get the text content if this is a text span
1218 pub const fn as_text(&self) -> Option<Atom> {
1219 match self {
1220 Self::Text(atom) => Some(*atom),
1221 _ => None,
1222 }
1223 }
1224
1225 /// Get the type ID if this is a type span
1226 pub const fn as_type(&self) -> Option<TypeId> {
1227 match self {
1228 Self::Type(type_id) => Some(*type_id),
1229 _ => None,
1230 }
1231 }
1232
1233 /// Create a type span
1234 pub const fn type_from_id(type_id: TypeId) -> Self {
1235 Self::Type(type_id)
1236 }
1237}
1238
1239/// Process escape sequences in a template literal string
1240/// Handles: \${, \\, \n, \r, \t, \b, \f, \v, \0, \xXX, \uXXXX, \u{X...}
1241pub fn process_template_escape_sequences(input: &str) -> String {
1242 let mut result = String::with_capacity(input.len());
1243 let mut chars = input.chars();
1244 let mut last_was_backslash = false;
1245
1246 while let Some(c) = chars.next() {
1247 if last_was_backslash {
1248 last_was_backslash = false;
1249 match c {
1250 '$' => {
1251 // \$${ becomes $ (not an interpolation)
1252 result.push('$');
1253 }
1254 '\\' => result.push('\\'),
1255 'n' => result.push('\n'),
1256 'r' => result.push('\r'),
1257 't' => result.push('\t'),
1258 'b' => result.push('\x08'),
1259 'f' => result.push('\x0c'),
1260 'v' => result.push('\x0b'),
1261 '0' => result.push('\0'),
1262 'x' => {
1263 // \xXX - exactly 2 hex digits
1264 let hex1 = chars.next().unwrap_or('0');
1265 let hex2 = chars.next().unwrap_or('0');
1266 let code = u8::from_str_radix(&format!("{hex1}{hex2}"), 16).unwrap_or(0);
1267 result.push(code as char);
1268 }
1269 'u' => {
1270 // \uXXXX or \u{X...}
1271 if let Some('{') = chars.next() {
1272 // \u{X...} - Unicode code point
1273 let mut code_str = String::new();
1274 for nc in chars.by_ref() {
1275 if nc == '}' {
1276 break;
1277 }
1278 code_str.push(nc);
1279 }
1280 if let Ok(code) = u32::from_str_radix(&code_str, 16)
1281 && let Some(c) = char::from_u32(code)
1282 {
1283 result.push(c);
1284 }
1285 } else {
1286 // \uXXXX - exactly 4 hex digits
1287 let mut code_str = String::new();
1288 for _ in 0..4 {
1289 if let Some(nc) = chars.next() {
1290 code_str.push(nc);
1291 }
1292 }
1293 if let Ok(code) = u16::from_str_radix(&code_str, 16)
1294 && let Some(c) = char::from_u32(code as u32)
1295 {
1296 result.push(c);
1297 }
1298 }
1299 }
1300 _ => {
1301 // Unknown escape - preserve the backslash and character
1302 result.push('\\');
1303 result.push(c);
1304 }
1305 }
1306 } else if c == '\\' {
1307 last_was_backslash = true;
1308 } else {
1309 result.push(c);
1310 }
1311 }
1312
1313 // Handle trailing backslash
1314 if last_was_backslash {
1315 result.push('\\');
1316 }
1317
1318 result
1319}
1320
1321/// Returns true if the type name corresponds to a built-in type that should
1322/// be represented structurally or intrinsically, rather than by reference.
1323///
1324/// ## Built-in vs Referenced Types
1325///
1326/// **Built-in types** (managed by the compiler) are represented directly by their
1327/// structure (e.g., `TypeData::Array`) rather than by symbol reference (`TypeData::Ref`).
1328/// This ensures canonicalization: `Array<number>` and `number[]` resolve to the same type.
1329///
1330/// **Referenced types** (user-defined and lib types) are represented as `TypeData::Ref(symbol_id)`
1331/// and resolved lazily during type checking through the `TypeEnvironment`.
1332///
1333/// ## Examples
1334///
1335/// - `Array<T>` → `TypeData::Array(T)` (structural, not `Ref`)
1336/// - `Uppercase<S>` → `TypeData::StringIntrinsic { kind: Uppercase, ... }`
1337/// - `MyInterface` → `TypeData::Ref(SymbolRef(sym_id))`
1338///
1339/// ## When to Add Types
1340///
1341/// Add a type to this list if:
1342/// 1. It has special structural representation in `TypeData` (e.g., `Array`)
1343/// 2. It is a compiler intrinsic (e.g., `Uppercase`, `Lowercase`)
1344/// 3. It needs canonicalization with alternative syntax (e.g., `T[]` vs `Array<T>`)
1345///
1346/// **DO NOT** add:
1347/// - Regular lib types like `Promise`, `Map`, `Set` (these use `Ref`)
1348/// - User-defined interfaces or type aliases
1349pub fn is_compiler_managed_type(name: &str) -> bool {
1350 matches!(
1351 name,
1352 "Array" | // Canonicalizes with T[] syntax
1353 "ReadonlyArray" | // Built-in readonly array type
1354 "Uppercase" | // String intrinsic
1355 "Lowercase" | // String intrinsic
1356 "Capitalize" | // String intrinsic
1357 "Uncapitalize" // String intrinsic
1358 )
1359}
1360
1361// =============================================================================
1362// Variance Types (Task #41)
1363// =============================================================================
1364
1365bitflags::bitflags! {
1366 /// Variance of a type parameter in a generic type.
1367 ///
1368 /// Variance determines how subtyping of generic types relates to subtyping
1369 /// of their type arguments. This is critical for O(1) generic assignability.
1370 ///
1371 /// ## Variance Kinds
1372 ///
1373 /// - **Covariant** (COVARIANT): T<U> <: T<V> iff U <: V
1374 /// - Example: `Array`, `ReadonlyArray`, `Promise`
1375 /// - Most common for immutable containers
1376 ///
1377 /// - **Contravariant** (CONTRAVARIANT): T<U> <: T<V> iff V <: U (reversed)
1378 /// - Example: Function parameters (in strict mode)
1379 /// - Rare in practice, mostly for function types
1380 ///
1381 /// - **Invariant** (COVARIANT | CONTRAVARIANT): T<U> <: T<V> iff U === V
1382 /// - Example: Mutable properties, `Box<T>` with read/write
1383 /// - Requires both directions to hold
1384 ///
1385 /// - **Independent** (empty): Type parameter not used in variance position
1386 /// - Example: Type parameter only used in non-variance positions
1387 /// - Can be skipped in subtype checks (always compatible)
1388 ///
1389 /// ## Examples
1390 ///
1391 /// ```typescript
1392 /// // Covariant: Array< Dog > <: Array< Animal >
1393 /// type Covariant<T> = { readonly get(): T };
1394 ///
1395 /// // Contravariant: Writer< Animal > <: Writer< Dog >
1396 /// type Contravariant<T> = { write(x: T): void };
1397 ///
1398 /// // Invariant: Box<Dog> NOT <: Box<Animal> (mutable!)
1399 /// type Invariant<T> = { get(): T; set(x: T): void };
1400 /// ```
1401 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
1402 pub struct Variance: u8 {
1403 /// Covariant position (e.g., function return types)
1404 const COVARIANT = 1 << 0;
1405 /// Contravariant position (e.g., function parameters)
1406 const CONTRAVARIANT = 1 << 1;
1407 }
1408}
1409
1410impl Variance {
1411 /// Check if this is an independent type parameter (not used in variance position).
1412 pub const fn is_independent(&self) -> bool {
1413 self.is_empty()
1414 }
1415
1416 /// Check if this is covariant only.
1417 pub const fn is_covariant(&self) -> bool {
1418 self.contains(Self::COVARIANT) && !self.contains(Self::CONTRAVARIANT)
1419 }
1420
1421 /// Check if this is contravariant only.
1422 pub const fn is_contravariant(&self) -> bool {
1423 self.contains(Self::CONTRAVARIANT) && !self.contains(Self::COVARIANT)
1424 }
1425
1426 /// Check if this is invariant (both covariant and contravariant).
1427 pub fn is_invariant(&self) -> bool {
1428 self.contains(Self::COVARIANT | Self::CONTRAVARIANT)
1429 }
1430
1431 /// Compose two variances (for nested generics).
1432 ///
1433 /// Rules:
1434 /// - Independent × anything = Independent
1435 /// - Covariant × Covariant = Covariant
1436 /// - Covariant × Contravariant = Contravariant
1437 /// - Contravariant × Covariant = Contravariant
1438 /// - Contravariant × Contravariant = Covariant
1439 /// - Invariant × anything = Invariant
1440 pub fn compose(&self, other: Self) -> Self {
1441 if self.is_invariant() || other.is_invariant() {
1442 return Self::COVARIANT | Self::CONTRAVARIANT;
1443 }
1444 if self.is_independent() || other.is_independent() {
1445 return Self::empty();
1446 }
1447
1448 // XOR for covariance composition
1449 let is_covariant = self.is_covariant() == other.is_covariant();
1450 let is_contravariant = !is_covariant;
1451
1452 let mut result = Self::empty();
1453 if is_covariant {
1454 result |= Self::COVARIANT;
1455 }
1456 if is_contravariant {
1457 result |= Self::CONTRAVARIANT;
1458 }
1459 result
1460 }
1461}
1462
1463#[cfg(test)]
1464#[path = "../tests/types_tests.rs"]
1465mod tests;