Skip to main content

tsz_solver/caches/
db.rs

1//! Type database abstraction for the solver.
2//!
3//! This trait isolates solver logic from concrete storage so we can
4//! swap in a query system (e.g., Salsa) without touching core logic.
5
6use crate::ObjectLiteralBuilder;
7use crate::def::DefId;
8use crate::intern::TypeInterner;
9use crate::narrowing;
10use crate::objects::element_access::{ElementAccessEvaluator, ElementAccessResult};
11use crate::relations::subtype::TypeResolver;
12use crate::type_factory::TypeFactory;
13use crate::types::{
14    CallableShape, CallableShapeId, ConditionalType, ConditionalTypeId, FunctionShape,
15    FunctionShapeId, IndexInfo, IntrinsicKind, MappedType, MappedTypeId, ObjectFlags, ObjectShape,
16    ObjectShapeId, PropertyInfo, PropertyLookup, RelationCacheKey, StringIntrinsicKind, SymbolRef,
17    TemplateLiteralId, TemplateSpan, TupleElement, TupleListId, TypeApplication, TypeApplicationId,
18    TypeData, TypeId, TypeListId, TypeParamInfo, Variance,
19};
20use std::sync::Arc;
21use tsz_binder::SymbolId;
22use tsz_common::interner::Atom;
23
24/// Query interface for the solver.
25///
26/// This keeps solver components generic and prevents them from reaching
27/// into concrete storage structures directly.
28pub trait TypeDatabase {
29    fn intern(&self, key: TypeData) -> TypeId;
30    fn lookup(&self, id: TypeId) -> Option<TypeData>;
31    fn intern_string(&self, s: &str) -> Atom;
32    fn resolve_atom(&self, atom: Atom) -> String;
33    fn resolve_atom_ref(&self, atom: Atom) -> Arc<str>;
34    fn type_list(&self, id: TypeListId) -> Arc<[TypeId]>;
35    fn tuple_list(&self, id: TupleListId) -> Arc<[TupleElement]>;
36    fn template_list(&self, id: TemplateLiteralId) -> Arc<[TemplateSpan]>;
37    fn object_shape(&self, id: ObjectShapeId) -> Arc<ObjectShape>;
38    fn object_property_index(&self, shape_id: ObjectShapeId, name: Atom) -> PropertyLookup;
39    fn function_shape(&self, id: FunctionShapeId) -> Arc<FunctionShape>;
40    fn callable_shape(&self, id: CallableShapeId) -> Arc<CallableShape>;
41    fn conditional_type(&self, id: ConditionalTypeId) -> Arc<ConditionalType>;
42    fn mapped_type(&self, id: MappedTypeId) -> Arc<MappedType>;
43    fn type_application(&self, id: TypeApplicationId) -> Arc<TypeApplication>;
44
45    fn literal_string(&self, value: &str) -> TypeId;
46    fn literal_number(&self, value: f64) -> TypeId;
47    fn literal_boolean(&self, value: bool) -> TypeId;
48    fn literal_bigint(&self, value: &str) -> TypeId;
49    fn literal_bigint_with_sign(&self, negative: bool, digits: &str) -> TypeId;
50
51    fn union(&self, members: Vec<TypeId>) -> TypeId;
52    fn union_from_sorted_vec(&self, flat: Vec<TypeId>) -> TypeId;
53    fn union2(&self, left: TypeId, right: TypeId) -> TypeId;
54    fn union3(&self, first: TypeId, second: TypeId, third: TypeId) -> TypeId;
55    fn intersection(&self, members: Vec<TypeId>) -> TypeId;
56    fn intersection2(&self, left: TypeId, right: TypeId) -> TypeId;
57    /// Raw intersection without normalization (used to avoid infinite recursion)
58    fn intersect_types_raw2(&self, left: TypeId, right: TypeId) -> TypeId;
59    fn array(&self, element: TypeId) -> TypeId;
60    fn tuple(&self, elements: Vec<TupleElement>) -> TypeId;
61    fn object(&self, properties: Vec<PropertyInfo>) -> TypeId;
62    fn object_with_flags(&self, properties: Vec<PropertyInfo>, flags: ObjectFlags) -> TypeId;
63    fn object_with_flags_and_symbol(
64        &self,
65        properties: Vec<PropertyInfo>,
66        flags: ObjectFlags,
67        symbol: Option<SymbolId>,
68    ) -> TypeId;
69    fn object_fresh(&self, properties: Vec<PropertyInfo>) -> TypeId {
70        self.object_with_flags(properties, ObjectFlags::FRESH_LITERAL)
71    }
72    fn object_with_index(&self, shape: ObjectShape) -> TypeId;
73    fn function(&self, shape: FunctionShape) -> TypeId;
74    fn callable(&self, shape: CallableShape) -> TypeId;
75    fn template_literal(&self, spans: Vec<TemplateSpan>) -> TypeId;
76    fn conditional(&self, conditional: ConditionalType) -> TypeId;
77    fn mapped(&self, mapped: MappedType) -> TypeId;
78    fn reference(&self, symbol: SymbolRef) -> TypeId;
79    fn lazy(&self, def_id: DefId) -> TypeId;
80    fn bound_parameter(&self, index: u32) -> TypeId;
81    fn recursive(&self, depth: u32) -> TypeId;
82    fn type_param(&self, info: TypeParamInfo) -> TypeId;
83    fn type_query(&self, symbol: SymbolRef) -> TypeId;
84    fn enum_type(&self, def_id: DefId, structural_type: TypeId) -> TypeId;
85    fn application(&self, base: TypeId, args: Vec<TypeId>) -> TypeId;
86
87    fn literal_string_atom(&self, atom: Atom) -> TypeId;
88    fn union_preserve_members(&self, members: Vec<TypeId>) -> TypeId;
89    fn readonly_type(&self, inner: TypeId) -> TypeId;
90    fn keyof(&self, inner: TypeId) -> TypeId;
91    fn index_access(&self, object_type: TypeId, index_type: TypeId) -> TypeId;
92    fn this_type(&self) -> TypeId;
93    fn no_infer(&self, inner: TypeId) -> TypeId;
94    fn unique_symbol(&self, symbol: SymbolRef) -> TypeId;
95    fn infer(&self, info: TypeParamInfo) -> TypeId;
96    fn string_intrinsic(&self, kind: StringIntrinsicKind, type_arg: TypeId) -> TypeId;
97
98    /// Get the base class type for a symbol (class/interface).
99    /// Returns the `TypeId` of the extends clause, or None if the symbol doesn't extend anything.
100    /// This is used by the BCT algorithm to find common base classes.
101    fn get_class_base_type(&self, symbol_id: SymbolId) -> Option<TypeId>;
102
103    /// Check if a type can be compared by `TypeId` identity alone (O(1) equality).
104    /// Identity-comparable types include literals, enum members, unique symbols, null, undefined,
105    /// void, never, and tuples composed entirely of identity-comparable types.
106    /// Results are cached for O(1) lookup after first computation.
107    fn is_identity_comparable_type(&self, type_id: TypeId) -> bool;
108}
109
110impl TypeDatabase for TypeInterner {
111    fn intern(&self, key: TypeData) -> TypeId {
112        Self::intern(self, key)
113    }
114
115    fn lookup(&self, id: TypeId) -> Option<TypeData> {
116        Self::lookup(self, id)
117    }
118
119    fn intern_string(&self, s: &str) -> Atom {
120        Self::intern_string(self, s)
121    }
122
123    fn resolve_atom(&self, atom: Atom) -> String {
124        Self::resolve_atom(self, atom)
125    }
126
127    fn resolve_atom_ref(&self, atom: Atom) -> Arc<str> {
128        Self::resolve_atom_ref(self, atom)
129    }
130
131    fn type_list(&self, id: TypeListId) -> Arc<[TypeId]> {
132        Self::type_list(self, id)
133    }
134
135    fn tuple_list(&self, id: TupleListId) -> Arc<[TupleElement]> {
136        Self::tuple_list(self, id)
137    }
138
139    fn template_list(&self, id: TemplateLiteralId) -> Arc<[TemplateSpan]> {
140        Self::template_list(self, id)
141    }
142
143    fn object_shape(&self, id: ObjectShapeId) -> Arc<ObjectShape> {
144        Self::object_shape(self, id)
145    }
146
147    fn object_property_index(&self, shape_id: ObjectShapeId, name: Atom) -> PropertyLookup {
148        Self::object_property_index(self, shape_id, name)
149    }
150
151    fn function_shape(&self, id: FunctionShapeId) -> Arc<FunctionShape> {
152        Self::function_shape(self, id)
153    }
154
155    fn callable_shape(&self, id: CallableShapeId) -> Arc<CallableShape> {
156        Self::callable_shape(self, id)
157    }
158
159    fn conditional_type(&self, id: ConditionalTypeId) -> Arc<ConditionalType> {
160        Self::conditional_type(self, id)
161    }
162
163    fn mapped_type(&self, id: MappedTypeId) -> Arc<MappedType> {
164        Self::mapped_type(self, id)
165    }
166
167    fn type_application(&self, id: TypeApplicationId) -> Arc<TypeApplication> {
168        Self::type_application(self, id)
169    }
170
171    fn literal_string(&self, value: &str) -> TypeId {
172        Self::literal_string(self, value)
173    }
174
175    fn literal_number(&self, value: f64) -> TypeId {
176        Self::literal_number(self, value)
177    }
178
179    fn literal_boolean(&self, value: bool) -> TypeId {
180        Self::literal_boolean(self, value)
181    }
182
183    fn literal_bigint(&self, value: &str) -> TypeId {
184        Self::literal_bigint(self, value)
185    }
186
187    fn literal_bigint_with_sign(&self, negative: bool, digits: &str) -> TypeId {
188        Self::literal_bigint_with_sign(self, negative, digits)
189    }
190
191    fn union(&self, members: Vec<TypeId>) -> TypeId {
192        Self::union(self, members)
193    }
194
195    fn union_from_sorted_vec(&self, flat: Vec<TypeId>) -> TypeId {
196        Self::union_from_sorted_vec(self, flat)
197    }
198
199    fn union2(&self, left: TypeId, right: TypeId) -> TypeId {
200        Self::union2(self, left, right)
201    }
202
203    fn union3(&self, first: TypeId, second: TypeId, third: TypeId) -> TypeId {
204        Self::union3(self, first, second, third)
205    }
206
207    fn intersection(&self, members: Vec<TypeId>) -> TypeId {
208        Self::intersection(self, members)
209    }
210
211    fn intersection2(&self, left: TypeId, right: TypeId) -> TypeId {
212        Self::intersection2(self, left, right)
213    }
214
215    fn intersect_types_raw2(&self, left: TypeId, right: TypeId) -> TypeId {
216        Self::intersect_types_raw2(self, left, right)
217    }
218
219    fn array(&self, element: TypeId) -> TypeId {
220        Self::array(self, element)
221    }
222
223    fn tuple(&self, elements: Vec<TupleElement>) -> TypeId {
224        Self::tuple(self, elements)
225    }
226
227    fn object(&self, properties: Vec<PropertyInfo>) -> TypeId {
228        Self::object(self, properties)
229    }
230
231    fn object_with_flags(&self, properties: Vec<PropertyInfo>, flags: ObjectFlags) -> TypeId {
232        Self::object_with_flags(self, properties, flags)
233    }
234
235    fn object_with_flags_and_symbol(
236        &self,
237        properties: Vec<PropertyInfo>,
238        flags: ObjectFlags,
239        symbol: Option<SymbolId>,
240    ) -> TypeId {
241        Self::object_with_flags_and_symbol(self, properties, flags, symbol)
242    }
243
244    fn object_with_index(&self, shape: ObjectShape) -> TypeId {
245        Self::object_with_index(self, shape)
246    }
247
248    fn function(&self, shape: FunctionShape) -> TypeId {
249        Self::function(self, shape)
250    }
251
252    fn callable(&self, shape: CallableShape) -> TypeId {
253        Self::callable(self, shape)
254    }
255
256    fn template_literal(&self, spans: Vec<TemplateSpan>) -> TypeId {
257        Self::template_literal(self, spans)
258    }
259
260    fn conditional(&self, conditional: ConditionalType) -> TypeId {
261        Self::conditional(self, conditional)
262    }
263
264    fn mapped(&self, mapped: MappedType) -> TypeId {
265        Self::mapped(self, mapped)
266    }
267
268    fn reference(&self, symbol: SymbolRef) -> TypeId {
269        Self::reference(self, symbol)
270    }
271
272    fn lazy(&self, def_id: DefId) -> TypeId {
273        Self::lazy(self, def_id)
274    }
275
276    fn bound_parameter(&self, index: u32) -> TypeId {
277        Self::bound_parameter(self, index)
278    }
279
280    fn recursive(&self, depth: u32) -> TypeId {
281        Self::recursive(self, depth)
282    }
283
284    fn type_param(&self, info: TypeParamInfo) -> TypeId {
285        Self::type_param(self, info)
286    }
287
288    fn type_query(&self, symbol: SymbolRef) -> TypeId {
289        Self::type_query(self, symbol)
290    }
291
292    fn enum_type(&self, def_id: DefId, structural_type: TypeId) -> TypeId {
293        Self::enum_type(self, def_id, structural_type)
294    }
295
296    fn application(&self, base: TypeId, args: Vec<TypeId>) -> TypeId {
297        Self::application(self, base, args)
298    }
299
300    fn literal_string_atom(&self, atom: Atom) -> TypeId {
301        Self::literal_string_atom(self, atom)
302    }
303
304    fn union_preserve_members(&self, members: Vec<TypeId>) -> TypeId {
305        Self::union_preserve_members(self, members)
306    }
307
308    fn readonly_type(&self, inner: TypeId) -> TypeId {
309        Self::readonly_type(self, inner)
310    }
311
312    fn keyof(&self, inner: TypeId) -> TypeId {
313        Self::keyof(self, inner)
314    }
315
316    fn index_access(&self, object_type: TypeId, index_type: TypeId) -> TypeId {
317        Self::index_access(self, object_type, index_type)
318    }
319
320    fn this_type(&self) -> TypeId {
321        Self::this_type(self)
322    }
323
324    fn no_infer(&self, inner: TypeId) -> TypeId {
325        Self::no_infer(self, inner)
326    }
327
328    fn unique_symbol(&self, symbol: SymbolRef) -> TypeId {
329        Self::unique_symbol(self, symbol)
330    }
331
332    fn infer(&self, info: TypeParamInfo) -> TypeId {
333        Self::infer(self, info)
334    }
335
336    fn string_intrinsic(&self, kind: StringIntrinsicKind, type_arg: TypeId) -> TypeId {
337        Self::string_intrinsic(self, kind, type_arg)
338    }
339
340    fn get_class_base_type(&self, _symbol_id: SymbolId) -> Option<TypeId> {
341        // TypeInterner doesn't have access to the Binder, so it can't resolve base classes.
342        // The Checker will override this to provide the actual implementation.
343        None
344    }
345
346    fn is_identity_comparable_type(&self, type_id: TypeId) -> bool {
347        Self::is_identity_comparable_type(self, type_id)
348    }
349}
350
351/// Implement `TypeResolver` for `TypeInterner` with noop resolution.
352///
353/// `TypeInterner` doesn't have access to the Binder or type environment,
354/// so it cannot resolve symbol references or `DefIds`. Only `resolve_ref`
355/// (required) is explicitly implemented; all other resolution methods
356/// inherit the trait's default `None`/`false` behavior. The three boxed/array
357/// methods delegate to `TypeInterner`'s own inherent methods.
358impl TypeResolver for TypeInterner {
359    fn resolve_ref(&self, _symbol: SymbolRef, _interner: &dyn TypeDatabase) -> Option<TypeId> {
360        None
361    }
362
363    fn get_boxed_type(&self, kind: IntrinsicKind) -> Option<TypeId> {
364        TypeInterner::get_boxed_type(self, kind)
365    }
366
367    fn get_array_base_type(&self) -> Option<TypeId> {
368        self.get_array_base_type()
369    }
370
371    fn get_array_base_type_params(&self) -> &[TypeParamInfo] {
372        self.get_array_base_type_params()
373    }
374}
375
376/// Query layer for higher-level solver operations.
377///
378/// This is the incremental boundary where caching and (future) salsa hooks live.
379/// Inherits from `TypeResolver` to enable Lazy/Ref type resolution through `evaluate_type()`.
380pub trait QueryDatabase: TypeDatabase + TypeResolver {
381    /// Expose the underlying `TypeDatabase` view for legacy entry points.
382    fn as_type_database(&self) -> &dyn TypeDatabase;
383
384    /// Expose the checked construction surface for type constructors.
385    #[inline]
386    fn factory(&self) -> TypeFactory<'_> {
387        TypeFactory::new(self.as_type_database())
388    }
389
390    /// Register the canonical `Array<T>` base type used by property access resolution.
391    ///
392    /// Some call paths resolve properties through a `TypeInterner`-backed database,
393    /// while others use a `TypeEnvironment`-backed resolver. Implementations should
394    /// store this in whichever backing stores they use so `T[]` methods/properties
395    /// (e.g. `push`, `length`) resolve consistently.
396    fn register_array_base_type(&self, _type_id: TypeId, _type_params: Vec<TypeParamInfo>) {}
397
398    /// Register a boxed interface type for a primitive intrinsic kind.
399    ///
400    /// Similar to `register_array_base_type`, this ensures that property access
401    /// resolution can find the correct interface type (e.g., String, Number) for
402    /// primitive types, regardless of which database backend is used.
403    fn register_boxed_type(&self, _kind: IntrinsicKind, _type_id: TypeId) {}
404
405    fn evaluate_conditional(&self, cond: &ConditionalType) -> TypeId {
406        crate::evaluation::evaluate::evaluate_conditional(self.as_type_database(), cond)
407    }
408
409    fn evaluate_index_access(&self, object_type: TypeId, index_type: TypeId) -> TypeId {
410        self.evaluate_index_access_with_options(
411            object_type,
412            index_type,
413            self.no_unchecked_indexed_access(),
414        )
415    }
416
417    fn evaluate_index_access_with_options(
418        &self,
419        object_type: TypeId,
420        index_type: TypeId,
421        no_unchecked_indexed_access: bool,
422    ) -> TypeId {
423        crate::evaluation::evaluate::evaluate_index_access_with_options(
424            self.as_type_database(),
425            object_type,
426            index_type,
427            no_unchecked_indexed_access,
428        )
429    }
430
431    fn evaluate_type(&self, type_id: TypeId) -> TypeId {
432        crate::evaluation::evaluate::evaluate_type(self.as_type_database(), type_id)
433    }
434
435    fn evaluate_type_with_options(
436        &self,
437        type_id: TypeId,
438        no_unchecked_indexed_access: bool,
439    ) -> TypeId {
440        if !no_unchecked_indexed_access {
441            return self.evaluate_type(type_id);
442        }
443
444        let mut evaluator =
445            crate::evaluation::evaluate::TypeEvaluator::new(self.as_type_database());
446        evaluator.set_no_unchecked_indexed_access(no_unchecked_indexed_access);
447        evaluator.evaluate(type_id)
448    }
449
450    fn evaluate_mapped(&self, mapped: &MappedType) -> TypeId {
451        crate::evaluation::evaluate::evaluate_mapped(self.as_type_database(), mapped)
452    }
453
454    /// Look up a shared cache entry for evaluated generic applications.
455    fn lookup_application_eval_cache(
456        &self,
457        _def_id: DefId,
458        _args: &[TypeId],
459        _no_unchecked_indexed_access: bool,
460    ) -> Option<TypeId> {
461        None
462    }
463
464    /// Store an evaluated generic application result in the shared cache.
465    fn insert_application_eval_cache(
466        &self,
467        _def_id: DefId,
468        _args: &[TypeId],
469        _no_unchecked_indexed_access: bool,
470        _result: TypeId,
471    ) {
472    }
473
474    fn evaluate_keyof(&self, operand: TypeId) -> TypeId {
475        crate::evaluation::evaluate::evaluate_keyof(self.as_type_database(), operand)
476    }
477
478    fn narrow(&self, type_id: TypeId, narrower: TypeId) -> TypeId
479    where
480        Self: Sized,
481    {
482        crate::narrowing::NarrowingContext::new(self).narrow(type_id, narrower)
483    }
484
485    fn resolve_property_access(
486        &self,
487        object_type: TypeId,
488        prop_name: &str,
489    ) -> crate::operations::property::PropertyAccessResult;
490
491    fn resolve_property_access_with_options(
492        &self,
493        object_type: TypeId,
494        prop_name: &str,
495        no_unchecked_indexed_access: bool,
496    ) -> crate::operations::property::PropertyAccessResult;
497
498    fn property_access_type(
499        &self,
500        object_type: TypeId,
501        prop_name: &str,
502    ) -> crate::operations::property::PropertyAccessResult {
503        self.resolve_property_access_with_options(
504            object_type,
505            prop_name,
506            self.no_unchecked_indexed_access(),
507        )
508    }
509
510    fn no_unchecked_indexed_access(&self) -> bool {
511        false
512    }
513
514    fn set_no_unchecked_indexed_access(&self, _enabled: bool) {}
515
516    fn contextual_property_type(&self, expected: TypeId, prop_name: &str) -> Option<TypeId> {
517        let ctx = crate::ContextualTypeContext::with_expected(self.as_type_database(), expected);
518        ctx.get_property_type(prop_name)
519    }
520
521    fn is_property_readonly(&self, object_type: TypeId, prop_name: &str) -> bool {
522        crate::operations::property::property_is_readonly(
523            self.as_type_database(),
524            object_type,
525            prop_name,
526        )
527    }
528
529    fn is_readonly_index_signature(
530        &self,
531        object_type: TypeId,
532        wants_string: bool,
533        wants_number: bool,
534    ) -> bool {
535        crate::operations::property::is_readonly_index_signature(
536            self.as_type_database(),
537            object_type,
538            wants_string,
539            wants_number,
540        )
541    }
542
543    /// Resolve element access (array/tuple indexing) with detailed error reporting
544    fn resolve_element_access(
545        &self,
546        object_type: TypeId,
547        index_type: TypeId,
548        literal_index: Option<usize>,
549    ) -> ElementAccessResult {
550        let evaluator = ElementAccessEvaluator::new(self.as_type_database());
551        evaluator.resolve_element_access(object_type, index_type, literal_index)
552    }
553
554    /// Resolve element access type with cache-friendly error normalization.
555    fn resolve_element_access_type(
556        &self,
557        object_type: TypeId,
558        index_type: TypeId,
559        literal_index: Option<usize>,
560    ) -> TypeId {
561        match self.resolve_element_access(object_type, index_type, literal_index) {
562            crate::element_access::ElementAccessResult::Success(type_id) => type_id,
563            _ => TypeId::ERROR,
564        }
565    }
566
567    /// Collect properties that can be spread into object literals.
568    fn collect_object_spread_properties(&self, spread_type: TypeId) -> Vec<PropertyInfo> {
569        let builder = ObjectLiteralBuilder::new(self.as_type_database());
570        builder.collect_spread_properties(spread_type)
571    }
572
573    /// Get index signatures for a type
574    fn get_index_signatures(&self, type_id: TypeId) -> IndexInfo;
575
576    /// Check if a type contains null or undefined
577    fn is_nullish_type(&self, type_id: TypeId) -> bool;
578
579    /// Remove null and undefined from a type
580    fn remove_nullish(&self, type_id: TypeId) -> TypeId;
581
582    /// Get the canonical `TypeId` for a type, achieving O(1) structural identity checks.
583    ///
584    /// This memoizes the Canonicalizer output so that structurally identical types
585    /// (e.g., `type A = Box<Box<string>>` and `type B = Box<Box<string>>`) return
586    /// the same canonical `TypeId`.
587    ///
588    /// The implementation must:
589    /// - Use a fresh Canonicalizer with empty stacks (for absolute De Bruijn indices)
590    /// - Only expand `TypeAlias` (`DefKind::TypeAlias`), preserving nominal types
591    /// - Cache the result for O(1) subsequent lookups
592    ///
593    /// Task #49: Global Canonical Mapping
594    fn canonical_id(&self, type_id: TypeId) -> TypeId;
595
596    /// Subtype check with compiler flags.
597    ///
598    /// The `flags` parameter is a packed u16 bitmask matching `RelationCacheKey.flags`:
599    /// - bit 0: `strict_null_checks`
600    /// - bit 1: `strict_function_types`
601    /// - bit 2: `exact_optional_property_types`
602    /// - bit 3: `no_unchecked_indexed_access`
603    /// - bit 4: `disable_method_bivariance`
604    /// - bit 5: `allow_void_return`
605    /// - bit 6: `allow_bivariant_rest`
606    /// - bit 7: `allow_bivariant_param_count`
607    fn is_subtype_of(&self, source: TypeId, target: TypeId) -> bool {
608        // Default implementation: use non-strict mode for backward compatibility
609        // Individual callers can use is_subtype_of_with_flags for explicit flag control
610        self.is_subtype_of_with_flags(source, target, 0)
611    }
612
613    /// Subtype check with explicit compiler flags.
614    ///
615    /// The `flags` parameter is a packed u16 bitmask matching `RelationCacheKey.flags`.
616    fn is_subtype_of_with_flags(&self, source: TypeId, target: TypeId, flags: u16) -> bool {
617        // Default implementation: use SubtypeChecker with default flags
618        // (This will be overridden by QueryCache with proper caching)
619        crate::relations::subtype::is_subtype_of_with_flags(
620            self.as_type_database(),
621            source,
622            target,
623            flags,
624        )
625    }
626
627    /// TypeScript assignability check with full compatibility rules (The Lawyer).
628    ///
629    /// This is distinct from `is_subtype_of`:
630    /// - `is_subtype_of` = Strict structural subtyping (The Judge) - for internal solver use
631    /// - `is_assignable_to` = Loose with TS rules (The Lawyer) - for Checker diagnostics
632    ///
633    /// The Lawyer handles:
634    /// - Any type propagation (any is assignable to/from everything)
635    /// - Legacy null/undefined assignability (without strictNullChecks)
636    /// - Weak type detection (excess property checking)
637    /// - Empty object accepts any non-nullish value
638    /// - Function bivariance (when not in strictFunctionTypes mode)
639    ///
640    /// Uses separate cache from `is_subtype_of` to prevent cache poisoning.
641    fn is_assignable_to(&self, source: TypeId, target: TypeId) -> bool {
642        // Default implementation: use non-strict mode for backward compatibility
643        // Individual callers can use is_assignable_to_with_flags for explicit flag control
644        self.is_assignable_to_with_flags(source, target, 0)
645    }
646
647    /// Assignability check with explicit compiler flags.
648    ///
649    /// The `flags` parameter is a packed u16 bitmask matching `RelationCacheKey.flags`.
650    fn is_assignable_to_with_flags(&self, source: TypeId, target: TypeId, flags: u16) -> bool;
651
652    /// Look up a cached subtype result for the given key.
653    /// Returns `None` if the result is not cached.
654    /// Default implementation returns `None` (no caching).
655    fn lookup_subtype_cache(&self, _key: RelationCacheKey) -> Option<bool> {
656        None
657    }
658
659    /// Cache a subtype result for the given key.
660    /// Default implementation is a no-op.
661    fn insert_subtype_cache(&self, _key: RelationCacheKey, _result: bool) {}
662
663    /// Look up a cached assignability result for the given key.
664    /// Returns `None` if the result is not cached.
665    /// Default implementation returns `None` (no caching).
666    fn lookup_assignability_cache(&self, _key: RelationCacheKey) -> Option<bool> {
667        None
668    }
669
670    /// Cache an assignability result for the given key.
671    /// Default implementation is a no-op.
672    fn insert_assignability_cache(&self, _key: RelationCacheKey, _result: bool) {}
673
674    fn new_inference_context(&self) -> crate::inference::infer::InferenceContext<'_> {
675        crate::inference::infer::InferenceContext::new(self.as_type_database())
676    }
677
678    /// Task #41: Get the variance mask for a generic type definition.
679    ///
680    /// Returns the variance of each type parameter for the given `DefId`.
681    /// Returns None if the `DefId` is not a generic type or variance cannot be determined.
682    fn get_type_param_variance(&self, def_id: DefId) -> Option<Arc<[Variance]>>;
683}
684
685impl QueryDatabase for TypeInterner {
686    fn as_type_database(&self) -> &dyn TypeDatabase {
687        self
688    }
689
690    fn register_array_base_type(&self, type_id: TypeId, type_params: Vec<TypeParamInfo>) {
691        self.set_array_base_type(type_id, type_params);
692    }
693
694    fn register_boxed_type(&self, kind: IntrinsicKind, type_id: TypeId) {
695        TypeInterner::set_boxed_type(self, kind, type_id);
696    }
697
698    fn get_index_signatures(&self, type_id: TypeId) -> IndexInfo {
699        match self.lookup(type_id) {
700            Some(TypeData::ObjectWithIndex(shape_id)) => {
701                let shape = self.object_shape(shape_id);
702                IndexInfo {
703                    string_index: shape.string_index.clone(),
704                    number_index: shape.number_index.clone(),
705                }
706            }
707            Some(TypeData::Array(element)) => {
708                // Arrays have number index signature with element type
709                IndexInfo {
710                    string_index: None,
711                    number_index: Some(crate::types::IndexSignature {
712                        key_type: TypeId::NUMBER,
713                        value_type: element,
714                        readonly: false,
715                    }),
716                }
717            }
718            Some(TypeData::Tuple(elements_id)) => {
719                // Tuples have number index signature with union of element types
720                let elements = self.tuple_list(elements_id);
721                let element_types: Vec<TypeId> = elements.iter().map(|e| e.type_id).collect();
722                let value_type = if element_types.is_empty() {
723                    TypeId::UNDEFINED
724                } else if element_types.len() == 1 {
725                    element_types[0]
726                } else {
727                    self.union(element_types)
728                };
729                IndexInfo {
730                    string_index: None,
731                    number_index: Some(crate::types::IndexSignature {
732                        key_type: TypeId::NUMBER,
733                        value_type,
734                        readonly: false,
735                    }),
736                }
737            }
738            Some(TypeData::Union(members_id)) => {
739                // For unions, collect index signatures from all members
740                let members = self.type_list(members_id);
741                let mut string_indices = Vec::new();
742                let mut number_indices = Vec::new();
743
744                for &member in members.iter() {
745                    let info = self.get_index_signatures(member);
746                    if let Some(sig) = info.string_index {
747                        string_indices.push(sig);
748                    }
749                    if let Some(sig) = info.number_index {
750                        number_indices.push(sig);
751                    }
752                }
753
754                // Union of the value types
755                let string_index = if string_indices.is_empty() {
756                    None
757                } else {
758                    Some(crate::types::IndexSignature {
759                        key_type: TypeId::STRING,
760                        value_type: self
761                            .union(string_indices.iter().map(|s| s.value_type).collect()),
762                        readonly: string_indices.iter().all(|s| s.readonly),
763                    })
764                };
765
766                let number_index = if number_indices.is_empty() {
767                    None
768                } else {
769                    Some(crate::types::IndexSignature {
770                        key_type: TypeId::NUMBER,
771                        value_type: self
772                            .union(number_indices.iter().map(|s| s.value_type).collect()),
773                        readonly: number_indices.iter().all(|s| s.readonly),
774                    })
775                };
776
777                IndexInfo {
778                    string_index,
779                    number_index,
780                }
781            }
782            Some(TypeData::Intersection(members_id)) => {
783                // For intersections, combine index signatures
784                let members = self.type_list(members_id);
785                let mut string_index = None;
786                let mut number_index = None;
787
788                for &member in members.iter() {
789                    let info = self.get_index_signatures(member);
790                    if let Some(sig) = info.string_index {
791                        string_index = Some(sig);
792                    }
793                    if let Some(sig) = info.number_index {
794                        number_index = Some(sig);
795                    }
796                }
797
798                IndexInfo {
799                    string_index,
800                    number_index,
801                }
802            }
803            _ => IndexInfo::default(),
804        }
805    }
806
807    fn is_nullish_type(&self, type_id: TypeId) -> bool {
808        narrowing::is_nullish_type(self, type_id)
809    }
810
811    fn remove_nullish(&self, type_id: TypeId) -> TypeId {
812        narrowing::remove_nullish(self, type_id)
813    }
814
815    fn is_assignable_to(&self, source: TypeId, target: TypeId) -> bool {
816        // Default implementation: use non-strict mode for backward compatibility
817        self.is_assignable_to_with_flags(source, target, 0)
818    }
819
820    fn is_assignable_to_with_flags(&self, source: TypeId, target: TypeId, flags: u16) -> bool {
821        use crate::relations::compat::CompatChecker;
822        let mut checker = CompatChecker::new(self);
823        if flags != 0 {
824            checker.apply_flags(flags);
825        }
826        checker.is_assignable(source, target)
827    }
828
829    fn resolve_property_access(
830        &self,
831        object_type: TypeId,
832        prop_name: &str,
833    ) -> crate::operations::property::PropertyAccessResult {
834        // TypeInterner doesn't have TypeResolver capability, so it can't resolve Lazy types
835        // Use PropertyAccessEvaluator with QueryDatabase (self implements both TypeDatabase and TypeResolver)
836        let evaluator = crate::operations::property::PropertyAccessEvaluator::new(self);
837        evaluator.resolve_property_access(object_type, prop_name)
838    }
839
840    fn resolve_property_access_with_options(
841        &self,
842        object_type: TypeId,
843        prop_name: &str,
844        no_unchecked_indexed_access: bool,
845    ) -> crate::operations::property::PropertyAccessResult {
846        let mut evaluator = crate::operations::property::PropertyAccessEvaluator::new(self);
847        evaluator.set_no_unchecked_indexed_access(no_unchecked_indexed_access);
848        evaluator.resolve_property_access(object_type, prop_name)
849    }
850
851    fn get_type_param_variance(&self, _def_id: DefId) -> Option<Arc<[Variance]>> {
852        // TypeInterner doesn't have access to type parameter information.
853        // The Checker will override this to provide the actual implementation.
854        None
855    }
856
857    fn canonical_id(&self, type_id: TypeId) -> TypeId {
858        // TypeInterner doesn't have caching, so compute directly
859        use crate::canonicalize::Canonicalizer;
860        let mut canon = Canonicalizer::new(self, self);
861        canon.canonicalize(type_id)
862    }
863}