Skip to main content

tsz_solver/evaluation/
evaluate.rs

1//! Type evaluation for meta-types (conditional, mapped, index access).
2//!
3//! Meta-types are "type-level functions" that compute output types from input types.
4//! This module provides evaluation logic for:
5//! - Conditional types: T extends U ? X : Y
6//! - Distributive conditional types: (A | B) extends U ? X : Y
7//! - Index access types: T[K]
8//!
9//! Key design:
10//! - Lazy evaluation: only evaluate when needed for subtype checking
11//! - Handles deferred evaluation when type parameters are unknown
12//! - Supports distributivity for naked type parameters in unions
13
14use crate::TypeDatabase;
15use crate::caches::db::QueryDatabase;
16use crate::def::DefId;
17use crate::instantiation::instantiate::instantiate_generic;
18use crate::relations::subtype::{NoopResolver, TypeResolver};
19#[cfg(test)]
20use crate::types::*;
21use crate::types::{
22    ConditionalType, ConditionalTypeId, MappedType, MappedTypeId, StringIntrinsicKind,
23    TemplateLiteralId, TemplateSpan, TypeApplicationId, TypeData, TypeId, TypeListId,
24    TypeParamInfo,
25};
26use rustc_hash::{FxHashMap, FxHashSet};
27
28/// Result of conditional type evaluation
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub enum ConditionalResult {
31    /// The condition was resolved to a definite type
32    Resolved(TypeId),
33    /// The condition could not be resolved (deferred)
34    /// This happens when `check_type` is a type parameter that hasn't been substituted
35    Deferred(TypeId),
36}
37
38/// Maximum number of unique types to track in the visiting set.
39/// Prevents unbounded memory growth in pathological cases.
40pub const MAX_VISITING_SET_SIZE: usize = 10_000;
41
42/// Controls which subtype direction makes a member redundant when simplifying
43/// a union or intersection.
44enum SubtypeDirection {
45    /// member[i] <: member[j] → member[i] is redundant (union semantics).
46    SourceSubsumedByOther,
47    /// member[j] <: member[i] → member[i] is redundant (intersection semantics).
48    OtherSubsumedBySource,
49}
50
51/// Type evaluator for meta-types.
52///
53/// # Salsa Preparation
54/// This struct uses `&mut self` methods instead of `RefCell` + `&self`.
55/// This makes the evaluator thread-safe (Send) and prepares for future
56/// Salsa integration where state is managed by the database runtime.
57pub struct TypeEvaluator<'a, R: TypeResolver = NoopResolver> {
58    interner: &'a dyn TypeDatabase,
59    /// Optional query database for Salsa-backed memoization.
60    query_db: Option<&'a dyn QueryDatabase>,
61    resolver: &'a R,
62    no_unchecked_indexed_access: bool,
63    cache: FxHashMap<TypeId, TypeId>,
64    /// Unified recursion guard for `TypeId` cycle detection, depth, and iteration limits.
65    guard: crate::recursion::RecursionGuard<TypeId>,
66    /// Per-DefId recursion depth counter.
67    /// Allows recursive type aliases (like `TrimRight`) to expand up to `MAX_DEF_DEPTH`
68    /// times before stopping, matching tsc's TS2589 "Type instantiation is excessively
69    /// deep and possibly infinite" behavior. Unlike a set-based cycle detector, this
70    /// permits legitimate bounded recursion where each expansion converges.
71    def_depth: FxHashMap<DefId, u32>,
72}
73
74/// Array methods that return any (used for apparent type computation).
75pub const ARRAY_METHODS_RETURN_ANY: &[&str] = &[
76    "concat",
77    "filter",
78    "flat",
79    "flatMap",
80    "map",
81    "reverse",
82    "slice",
83    "sort",
84    "splice",
85    "toReversed",
86    "toSorted",
87    "toSpliced",
88    "with",
89    "at",
90    "find",
91    "findLast",
92    "pop",
93    "shift",
94    "entries",
95    "keys",
96    "values",
97    "reduce",
98    "reduceRight",
99];
100/// Array methods that return boolean.
101pub const ARRAY_METHODS_RETURN_BOOLEAN: &[&str] = &["every", "includes", "some"];
102/// Array methods that return number.
103pub const ARRAY_METHODS_RETURN_NUMBER: &[&str] = &[
104    "findIndex",
105    "findLastIndex",
106    "indexOf",
107    "lastIndexOf",
108    "push",
109    "unshift",
110];
111/// Array methods that return void.
112pub const ARRAY_METHODS_RETURN_VOID: &[&str] = &["forEach", "copyWithin", "fill"];
113/// Array methods that return string.
114pub const ARRAY_METHODS_RETURN_STRING: &[&str] = &["join", "toLocaleString", "toString"];
115
116impl<'a> TypeEvaluator<'a, NoopResolver> {
117    /// Create a new evaluator without a resolver.
118    pub fn new(interner: &'a dyn TypeDatabase) -> Self {
119        static NOOP: NoopResolver = NoopResolver;
120        TypeEvaluator {
121            interner,
122            query_db: None,
123            resolver: &NOOP,
124            no_unchecked_indexed_access: false,
125            cache: FxHashMap::default(),
126            guard: crate::recursion::RecursionGuard::with_profile(
127                crate::recursion::RecursionProfile::TypeEvaluation,
128            ),
129            def_depth: FxHashMap::default(),
130        }
131    }
132}
133
134impl<'a, R: TypeResolver> TypeEvaluator<'a, R> {
135    /// Maximum recursive expansion depth for a single `DefId`.
136    /// Matches TypeScript's instantiation depth limit that triggers TS2589.
137    const MAX_DEF_DEPTH: u32 = 50;
138
139    /// Create a new evaluator with a custom resolver.
140    pub fn with_resolver(interner: &'a dyn TypeDatabase, resolver: &'a R) -> Self {
141        TypeEvaluator {
142            interner,
143            query_db: None,
144            resolver,
145            no_unchecked_indexed_access: false,
146            cache: FxHashMap::default(),
147            guard: crate::recursion::RecursionGuard::with_profile(
148                crate::recursion::RecursionProfile::TypeEvaluation,
149            ),
150            def_depth: FxHashMap::default(),
151        }
152    }
153
154    /// Set the query database for Salsa-backed memoization.
155    pub fn with_query_db(mut self, db: &'a dyn QueryDatabase) -> Self {
156        self.query_db = Some(db);
157        self
158    }
159
160    pub fn set_no_unchecked_indexed_access(&mut self, enabled: bool) {
161        if self.no_unchecked_indexed_access != enabled {
162            self.cache.clear();
163        }
164        self.no_unchecked_indexed_access = enabled;
165    }
166
167    /// Reset per-evaluation state so this evaluator can be reused.
168    ///
169    /// Clears the cache, cycle detection sets, and counters while preserving
170    /// configuration and borrowed references. Uses `.clear()` to reuse memory.
171    #[inline]
172    pub fn reset(&mut self) {
173        self.cache.clear();
174        self.guard.reset();
175        self.def_depth.clear();
176    }
177
178    // =========================================================================
179    // Accessor methods for evaluate_rules modules
180    // =========================================================================
181
182    /// Get the type interner.
183    #[inline]
184    pub(crate) fn interner(&self) -> &'a dyn TypeDatabase {
185        self.interner
186    }
187
188    /// Get the type resolver.
189    #[inline]
190    pub(crate) const fn resolver(&self) -> &'a R {
191        self.resolver
192    }
193
194    /// Check if `no_unchecked_indexed_access` is enabled.
195    #[inline]
196    pub(crate) const fn no_unchecked_indexed_access(&self) -> bool {
197        self.no_unchecked_indexed_access
198    }
199
200    /// Check if depth limit was exceeded.
201    #[inline]
202    pub const fn is_depth_exceeded(&self) -> bool {
203        self.guard.is_exceeded()
204    }
205
206    /// Mark the guard as exceeded, causing subsequent evaluations to bail out.
207    ///
208    /// Used when an external condition (e.g. mapped key count or distribution
209    /// size exceeds its limit) means further recursive evaluation should stop.
210    #[inline]
211    pub(crate) const fn mark_depth_exceeded(&mut self) {
212        self.guard.mark_exceeded();
213    }
214
215    /// Evaluate a type, resolving any meta-types if possible.
216    /// Returns the evaluated type (may be the same if no evaluation needed).
217    pub fn evaluate(&mut self, type_id: TypeId) -> TypeId {
218        use crate::recursion::RecursionResult;
219
220        // Fast path for intrinsics
221        if type_id.is_intrinsic() {
222            return type_id;
223        }
224
225        // Check if depth was already exceeded in a previous call
226        if self.guard.is_exceeded() {
227            return TypeId::ERROR;
228        }
229
230        if let Some(&cached) = self.cache.get(&type_id) {
231            return cached;
232        }
233
234        // Unified enter: checks iterations, depth, cycle detection, and visiting set size
235        match self.guard.enter(type_id) {
236            RecursionResult::Entered => {}
237            RecursionResult::Cycle => {
238                // Recursion guard for self-referential mapped/application types.
239                // Per TypeScript behavior, recursive mapped types evaluate to empty objects.
240                let key = self.interner.lookup(type_id);
241                if matches!(key, Some(TypeData::Mapped(_))) {
242                    let empty = self.interner.object(vec![]);
243                    self.cache.insert(type_id, empty);
244                    return empty;
245                }
246                return type_id;
247            }
248            RecursionResult::DepthExceeded => {
249                self.cache.insert(type_id, TypeId::ERROR);
250                return TypeId::ERROR;
251            }
252            RecursionResult::IterationExceeded => {
253                self.cache.insert(type_id, type_id);
254                return type_id;
255            }
256        }
257
258        let key = match self.interner.lookup(type_id) {
259            Some(k) => k,
260            None => {
261                self.guard.leave(type_id);
262                return type_id;
263            }
264        };
265
266        // Visitor pattern: dispatch to appropriate visit_* method
267        let result = self.visit_type_key(type_id, &key);
268
269        // Symmetric cleanup: leave guard and cache result
270        self.guard.leave(type_id);
271        self.cache.insert(type_id, result);
272
273        result
274    }
275
276    /// Evaluate a generic type application: Base<Args>
277    ///
278    /// Algorithm:
279    /// 1. Look up the base type - if it's a Ref, resolve it
280    /// 2. Get the type parameters for the base symbol
281    /// 3. If we have type params, instantiate the resolved type with args
282    /// 4. Recursively evaluate the result
283    fn evaluate_application(&mut self, app_id: TypeApplicationId) -> TypeId {
284        let app = self.interner.type_application(app_id);
285
286        // Look up the base type
287        let base_key = match self.interner.lookup(app.base) {
288            Some(k) => k,
289            None => return self.interner.application(app.base, app.args.clone()),
290        };
291
292        // Task B: Resolve TypeQuery bases to DefId for expansion
293        // This fixes the "Ref(5)<error>" diagnostic issue where generic types
294        // aren't expanded to their underlying function/object types
295        // Note: Ref(SymbolRef) was migrated to Lazy(DefId)
296        let def_id = match base_key {
297            TypeData::Lazy(def_id) => Some(def_id),
298            TypeData::TypeQuery(sym_ref) => self.resolver.symbol_to_def_id(sym_ref),
299            _ => None,
300        };
301
302        tracing::trace!(
303            base = app.base.0,
304            base_key = ?base_key,
305            def_id = ?def_id,
306            args = ?app.args.iter().map(|a| a.0).collect::<Vec<_>>(),
307            "evaluate_application"
308        );
309
310        // If the base is a DefId (Lazy, Ref, or TypeQuery), try to resolve and instantiate
311        if let Some(def_id) = def_id {
312            // =======================================================================
313            // PER-DEFID DEPTH LIMITING
314            // =======================================================================
315            // This catches expansive recursion in type aliases like `type T<X> = T<Box<X>>`
316            // that produce new TypeIds on each evaluation, bypassing the `visiting` set.
317            //
318            // Unlike a set-based cycle detector (which blocks ANY re-entry), we use a
319            // per-DefId counter that allows up to MAX_DEF_DEPTH recursive expansions.
320            // This correctly handles legitimate recursive types like:
321            //   type TrimRight<S> = S extends `${infer R} ` ? TrimRight<R> : S;
322            // which need multiple re-entries of the same DefId to converge.
323            // =======================================================================
324            let depth = self.def_depth.entry(def_id).or_insert(0);
325            if *depth >= Self::MAX_DEF_DEPTH {
326                self.guard.mark_exceeded();
327                return TypeId::ERROR;
328            }
329            *depth += 1;
330
331            // Try to get the type parameters for this DefId
332            let type_params = self.resolver.get_lazy_type_params(def_id);
333            let resolved = self.resolver.resolve_lazy(def_id, self.interner);
334
335            tracing::trace!(
336                ?def_id,
337                has_type_params = type_params.is_some(),
338                type_params_count = type_params.as_ref().map(std::vec::Vec::len),
339                has_resolved = resolved.is_some(),
340                resolved_key = ?resolved.and_then(|r| self.interner.lookup(r)),
341                "evaluate_application resolve"
342            );
343
344            let result = if let Some(type_params) = type_params {
345                // Resolve the base type to get the body
346                if let Some(resolved) = resolved {
347                    // Pre-expand type arguments that are TypeQuery or Application.
348                    // For conditional type bodies with Application extends containing infer,
349                    // preserve Application args so the conditional evaluator can match
350                    // at the Application level (e.g., Promise<string> vs Promise<infer U>).
351                    let body_is_conditional_with_app_infer =
352                        self.is_conditional_with_application_infer(resolved);
353                    let expanded_args = if body_is_conditional_with_app_infer {
354                        self.expand_type_args_preserve_applications(&app.args)
355                    } else {
356                        self.expand_type_args(&app.args)
357                    };
358                    let no_unchecked_indexed_access = self.no_unchecked_indexed_access;
359
360                    if let Some(db) = self.query_db
361                        && let Some(cached) = db.lookup_application_eval_cache(
362                            def_id,
363                            &expanded_args,
364                            no_unchecked_indexed_access,
365                        )
366                    {
367                        if let Some(d) = self.def_depth.get_mut(&def_id) {
368                            *d = d.saturating_sub(1);
369                        }
370                        return cached;
371                    }
372
373                    // Instantiate the resolved type with the type arguments
374                    let instantiated =
375                        instantiate_generic(self.interner, resolved, &type_params, &expanded_args);
376                    // Recursively evaluate the result
377                    let evaluated = self.evaluate(instantiated);
378                    if let Some(db) = self.query_db {
379                        db.insert_application_eval_cache(
380                            def_id,
381                            &expanded_args,
382                            no_unchecked_indexed_access,
383                            evaluated,
384                        );
385                    }
386                    evaluated
387                } else {
388                    self.interner.application(app.base, app.args.clone())
389                }
390            } else if let Some(resolved) = resolved {
391                // Fallback: try to extract type params from the resolved type's properties
392                let extracted_params = self.extract_type_params_from_type(resolved);
393                if !extracted_params.is_empty() && extracted_params.len() == app.args.len() {
394                    // Pre-expand type arguments
395                    let expanded_args = self.expand_type_args(&app.args);
396                    let no_unchecked_indexed_access = self.no_unchecked_indexed_access;
397
398                    if let Some(db) = self.query_db
399                        && let Some(cached) = db.lookup_application_eval_cache(
400                            def_id,
401                            &expanded_args,
402                            no_unchecked_indexed_access,
403                        )
404                    {
405                        if let Some(d) = self.def_depth.get_mut(&def_id) {
406                            *d = d.saturating_sub(1);
407                        }
408                        return cached;
409                    }
410
411                    let instantiated = instantiate_generic(
412                        self.interner,
413                        resolved,
414                        &extracted_params,
415                        &expanded_args,
416                    );
417                    let evaluated = self.evaluate(instantiated);
418                    if let Some(db) = self.query_db {
419                        db.insert_application_eval_cache(
420                            def_id,
421                            &expanded_args,
422                            no_unchecked_indexed_access,
423                            evaluated,
424                        );
425                    }
426                    evaluated
427                } else {
428                    self.interner.application(app.base, app.args.clone())
429                }
430            } else {
431                self.interner.application(app.base, app.args.clone())
432            };
433
434            // Decrement per-DefId depth after evaluation
435            if let Some(d) = self.def_depth.get_mut(&def_id) {
436                *d = d.saturating_sub(1);
437            }
438
439            result
440        } else {
441            // If we can't expand, return the original application
442            self.interner.application(app.base, app.args.clone())
443        }
444    }
445
446    /// Check if a type is a Conditional whose `extends_type` is an Application containing infer.
447    /// This detects patterns like `T extends Promise<infer U> ? U : T`.
448    fn is_conditional_with_application_infer(&self, type_id: TypeId) -> bool {
449        let Some(TypeData::Conditional(cond_id)) = self.interner.lookup(type_id) else {
450            return false;
451        };
452        let cond = self.interner.conditional_type(cond_id);
453        matches!(
454            self.interner.lookup(cond.extends_type),
455            Some(TypeData::Application(_))
456        )
457    }
458
459    /// Like `expand_type_args` but preserves Application types without evaluating them.
460    /// Used for conditional type bodies so the conditional evaluator can match
461    /// at the Application level for infer pattern matching.
462    fn expand_type_args_preserve_applications(&mut self, args: &[TypeId]) -> Vec<TypeId> {
463        let mut expanded = Vec::with_capacity(args.len());
464        for &arg in args {
465            let Some(key) = self.interner.lookup(arg) else {
466                expanded.push(arg);
467                continue;
468            };
469            match key {
470                TypeData::Application(_) => {
471                    // Preserve Application form for conditional infer matching
472                    expanded.push(arg);
473                }
474                _ => expanded.push(self.try_expand_type_arg(arg)),
475            }
476        }
477        expanded
478    }
479
480    /// Expand type arguments by evaluating any that are `TypeQuery` or Application.
481    /// Uses a loop instead of closure to allow mutable self access.
482    fn expand_type_args(&mut self, args: &[TypeId]) -> Vec<TypeId> {
483        let mut expanded = Vec::with_capacity(args.len());
484        for &arg in args {
485            expanded.push(self.try_expand_type_arg(arg));
486        }
487        expanded
488    }
489
490    /// Extract type parameter infos from a type by scanning for `TypeParameter` types.
491    fn extract_type_params_from_type(&self, type_id: TypeId) -> Vec<TypeParamInfo> {
492        let mut seen = FxHashSet::default();
493        let mut params = Vec::new();
494        self.collect_type_params(type_id, &mut seen, &mut params);
495        params
496    }
497
498    /// Recursively collect `TypeParameter` types from a type.
499    fn collect_type_params(
500        &self,
501        type_id: TypeId,
502        seen: &mut FxHashSet<tsz_common::interner::Atom>,
503        params: &mut Vec<TypeParamInfo>,
504    ) {
505        if type_id.is_intrinsic() {
506            return;
507        }
508
509        let Some(key) = self.interner.lookup(type_id) else {
510            return;
511        };
512
513        match key {
514            TypeData::TypeParameter(ref info) => {
515                if !seen.contains(&info.name) {
516                    seen.insert(info.name);
517                    params.push(info.clone());
518                }
519            }
520            TypeData::Object(shape_id) | TypeData::ObjectWithIndex(shape_id) => {
521                let shape = self.interner.object_shape(shape_id);
522                for prop in &shape.properties {
523                    self.collect_type_params(prop.type_id, seen, params);
524                }
525            }
526            TypeData::Function(shape_id) => {
527                let shape = self.interner.function_shape(shape_id);
528                for param in &shape.params {
529                    self.collect_type_params(param.type_id, seen, params);
530                }
531                self.collect_type_params(shape.return_type, seen, params);
532            }
533            TypeData::Union(members) | TypeData::Intersection(members) => {
534                let members = self.interner.type_list(members);
535                for &member in members.iter() {
536                    self.collect_type_params(member, seen, params);
537                }
538            }
539            TypeData::Array(elem) => {
540                self.collect_type_params(elem, seen, params);
541            }
542            TypeData::Conditional(cond_id) => {
543                let cond = self.interner.conditional_type(cond_id);
544                self.collect_type_params(cond.check_type, seen, params);
545                self.collect_type_params(cond.extends_type, seen, params);
546                self.collect_type_params(cond.true_type, seen, params);
547                self.collect_type_params(cond.false_type, seen, params);
548            }
549            TypeData::Application(app_id) => {
550                let app = self.interner.type_application(app_id);
551                self.collect_type_params(app.base, seen, params);
552                for &arg in &app.args {
553                    self.collect_type_params(arg, seen, params);
554                }
555            }
556            TypeData::Mapped(mapped_id) => {
557                let mapped = self.interner.mapped_type(mapped_id);
558                // Note: mapped.type_param is the iteration variable (e.g., K in "K in keyof T")
559                // We should NOT add it directly - the outer type param (T) is found in the constraint.
560                // For DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }:
561                //   - type_param is K (iteration var, NOT the outer param)
562                //   - constraint is "keyof T" (contains T, the actual param to extract)
563                //   - template is DeepPartial<T[K]> (also contains T)
564                self.collect_type_params(mapped.constraint, seen, params);
565                self.collect_type_params(mapped.template, seen, params);
566                if let Some(name_type) = mapped.name_type {
567                    self.collect_type_params(name_type, seen, params);
568                }
569            }
570            TypeData::KeyOf(operand) => {
571                // Extract type params from the operand of keyof
572                // e.g., keyof T -> extract T
573                self.collect_type_params(operand, seen, params);
574            }
575            TypeData::IndexAccess(obj, idx) => {
576                // Extract type params from both object and index
577                // e.g., T[K] -> extract T and K
578                self.collect_type_params(obj, seen, params);
579                self.collect_type_params(idx, seen, params);
580            }
581            TypeData::TemplateLiteral(spans) => {
582                // Extract type params from template literal interpolations
583                let spans = self.interner.template_list(spans);
584                for span in spans.iter() {
585                    if let TemplateSpan::Type(inner) = span {
586                        self.collect_type_params(*inner, seen, params);
587                    }
588                }
589            }
590            _ => {}
591        }
592    }
593
594    /// Try to expand a type argument that may be a `TypeQuery` or Application.
595    /// Returns the expanded type, or the original if it can't be expanded.
596    /// This ensures type arguments are resolved before instantiation.
597    ///
598    /// NOTE: This method uses `self.evaluate()` for Application, Conditional, Mapped,
599    /// and `TemplateLiteral` types to ensure recursion depth limits are enforced.
600    fn try_expand_type_arg(&mut self, arg: TypeId) -> TypeId {
601        let Some(key) = self.interner.lookup(arg) else {
602            return arg;
603        };
604        match key {
605            TypeData::TypeQuery(sym_ref) => {
606                // Resolve the TypeQuery to get the VALUE type (constructor for classes).
607                // Use resolve_ref, not resolve_symbol_ref, to avoid the resolve_lazy path
608                // which returns instance types for classes.
609                if let Some(resolved) = self.resolver.resolve_ref(sym_ref, self.interner) {
610                    resolved
611                } else if let Some(def_id) = self.resolver.symbol_to_def_id(sym_ref) {
612                    self.resolver
613                        .resolve_lazy(def_id, self.interner)
614                        .unwrap_or(arg)
615                } else {
616                    arg
617                }
618            }
619            TypeData::Application(_)
620            | TypeData::Conditional(_)
621            | TypeData::Mapped(_)
622            | TypeData::TemplateLiteral(_) => {
623                // Use evaluate() to ensure depth limits are enforced
624                self.evaluate(arg)
625            }
626            TypeData::Lazy(def_id) => {
627                // Resolve Lazy types in type arguments
628                // This helps with generic instantiation accuracy
629                self.resolver
630                    .resolve_lazy(def_id, self.interner)
631                    .unwrap_or(arg)
632            }
633            _ => arg,
634        }
635    }
636
637    /// Check if a type is "complex" and requires full evaluation for identity.
638    ///
639    /// Complex types are those whose structural identity depends on evaluation context:
640    /// - `TypeParameter`: Opaque until instantiation
641    /// - Lazy: Requires resolution
642    /// - Conditional: Requires evaluation of extends clause
643    /// - Mapped: Requires evaluation of mapped type
644    /// - `IndexAccess`: Requires evaluation of T[K]
645    /// - `KeyOf`: Requires evaluation of keyof
646    /// - Application: Requires expansion of Base<Args>
647    /// - `TypeQuery`: Requires resolution of typeof
648    /// - `TemplateLiteral`: Requires evaluation of template parts
649    /// - `ReadonlyType`: Wraps another type
650    /// - `StringIntrinsic`: Uppercase, Lowercase, Capitalize, Uncapitalize
651    ///
652    /// These types are NOT safe for simplification because bypassing evaluation
653    /// would produce incorrect results (e.g., treating T[K] as a distinct type from
654    /// the value it evaluates to).
655    ///
656    /// ## Task #37: Deep Structural Simplification
657    ///
658    /// After implementing the Canonicalizer (Task #32), we can now safely handle
659    /// `Lazy` (type aliases) and `Application` (generics) structurally. These types
660    /// are now "unlocked" for simplification because:
661    /// - `Lazy` types are canonicalized using De Bruijn indices
662    /// - `Application` types are recursively canonicalized
663    /// - The `SubtypeChecker`'s fast-path (Task #36) uses O(1) structural identity
664    ///
665    /// Types that remain "complex" are those that are **inherently deferred**:
666    /// - `TypeParameter`, `Infer`: Waiting for generic substitution
667    /// - `Conditional`, `Mapped`, `IndexAccess`, `KeyOf`: Require type-level computation
668    /// - These cannot be compared structurally until they are fully evaluated
669    fn is_complex_type(&self, type_id: TypeId) -> bool {
670        let Some(key) = self.interner.lookup(type_id) else {
671            return false;
672        };
673
674        matches!(
675            key,
676            TypeData::TypeParameter(_)
677                | TypeData::Infer(_) // Type parameter for conditional types
678                | TypeData::Conditional(_)
679                | TypeData::Mapped(_)
680                | TypeData::IndexAccess(_, _)
681                | TypeData::KeyOf(_)
682                | TypeData::TypeQuery(_)
683                | TypeData::TemplateLiteral(_)
684                | TypeData::ReadonlyType(_)
685                | TypeData::StringIntrinsic { .. }
686                | TypeData::ThisType // Context-dependent polymorphic type
687                                     // Note: Lazy and Application are REMOVED (Task #37)
688                                     // They are now handled by the Canonicalizer (Task #32)
689        )
690    }
691
692    /// Evaluate an intersection type by recursively evaluating members and re-interning.
693    /// This enables "deferred reduction" where intersections containing meta-types
694    /// (e.g., `string & T[K]`) are reduced after the meta-types are evaluated.
695    ///
696    /// Example: `string & T[K]` where `T[K]` evaluates to `number` will become
697    /// `string & number`, which then reduces to `never` via the interner's normalization.
698    fn evaluate_intersection(&mut self, list_id: TypeListId) -> TypeId {
699        let members = self.interner.type_list(list_id);
700        let mut evaluated_members = Vec::with_capacity(members.len());
701
702        for &member in members.iter() {
703            evaluated_members.push(self.evaluate(member));
704        }
705
706        // Deep structural simplification using SubtypeChecker
707        self.simplify_intersection_members(&mut evaluated_members);
708
709        self.interner.intersection(evaluated_members)
710    }
711
712    /// Evaluate a union type by recursively evaluating members and re-interning.
713    /// This enables "deferred reduction" where unions containing meta-types
714    /// (e.g., `string | T[K]`) are reduced after the meta-types are evaluated.
715    ///
716    /// Example: `string | T[K]` where `T[K]` evaluates to `string` will become
717    /// `string | string`, which then reduces to `string` via the interner's normalization.
718    fn evaluate_union(&mut self, list_id: TypeListId) -> TypeId {
719        let members = self.interner.type_list(list_id);
720        let mut evaluated_members = Vec::with_capacity(members.len());
721
722        for &member in members.iter() {
723            evaluated_members.push(self.evaluate(member));
724        }
725
726        // Deep structural simplification using SubtypeChecker
727        self.simplify_union_members(&mut evaluated_members);
728
729        self.interner.union(evaluated_members)
730    }
731
732    /// Simplify union members by removing redundant types using deep subtype checks.
733    /// If A <: B, then A | B = B (A is redundant in the union).
734    ///
735    /// This uses `SubtypeChecker` with `bypass_evaluation=true` to prevent infinite
736    /// recursion, since `TypeEvaluator` has already evaluated all members.
737    ///
738    /// Performance: O(N²) where N is the number of members. We skip simplification
739    /// if the union has more than 25 members to avoid excessive computation.
740    ///
741    /// ## Strategy
742    ///
743    /// 1. **Early exit for large unions** (>25 members) to avoid O(N²) explosion
744    /// 2. **Skip complex types** that require full resolution:
745    ///    - `TypeParameter`, Infer, Conditional, Mapped, `IndexAccess`, `KeyOf`, `TypeQuery`
746    ///    - `TemplateLiteral`, `ReadonlyType`, String manipulation types
747    ///    - Note: Lazy and Application are NOW safe (Task #37: handled by Canonicalizer)
748    /// 3. **Fast-path for any/unknown**: If any member is any, entire union becomes any
749    /// 4. **Identity check**: O(1) structural identity via `SubtypeChecker` (Task #36 fast-path)
750    /// 5. **Depth limit**: `MAX_SUBTYPE_DEPTH` enables deep recursive type simplification (Task #37)
751    ///
752    /// ## Example Reductions
753    ///
754    /// - `"a" | string` → `string` (literal absorbed by primitive)
755    /// - `number | 1 | 2` → `number` (literals absorbed by primitive)
756    /// - `{ a: string } | { a: string; b: number }` → `{ a: string; b: number }`
757    fn simplify_union_members(&mut self, members: &mut Vec<TypeId>) {
758        // Union-specific early exits
759        if members.iter().any(|&id| id.is_unknown()) {
760            return;
761        }
762        // Skip if all members are identity-comparable — they're disjoint, so the O(n²) loop
763        // would find nothing. The interner's reduce_union_subtypes handles shallow cases.
764        if members
765            .iter()
766            .all(|&id| self.interner.is_identity_comparable_type(id))
767        {
768            return;
769        }
770        // In a union, A <: B means A is redundant (B subsumes it).
771        // E.g. `"a" | string` => "a" is redundant, result: `string`
772        self.remove_redundant_members(members, SubtypeDirection::SourceSubsumedByOther);
773    }
774
775    /// Simplify intersection members by removing redundant types using deep subtype checks.
776    /// If A <: B, then A & B = A (B is redundant in the intersection).
777    ///
778    /// ## Example Reductions
779    ///
780    /// - `{ a: string } & { a: string; b: number }` → `{ a: string; b: number }`
781    /// - `{ readonly a: string } & { a: string }` → `{ readonly a: string }`
782    /// - `number & 1` → `1` (literal is more specific)
783    fn simplify_intersection_members(&mut self, members: &mut Vec<TypeId>) {
784        // In an intersection, A <: B means B is redundant (A is more specific).
785        // We check if other members are subtypes of the candidate to remove the supertype.
786        self.remove_redundant_members(members, SubtypeDirection::OtherSubsumedBySource);
787    }
788
789    /// Remove redundant members from a type list using subtype checks.
790    ///
791    /// This is the shared O(n²) core for both union and intersection simplification.
792    /// The `direction` parameter controls which subtype relationship makes a member
793    /// redundant:
794    /// - `SourceSubsumedByOther`: member[i] <: member[j] → i is redundant (union semantics)
795    /// - `OtherSubsumedBySource`: member[j] <: member[i] → i is redundant (intersection semantics)
796    ///
797    /// Common early exits (size guards, `any` check, complex-type check) are applied here.
798    fn remove_redundant_members(&mut self, members: &mut Vec<TypeId>, direction: SubtypeDirection) {
799        // Performance guard: skip small or very large type lists
800        const MAX_SIMPLIFICATION_SIZE: usize = 25;
801        if members.len() < 2 || members.len() > MAX_SIMPLIFICATION_SIZE {
802            return;
803        }
804
805        if members.iter().any(|&id| id.is_any()) {
806            return;
807        }
808
809        if members.iter().any(|&id| self.is_complex_type(id)) {
810            return;
811        }
812
813        use crate::relations::subtype::{MAX_SUBTYPE_DEPTH, SubtypeChecker};
814        let mut checker = SubtypeChecker::with_resolver(self.interner, self.resolver);
815        checker.bypass_evaluation = true;
816        checker.max_depth = MAX_SUBTYPE_DEPTH;
817        checker.no_unchecked_indexed_access = self.no_unchecked_indexed_access;
818
819        let mut i = 0;
820        while i < members.len() {
821            let mut redundant = false;
822            for j in 0..members.len() {
823                if i == j {
824                    continue;
825                }
826                if members[i] == members[j] {
827                    continue;
828                }
829
830                let is_subtype = match direction {
831                    SubtypeDirection::SourceSubsumedByOther => {
832                        checker.is_subtype_of(members[i], members[j])
833                    }
834                    SubtypeDirection::OtherSubsumedBySource => {
835                        checker.is_subtype_of(members[j], members[i])
836                    }
837                };
838                if is_subtype {
839                    redundant = true;
840                    break;
841                }
842            }
843            if redundant {
844                members.remove(i);
845            } else {
846                i += 1;
847            }
848        }
849    }
850
851    // =========================================================================
852    // Visitor Pattern Implementation (North Star Rule 2)
853    // =========================================================================
854
855    /// Visit a `TypeData` and return its evaluated form.
856    ///
857    /// This is the visitor dispatch method that routes to specific visit_* methods.
858    /// The `visiting.remove()` and `cache.insert()` are handled in `evaluate()` for symmetry.
859    fn visit_type_key(&mut self, type_id: TypeId, key: &TypeData) -> TypeId {
860        match key {
861            TypeData::Conditional(cond_id) => self.visit_conditional(*cond_id),
862            TypeData::IndexAccess(obj, idx) => self.visit_index_access(*obj, *idx),
863            TypeData::Mapped(mapped_id) => self.visit_mapped(*mapped_id),
864            TypeData::KeyOf(operand) => self.visit_keyof(*operand),
865            TypeData::TypeQuery(symbol) => self.visit_type_query(symbol.0, type_id),
866            TypeData::Application(app_id) => self.visit_application(*app_id),
867            TypeData::TemplateLiteral(spans) => self.visit_template_literal(*spans),
868            TypeData::Lazy(def_id) => self.visit_lazy(*def_id, type_id),
869            TypeData::StringIntrinsic { kind, type_arg } => {
870                self.visit_string_intrinsic(*kind, *type_arg)
871            }
872            TypeData::Intersection(list_id) => self.visit_intersection(*list_id),
873            TypeData::Union(list_id) => self.visit_union(*list_id),
874            TypeData::NoInfer(inner) => {
875                // NoInfer<T> evaluates to T (strip wrapper, evaluate inner)
876                self.evaluate(*inner)
877            }
878            // All other types pass through unchanged (default behavior)
879            _ => type_id,
880        }
881    }
882
883    /// Visit a conditional type: T extends U ? X : Y
884    fn visit_conditional(&mut self, cond_id: ConditionalTypeId) -> TypeId {
885        let cond = self.interner.conditional_type(cond_id);
886        self.evaluate_conditional(cond.as_ref())
887    }
888
889    /// Visit an index access type: T[K]
890    fn visit_index_access(&mut self, object_type: TypeId, index_type: TypeId) -> TypeId {
891        self.evaluate_index_access(object_type, index_type)
892    }
893
894    /// Visit a mapped type: { [K in Keys]: V }
895    fn visit_mapped(&mut self, mapped_id: MappedTypeId) -> TypeId {
896        let mapped = self.interner.mapped_type(mapped_id);
897        self.evaluate_mapped(mapped.as_ref())
898    }
899
900    /// Visit a keyof type: keyof T
901    fn visit_keyof(&mut self, operand: TypeId) -> TypeId {
902        self.evaluate_keyof(operand)
903    }
904
905    /// Visit a type query: typeof expr
906    ///
907    /// `TypeQuery` represents `typeof X` which must resolve to the VALUE-space type
908    /// (constructor type for classes). We use `resolve_ref` which returns the
909    /// constructor type stored under `SymbolRef`, NOT `resolve_lazy` which returns
910    /// the instance type for classes. This distinction is critical: `typeof A`
911    /// for a class A should give the constructor type (with static members and
912    /// construct signatures), not the instance type.
913    fn visit_type_query(&mut self, symbol_ref: u32, original_type_id: TypeId) -> TypeId {
914        use crate::types::SymbolRef;
915        let symbol = SymbolRef(symbol_ref);
916
917        // Prefer resolve_ref which returns the VALUE type (constructor for classes).
918        // This is correct for TypeQuery (typeof) which is a value-space query.
919        if let Some(resolved) = self.resolver.resolve_ref(symbol, self.interner) {
920            return resolved;
921        }
922
923        // Fallback: try DefId-based resolution if no SymbolRef mapping exists
924        if let Some(def_id) = self.resolver.symbol_to_def_id(symbol)
925            && let Some(resolved) = self.resolver.resolve_lazy(def_id, self.interner)
926        {
927            return resolved;
928        }
929
930        original_type_id
931    }
932
933    /// Visit a generic type application: Base<Args>
934    fn visit_application(&mut self, app_id: TypeApplicationId) -> TypeId {
935        self.evaluate_application(app_id)
936    }
937
938    /// Visit a template literal type: `hello${T}world`
939    fn visit_template_literal(&mut self, spans: TemplateLiteralId) -> TypeId {
940        self.evaluate_template_literal(spans)
941    }
942
943    /// Visit a lazy type reference: Lazy(DefId)
944    fn visit_lazy(&mut self, def_id: DefId, original_type_id: TypeId) -> TypeId {
945        if let Some(resolved) = self.resolver.resolve_lazy(def_id, self.interner) {
946            // Re-evaluate the resolved type in case it needs further evaluation
947            self.evaluate(resolved)
948        } else {
949            original_type_id
950        }
951    }
952
953    /// Visit a string manipulation intrinsic type: Uppercase<T>, Lowercase<T>, etc.
954    fn visit_string_intrinsic(&mut self, kind: StringIntrinsicKind, type_arg: TypeId) -> TypeId {
955        self.evaluate_string_intrinsic(kind, type_arg)
956    }
957
958    /// Visit an intersection type: A & B & C
959    fn visit_intersection(&mut self, list_id: TypeListId) -> TypeId {
960        self.evaluate_intersection(list_id)
961    }
962
963    /// Visit a union type: A | B | C
964    fn visit_union(&mut self, list_id: TypeListId) -> TypeId {
965        self.evaluate_union(list_id)
966    }
967}
968
969/// Convenience function for evaluating conditional types
970pub fn evaluate_conditional(interner: &dyn TypeDatabase, cond: &ConditionalType) -> TypeId {
971    let mut evaluator = TypeEvaluator::new(interner);
972    evaluator.evaluate_conditional(cond)
973}
974
975/// Convenience function for evaluating index access types
976pub fn evaluate_index_access(
977    interner: &dyn TypeDatabase,
978    object_type: TypeId,
979    index_type: TypeId,
980) -> TypeId {
981    let mut evaluator = TypeEvaluator::new(interner);
982    evaluator.evaluate_index_access(object_type, index_type)
983}
984
985/// Convenience function for evaluating index access types with options.
986pub fn evaluate_index_access_with_options(
987    interner: &dyn TypeDatabase,
988    object_type: TypeId,
989    index_type: TypeId,
990    no_unchecked_indexed_access: bool,
991) -> TypeId {
992    let mut evaluator = TypeEvaluator::new(interner);
993    evaluator.set_no_unchecked_indexed_access(no_unchecked_indexed_access);
994    evaluator.evaluate_index_access(object_type, index_type)
995}
996
997/// Convenience function for full type evaluation
998pub fn evaluate_type(interner: &dyn TypeDatabase, type_id: TypeId) -> TypeId {
999    let mut evaluator = TypeEvaluator::new(interner);
1000    evaluator.evaluate(type_id)
1001}
1002
1003/// Convenience function for evaluating mapped types
1004pub fn evaluate_mapped(interner: &dyn TypeDatabase, mapped: &MappedType) -> TypeId {
1005    let mut evaluator = TypeEvaluator::new(interner);
1006    evaluator.evaluate_mapped(mapped)
1007}
1008
1009/// Convenience function for evaluating keyof types
1010pub fn evaluate_keyof(interner: &dyn TypeDatabase, operand: TypeId) -> TypeId {
1011    let mut evaluator = TypeEvaluator::new(interner);
1012    evaluator.evaluate_keyof(operand)
1013}
1014
1015// Re-enabled evaluate tests - verifying API compatibility
1016#[cfg(test)]
1017#[path = "../../tests/evaluate_tests.rs"]
1018mod tests;