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}