Skip to main content

tsz_solver/
type_resolver.rs

1//! Type resolution trait and environment.
2//!
3//! Defines `TypeResolver` — the trait for lazily resolving type references
4//! (both legacy `SymbolRef` and modern `DefId`), and `TypeEnvironment` — the
5//! standard implementation that maps identifiers to their resolved types.
6
7use crate::TypeDatabase;
8use crate::def::DefId;
9use crate::types::{IntrinsicKind, SymbolRef, TypeId, TypeParamInfo};
10use rustc_hash::{FxHashMap, FxHashSet};
11use tsz_binder::SymbolId;
12
13/// Trait for resolving type references to their structural types.
14/// This allows the `SubtypeChecker` to lazily resolve Ref types
15/// without being tightly coupled to the binder/checker.
16pub trait TypeResolver {
17    /// Resolve a symbol reference to its structural type.
18    /// Returns None if the symbol cannot be resolved.
19    ///
20    /// Deprecated: use `resolve_lazy` with `DefId` instead.
21    fn resolve_ref(&self, symbol: SymbolRef, interner: &dyn TypeDatabase) -> Option<TypeId>;
22
23    /// Resolve a symbol reference to a structural type, preferring DefId-based lazy paths.
24    ///
25    /// Prefers `resolve_lazy` via `DefId` when available, falling back to `resolve_ref`.
26    fn resolve_symbol_ref(&self, symbol: SymbolRef, interner: &dyn TypeDatabase) -> Option<TypeId> {
27        if let Some(def_id) = self.symbol_to_def_id(symbol) {
28            self.resolve_lazy(def_id, interner)
29        } else {
30            self.resolve_ref(symbol, interner)
31        }
32    }
33
34    /// Resolve a `DefId` reference to its structural type.
35    ///
36    /// This is the `DefId` equivalent of `resolve_ref`, used for `TypeData::Lazy(DefId)`.
37    /// `DefIds` are Solver-owned identifiers that decouple type references from the Binder.
38    ///
39    /// Returns None by default; implementations should override to support Lazy type resolution.
40    fn resolve_lazy(&self, _def_id: DefId, _interner: &dyn TypeDatabase) -> Option<TypeId> {
41        None
42    }
43
44    /// Get type parameters for a symbol (for generic type aliases/interfaces).
45    /// Returns None by default; implementations can override to support
46    /// Application type expansion.
47    fn get_type_params(&self, _symbol: SymbolRef) -> Option<Vec<TypeParamInfo>> {
48        None
49    }
50
51    /// Get type parameters for a `DefId` (for generic type aliases/interfaces).
52    ///
53    /// This is the `DefId` equivalent of `get_type_params`.
54    /// Returns None by default; implementations can override to support
55    /// Application type expansion with Lazy types.
56    fn get_lazy_type_params(&self, _def_id: DefId) -> Option<Vec<TypeParamInfo>> {
57        None
58    }
59
60    /// Get the `SymbolId` for a `DefId` (bridge for `InheritanceGraph`).
61    ///
62    /// This enables DefId-based types to use the existing O(1) `InheritanceGraph`
63    /// by mapping `DefIds` back to their corresponding `SymbolIds`. The mapping is
64    /// maintained by the Binder/Checker during type resolution.
65    ///
66    /// Returns None if the `DefId` doesn't have a corresponding `SymbolId`.
67    fn def_to_symbol_id(&self, _def_id: DefId) -> Option<SymbolId> {
68        None
69    }
70
71    /// Get the `DefId` for a `SymbolRef` (Ref -> Lazy migration).
72    ///
73    /// This enables migrating Ref(SymbolRef) types to Lazy(DefId) resolution logic.
74    /// When a `SymbolRef` has a corresponding `DefId`, we should use `resolve_lazy` instead
75    /// of `resolve_ref` for consistent type identity.
76    ///
77    /// Returns None if the `SymbolRef` doesn't have a corresponding `DefId`.
78    fn symbol_to_def_id(&self, _symbol: SymbolRef) -> Option<DefId> {
79        None
80    }
81
82    /// Get the `DefKind` for a `DefId` (Task #32: Graph Isomorphism).
83    ///
84    /// This is used by the Canonicalizer to distinguish between structural types
85    /// (`TypeAlias` - should be canonicalized with Recursive indices) and nominal
86    /// types (Interface/Class/Enum - must remain as Lazy(DefId) for nominal identity).
87    ///
88    /// Returns None if the `DefId` doesn't exist or the implementation doesn't
89    /// support `DefKind` lookup.
90    fn get_def_kind(&self, _def_id: DefId) -> Option<crate::def::DefKind> {
91        None
92    }
93
94    /// Get the boxed interface type for a primitive intrinsic (Rule #33).
95    /// For example, `IntrinsicKind::Number` -> `TypeId` of the Number interface.
96    /// This enables primitives to be subtypes of their boxed interfaces.
97    fn get_boxed_type(&self, _kind: IntrinsicKind) -> Option<TypeId> {
98        None
99    }
100
101    /// Check if a `DefId` corresponds to a boxed type for the given intrinsic kind.
102    fn is_boxed_def_id(&self, _def_id: DefId, _kind: IntrinsicKind) -> bool {
103        false
104    }
105
106    /// Check if a `TypeId` is any known resolved form of a boxed type.
107    ///
108    /// The `Object` interface (and other boxed types) can have multiple `TypeId`s:
109    /// one from `resolve_lib_type_by_name` and another from `type_reference_symbol_type`.
110    /// This method checks all registered boxed `DefId`s and their resolved `TypeId`s.
111    fn is_boxed_type_id(&self, _type_id: TypeId, _kind: IntrinsicKind) -> bool {
112        false
113    }
114
115    /// Get the Array<T> interface type from lib.d.ts.
116    fn get_array_base_type(&self) -> Option<TypeId> {
117        None
118    }
119
120    /// Get the type parameters for the Array<T> interface.
121    fn get_array_base_type_params(&self) -> &[TypeParamInfo] {
122        &[]
123    }
124
125    /// Get an export from a namespace/module by name.
126    ///
127    /// Used for qualified name resolution: `namespace.member`.
128    fn get_lazy_export(&self, _def_id: DefId, _name: tsz_common::interner::Atom) -> Option<TypeId> {
129        None
130    }
131
132    /// Get enum member type by name from an enum `DefId`.
133    ///
134    /// Used for enum member access: `Enum.Member`.
135    fn get_lazy_enum_member(
136        &self,
137        _def_id: DefId,
138        _name: tsz_common::interner::Atom,
139    ) -> Option<TypeId> {
140        None
141    }
142
143    /// Check if a `DefId` corresponds to a numeric enum (not a string enum).
144    ///
145    /// Used for TypeScript's unsound Rule #7 (Open Numeric Enums) where
146    /// number types are assignable to/from numeric enums.
147    fn is_numeric_enum(&self, _def_id: DefId) -> bool {
148        false
149    }
150
151    /// Check if a `TypeId` represents a full Enum type (not a specific member).
152    fn is_enum_type(&self, _type_id: TypeId, _interner: &dyn TypeDatabase) -> bool {
153        false
154    }
155
156    /// Get the parent Enum's `DefId` for an Enum Member's `DefId`.
157    ///
158    /// Used to check nominal relationships between enum members and their parent types.
159    fn get_enum_parent_def_id(&self, _member_def_id: DefId) -> Option<DefId> {
160        None
161    }
162
163    /// Check if a `DefId` represents a user-defined enum (not an intrinsic type).
164    fn is_user_enum_def(&self, _def_id: DefId) -> bool {
165        false
166    }
167
168    /// Get the base class type for a class/interface type.
169    ///
170    /// Used by the Best Common Type (BCT) algorithm to find common base classes.
171    fn get_base_type(&self, _type_id: TypeId, _interner: &dyn TypeDatabase) -> Option<TypeId> {
172        None
173    }
174
175    /// Get the variance mask for type parameters of a generic type (Task #41).
176    ///
177    /// Used by `check_application_to_application_subtype` to optimize generic
178    /// assignability checks via variance annotations instead of full structural expansion.
179    fn get_type_param_variance(
180        &self,
181        _def_id: DefId,
182    ) -> Option<std::sync::Arc<[crate::types::Variance]>> {
183        None
184    }
185}
186
187/// A no-op resolver that doesn't resolve any references.
188/// Useful for tests or when symbol resolution isn't needed.
189pub struct NoopResolver;
190
191impl TypeResolver for NoopResolver {
192    fn resolve_ref(&self, _symbol: SymbolRef, _interner: &dyn TypeDatabase) -> Option<TypeId> {
193        None
194    }
195}
196
197/// Blanket implementation of `TypeResolver` for references to resolver types.
198///
199/// This allows `&dyn TypeResolver` (which is Sized) to be used wherever
200/// `R: TypeResolver` is expected.
201impl<T: TypeResolver + ?Sized> TypeResolver for &T {
202    fn resolve_ref(&self, _symbol: SymbolRef, _interner: &dyn TypeDatabase) -> Option<TypeId> {
203        // This method is deprecated - use resolve_lazy instead
204        None
205    }
206
207    fn resolve_lazy(&self, def_id: DefId, interner: &dyn TypeDatabase) -> Option<TypeId> {
208        (**self).resolve_lazy(def_id, interner)
209    }
210
211    fn get_type_params(&self, symbol: SymbolRef) -> Option<Vec<TypeParamInfo>> {
212        (**self).get_type_params(symbol)
213    }
214
215    fn get_lazy_type_params(&self, def_id: DefId) -> Option<Vec<TypeParamInfo>> {
216        (**self).get_lazy_type_params(def_id)
217    }
218
219    fn symbol_to_def_id(&self, symbol: SymbolRef) -> Option<DefId> {
220        (**self).symbol_to_def_id(symbol)
221    }
222}
223
224// =============================================================================
225// TypeEnvironment
226// =============================================================================
227
228/// A type environment that maps symbol refs to their resolved types.
229/// This is populated before type checking and passed to the `SubtypeChecker`.
230#[derive(Clone, Debug, Default)]
231pub struct TypeEnvironment {
232    /// Maps symbol references to their resolved structural types.
233    types: FxHashMap<u32, TypeId>,
234    /// Maps symbol references to their type parameters (for generic types).
235    type_params: FxHashMap<u32, Vec<TypeParamInfo>>,
236    /// Maps primitive intrinsic kinds to their boxed interface types (Rule #33).
237    boxed_types: FxHashMap<IntrinsicKind, TypeId>,
238    /// The Array<T> interface type from lib.d.ts.
239    array_base_type: Option<TypeId>,
240    /// Type parameters for the Array<T> interface (usually just [T]).
241    array_base_type_params: Vec<TypeParamInfo>,
242    /// Maps `DefIds` to their resolved structural types.
243    def_types: FxHashMap<u32, TypeId>,
244    /// Maps `DefIds` to their type parameters (for generic types with Lazy refs).
245    def_type_params: FxHashMap<u32, Vec<TypeParamInfo>>,
246    /// Maps `DefIds` back to `SymbolIds` for `InheritanceGraph` lookups.
247    def_to_symbol: FxHashMap<u32, SymbolId>,
248    /// Maps `SymbolIds` to `DefIds` for Ref -> Lazy migration.
249    symbol_to_def: FxHashMap<u32, DefId>,
250    /// Set of `DefIds` that correspond to numeric enums.
251    numeric_enums: FxHashSet<u32>,
252    /// Maps `DefIds` to their `DefKind` (Task #32: Graph Isomorphism).
253    def_kinds: FxHashMap<u32, crate::def::DefKind>,
254    /// Maps enum member `DefIds` to their parent enum `DefId`.
255    enum_parents: FxHashMap<u32, DefId>,
256    /// Maps class `DefIds` to their instance types.
257    class_instance_types: FxHashMap<u32, TypeId>,
258    /// Maps `IntrinsicKind` to all `DefIds` that correspond to that boxed type.
259    boxed_def_ids: FxHashMap<IntrinsicKind, Vec<DefId>>,
260}
261
262impl TypeEnvironment {
263    pub fn new() -> Self {
264        Self {
265            types: FxHashMap::default(),
266            type_params: FxHashMap::default(),
267            boxed_types: FxHashMap::default(),
268            array_base_type: None,
269            array_base_type_params: Vec::new(),
270            def_types: FxHashMap::default(),
271            def_type_params: FxHashMap::default(),
272            def_to_symbol: FxHashMap::default(),
273            symbol_to_def: FxHashMap::default(),
274            numeric_enums: FxHashSet::default(),
275            def_kinds: FxHashMap::default(),
276            enum_parents: FxHashMap::default(),
277            class_instance_types: FxHashMap::default(),
278            boxed_def_ids: FxHashMap::default(),
279        }
280    }
281
282    /// Register a symbol's resolved type.
283    pub fn insert(&mut self, symbol: SymbolRef, type_id: TypeId) {
284        self.types.insert(symbol.0, type_id);
285    }
286
287    /// Register a boxed type for a primitive (Rule #33).
288    pub fn set_boxed_type(&mut self, kind: IntrinsicKind, type_id: TypeId) {
289        self.boxed_types.insert(kind, type_id);
290    }
291
292    /// Get the boxed type for a primitive.
293    pub fn get_boxed_type(&self, kind: IntrinsicKind) -> Option<TypeId> {
294        self.boxed_types.get(&kind).copied()
295    }
296
297    /// Register a `DefId` as belonging to a boxed type.
298    pub fn register_boxed_def_id(&mut self, kind: IntrinsicKind, def_id: DefId) {
299        self.boxed_def_ids.entry(kind).or_default().push(def_id);
300    }
301
302    /// Check if a `DefId` corresponds to a boxed type of the given kind.
303    pub fn is_boxed_def_id(&self, def_id: DefId, kind: IntrinsicKind) -> bool {
304        self.boxed_def_ids
305            .get(&kind)
306            .is_some_and(|ids| ids.contains(&def_id))
307    }
308
309    /// Check if a `TypeId` is any known resolved form of a boxed type.
310    pub fn is_boxed_type_id(&self, type_id: TypeId, kind: IntrinsicKind) -> bool {
311        // First check the direct boxed type
312        if self.boxed_types.get(&kind).is_some_and(|&t| t == type_id) {
313            return true;
314        }
315        // Check if any registered boxed DefId resolves to this TypeId
316        if let Some(def_ids) = self.boxed_def_ids.get(&kind) {
317            for &def_id in def_ids {
318                if self.def_types.get(&def_id.0).is_some_and(|&t| t == type_id) {
319                    return true;
320                }
321            }
322        }
323        false
324    }
325
326    /// Register the Array<T> interface type from lib.d.ts.
327    pub fn set_array_base_type(&mut self, type_id: TypeId, type_params: Vec<TypeParamInfo>) {
328        self.array_base_type = Some(type_id);
329        self.array_base_type_params = type_params;
330    }
331
332    /// Get the Array<T> interface type.
333    pub const fn get_array_base_type(&self) -> Option<TypeId> {
334        self.array_base_type
335    }
336
337    /// Get the type parameters for the Array<T> interface.
338    pub fn get_array_base_type_params(&self) -> &[TypeParamInfo] {
339        &self.array_base_type_params
340    }
341
342    /// Register a symbol's resolved type with type parameters.
343    pub fn insert_with_params(
344        &mut self,
345        symbol: SymbolRef,
346        type_id: TypeId,
347        params: Vec<TypeParamInfo>,
348    ) {
349        self.types.insert(symbol.0, type_id);
350        if !params.is_empty() {
351            self.type_params.insert(symbol.0, params);
352        }
353    }
354
355    /// Get a symbol's resolved type.
356    pub fn get(&self, symbol: SymbolRef) -> Option<TypeId> {
357        self.types.get(&symbol.0).copied()
358    }
359
360    /// Get a symbol's type parameters.
361    pub fn get_params(&self, symbol: SymbolRef) -> Option<&Vec<TypeParamInfo>> {
362        self.type_params.get(&symbol.0)
363    }
364
365    /// Check if the environment contains a symbol.
366    pub fn contains(&self, symbol: SymbolRef) -> bool {
367        self.types.contains_key(&symbol.0)
368    }
369
370    /// Number of resolved types.
371    pub fn len(&self) -> usize {
372        self.types.len()
373    }
374
375    /// Check if empty.
376    pub fn is_empty(&self) -> bool {
377        self.types.is_empty()
378    }
379
380    // =========================================================================
381    // DefId Resolution
382    // =========================================================================
383
384    /// Register a `DefId`'s resolved type.
385    pub fn insert_def(&mut self, def_id: DefId, type_id: TypeId) {
386        self.def_types.insert(def_id.0, type_id);
387    }
388
389    /// Register a class `DefId`'s instance type.
390    pub fn insert_class_instance_type(&mut self, def_id: DefId, instance_type: TypeId) {
391        self.class_instance_types.insert(def_id.0, instance_type);
392    }
393
394    /// Register a `DefId`'s resolved type with type parameters.
395    pub fn insert_def_with_params(
396        &mut self,
397        def_id: DefId,
398        type_id: TypeId,
399        params: Vec<TypeParamInfo>,
400    ) {
401        self.def_types.insert(def_id.0, type_id);
402        if !params.is_empty() {
403            self.def_type_params.insert(def_id.0, params);
404        }
405    }
406
407    /// Get a `DefId`'s resolved type.
408    pub fn get_def(&self, def_id: DefId) -> Option<TypeId> {
409        self.def_types.get(&def_id.0).copied()
410    }
411
412    /// Get a `DefId`'s type parameters.
413    pub fn get_def_params(&self, def_id: DefId) -> Option<&Vec<TypeParamInfo>> {
414        self.def_type_params.get(&def_id.0)
415    }
416
417    /// Check if the environment contains a `DefId`.
418    pub fn contains_def(&self, def_id: DefId) -> bool {
419        self.def_types.contains_key(&def_id.0)
420    }
421
422    /// Merge def entries (types and type params) from this environment into another.
423    pub fn merge_defs_into(&self, target: &mut Self) {
424        for (&key, &type_id) in &self.def_types {
425            target.def_types.entry(key).or_insert(type_id);
426        }
427        for (key, params) in &self.def_type_params {
428            target
429                .def_type_params
430                .entry(*key)
431                .or_insert_with(|| params.clone());
432        }
433    }
434
435    // =========================================================================
436    // DefKind Storage (Task #32: Graph Isomorphism)
437    // =========================================================================
438
439    /// Register a `DefId`'s `DefKind`.
440    pub fn insert_def_kind(&mut self, def_id: DefId, kind: crate::def::DefKind) {
441        self.def_kinds.insert(def_id.0, kind);
442    }
443
444    /// Get a `DefId`'s `DefKind`.
445    pub fn get_def_kind(&self, def_id: DefId) -> Option<crate::def::DefKind> {
446        self.def_kinds.get(&def_id.0).copied()
447    }
448
449    // =========================================================================
450    // DefId <-> SymbolId Bridge
451    // =========================================================================
452
453    /// Register a mapping from `DefId` to `SymbolId` for `InheritanceGraph` lookups.
454    ///
455    /// Also registers the reverse mapping (`SymbolId` -> `DefId`).
456    pub fn register_def_symbol_mapping(&mut self, def_id: DefId, sym_id: SymbolId) {
457        self.def_to_symbol.insert(def_id.0, sym_id);
458        self.symbol_to_def.insert(sym_id.0, def_id);
459    }
460
461    /// Register a `DefId` as a numeric enum.
462    pub fn register_numeric_enum(&mut self, def_id: DefId) {
463        self.numeric_enums.insert(def_id.0);
464    }
465
466    /// Check if a `DefId` is a numeric enum.
467    pub fn is_numeric_enum(&self, def_id: DefId) -> bool {
468        self.numeric_enums.contains(&def_id.0)
469    }
470
471    // =========================================================================
472    // Enum Parent Relationships
473    // =========================================================================
474
475    /// Register an enum member's parent enum `DefId`.
476    pub fn register_enum_parent(&mut self, member_def_id: DefId, parent_def_id: DefId) {
477        self.enum_parents.insert(member_def_id.0, parent_def_id);
478    }
479
480    /// Get the parent enum `DefId` for an enum member `DefId`.
481    pub fn get_enum_parent(&self, member_def_id: DefId) -> Option<DefId> {
482        self.enum_parents.get(&member_def_id.0).copied()
483    }
484}
485
486impl TypeResolver for TypeEnvironment {
487    fn resolve_ref(&self, symbol: SymbolRef, _interner: &dyn TypeDatabase) -> Option<TypeId> {
488        self.get(symbol)
489    }
490
491    fn resolve_lazy(&self, def_id: DefId, _interner: &dyn TypeDatabase) -> Option<TypeId> {
492        // For classes, return the instance type (type position) instead of the constructor type
493        if let Some(&instance_type) = self.class_instance_types.get(&def_id.0) {
494            return Some(instance_type);
495        }
496        self.get_def(def_id).or_else(|| {
497            // Fallback: `interner.reference(SymbolRef(N))` creates `Lazy(DefId(N))`
498            // where N is the raw SymbolId. Look up the real DefId via symbol_to_def.
499            let real_def = self.symbol_to_def.get(&def_id.0)?;
500            if let Some(&instance_type) = self.class_instance_types.get(&real_def.0) {
501                return Some(instance_type);
502            }
503            self.get_def(*real_def)
504        })
505    }
506
507    fn get_type_params(&self, symbol: SymbolRef) -> Option<Vec<TypeParamInfo>> {
508        self.get_params(symbol).cloned()
509    }
510
511    fn get_lazy_type_params(&self, def_id: DefId) -> Option<Vec<TypeParamInfo>> {
512        self.get_def_params(def_id).cloned().or_else(|| {
513            // Fallback: resolve raw SymbolId-based DefIds to real DefIds
514            let real_def = self.symbol_to_def.get(&def_id.0)?;
515            self.get_def_params(*real_def).cloned()
516        })
517    }
518
519    fn get_boxed_type(&self, kind: IntrinsicKind) -> Option<TypeId> {
520        Self::get_boxed_type(self, kind)
521    }
522
523    fn is_boxed_def_id(&self, def_id: DefId, kind: IntrinsicKind) -> bool {
524        Self::is_boxed_def_id(self, def_id, kind)
525    }
526
527    fn is_boxed_type_id(&self, type_id: TypeId, kind: IntrinsicKind) -> bool {
528        Self::is_boxed_type_id(self, type_id, kind)
529    }
530
531    fn get_array_base_type(&self) -> Option<TypeId> {
532        Self::get_array_base_type(self)
533    }
534
535    fn get_array_base_type_params(&self) -> &[TypeParamInfo] {
536        Self::get_array_base_type_params(self)
537    }
538
539    fn def_to_symbol_id(&self, def_id: DefId) -> Option<SymbolId> {
540        self.def_to_symbol.get(&def_id.0).copied()
541    }
542
543    fn symbol_to_def_id(&self, symbol: SymbolRef) -> Option<DefId> {
544        self.symbol_to_def.get(&symbol.0).copied()
545    }
546
547    fn get_def_kind(&self, def_id: DefId) -> Option<crate::def::DefKind> {
548        Self::get_def_kind(self, def_id)
549    }
550
551    fn is_numeric_enum(&self, def_id: DefId) -> bool {
552        Self::is_numeric_enum(self, def_id)
553    }
554
555    fn get_enum_parent_def_id(&self, member_def_id: DefId) -> Option<DefId> {
556        Self::get_enum_parent(self, member_def_id)
557    }
558
559    fn is_user_enum_def(&self, _def_id: DefId) -> bool {
560        // TypeEnvironment doesn't have access to binder symbol information
561        false
562    }
563}