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 fn symbol_to_def_id(&self, _symbol: SymbolRef) -> Option<DefId> {
197 None
198 }
199}
200
201/// Blanket implementation of `TypeResolver` for references to resolver types.
202///
203/// This allows `&dyn TypeResolver` (which is Sized) to be used wherever
204/// `R: TypeResolver` is expected.
205impl<T: TypeResolver + ?Sized> TypeResolver for &T {
206 fn resolve_ref(&self, _symbol: SymbolRef, _interner: &dyn TypeDatabase) -> Option<TypeId> {
207 // This method is deprecated - use resolve_lazy instead
208 None
209 }
210
211 fn resolve_lazy(&self, def_id: DefId, interner: &dyn TypeDatabase) -> Option<TypeId> {
212 (**self).resolve_lazy(def_id, interner)
213 }
214
215 fn get_type_params(&self, symbol: SymbolRef) -> Option<Vec<TypeParamInfo>> {
216 (**self).get_type_params(symbol)
217 }
218
219 fn get_lazy_type_params(&self, def_id: DefId) -> Option<Vec<TypeParamInfo>> {
220 (**self).get_lazy_type_params(def_id)
221 }
222
223 fn symbol_to_def_id(&self, symbol: SymbolRef) -> Option<DefId> {
224 (**self).symbol_to_def_id(symbol)
225 }
226}
227
228// =============================================================================
229// TypeEnvironment
230// =============================================================================
231
232/// A type environment that maps symbol refs to their resolved types.
233/// This is populated before type checking and passed to the `SubtypeChecker`.
234#[derive(Clone, Debug, Default)]
235pub struct TypeEnvironment {
236 /// Maps symbol references to their resolved structural types.
237 types: FxHashMap<u32, TypeId>,
238 /// Maps symbol references to their type parameters (for generic types).
239 type_params: FxHashMap<u32, Vec<TypeParamInfo>>,
240 /// Maps primitive intrinsic kinds to their boxed interface types (Rule #33).
241 boxed_types: FxHashMap<IntrinsicKind, TypeId>,
242 /// The Array<T> interface type from lib.d.ts.
243 array_base_type: Option<TypeId>,
244 /// Type parameters for the Array<T> interface (usually just [T]).
245 array_base_type_params: Vec<TypeParamInfo>,
246 /// Maps `DefIds` to their resolved structural types.
247 def_types: FxHashMap<u32, TypeId>,
248 /// Maps `DefIds` to their type parameters (for generic types with Lazy refs).
249 def_type_params: FxHashMap<u32, Vec<TypeParamInfo>>,
250 /// Maps `DefIds` back to `SymbolIds` for `InheritanceGraph` lookups.
251 def_to_symbol: FxHashMap<u32, SymbolId>,
252 /// Maps `SymbolIds` to `DefIds` for Ref -> Lazy migration.
253 symbol_to_def: FxHashMap<u32, DefId>,
254 /// Set of `DefIds` that correspond to numeric enums.
255 numeric_enums: FxHashSet<u32>,
256 /// Maps `DefIds` to their `DefKind` (Task #32: Graph Isomorphism).
257 def_kinds: FxHashMap<u32, crate::def::DefKind>,
258 /// Maps enum member `DefIds` to their parent enum `DefId`.
259 enum_parents: FxHashMap<u32, DefId>,
260 /// Maps class `DefIds` to their instance types.
261 class_instance_types: FxHashMap<u32, TypeId>,
262 /// Maps `IntrinsicKind` to all `DefIds` that correspond to that boxed type.
263 boxed_def_ids: FxHashMap<IntrinsicKind, Vec<DefId>>,
264}
265
266impl TypeEnvironment {
267 pub fn new() -> Self {
268 Self {
269 types: FxHashMap::default(),
270 type_params: FxHashMap::default(),
271 boxed_types: FxHashMap::default(),
272 array_base_type: None,
273 array_base_type_params: Vec::new(),
274 def_types: FxHashMap::default(),
275 def_type_params: FxHashMap::default(),
276 def_to_symbol: FxHashMap::default(),
277 symbol_to_def: FxHashMap::default(),
278 numeric_enums: FxHashSet::default(),
279 def_kinds: FxHashMap::default(),
280 enum_parents: FxHashMap::default(),
281 class_instance_types: FxHashMap::default(),
282 boxed_def_ids: FxHashMap::default(),
283 }
284 }
285
286 /// Register a symbol's resolved type.
287 pub fn insert(&mut self, symbol: SymbolRef, type_id: TypeId) {
288 self.types.insert(symbol.0, type_id);
289 }
290
291 /// Register a boxed type for a primitive (Rule #33).
292 pub fn set_boxed_type(&mut self, kind: IntrinsicKind, type_id: TypeId) {
293 self.boxed_types.insert(kind, type_id);
294 }
295
296 /// Get the boxed type for a primitive.
297 pub fn get_boxed_type(&self, kind: IntrinsicKind) -> Option<TypeId> {
298 self.boxed_types.get(&kind).copied()
299 }
300
301 /// Register a `DefId` as belonging to a boxed type.
302 pub fn register_boxed_def_id(&mut self, kind: IntrinsicKind, def_id: DefId) {
303 self.boxed_def_ids.entry(kind).or_default().push(def_id);
304 }
305
306 /// Check if a `DefId` corresponds to a boxed type of the given kind.
307 pub fn is_boxed_def_id(&self, def_id: DefId, kind: IntrinsicKind) -> bool {
308 self.boxed_def_ids
309 .get(&kind)
310 .is_some_and(|ids| ids.contains(&def_id))
311 }
312
313 /// Check if a `TypeId` is any known resolved form of a boxed type.
314 pub fn is_boxed_type_id(&self, type_id: TypeId, kind: IntrinsicKind) -> bool {
315 // First check the direct boxed type
316 if self.boxed_types.get(&kind).is_some_and(|&t| t == type_id) {
317 return true;
318 }
319 // Check if any registered boxed DefId resolves to this TypeId
320 if let Some(def_ids) = self.boxed_def_ids.get(&kind) {
321 for &def_id in def_ids {
322 if self.def_types.get(&def_id.0).is_some_and(|&t| t == type_id) {
323 return true;
324 }
325 }
326 }
327 false
328 }
329
330 /// Register the Array<T> interface type from lib.d.ts.
331 pub fn set_array_base_type(&mut self, type_id: TypeId, type_params: Vec<TypeParamInfo>) {
332 self.array_base_type = Some(type_id);
333 self.array_base_type_params = type_params;
334 }
335
336 /// Get the Array<T> interface type.
337 pub const fn get_array_base_type(&self) -> Option<TypeId> {
338 self.array_base_type
339 }
340
341 /// Get the type parameters for the Array<T> interface.
342 pub fn get_array_base_type_params(&self) -> &[TypeParamInfo] {
343 &self.array_base_type_params
344 }
345
346 /// Register a symbol's resolved type with type parameters.
347 pub fn insert_with_params(
348 &mut self,
349 symbol: SymbolRef,
350 type_id: TypeId,
351 params: Vec<TypeParamInfo>,
352 ) {
353 self.types.insert(symbol.0, type_id);
354 if !params.is_empty() {
355 self.type_params.insert(symbol.0, params);
356 }
357 }
358
359 /// Get a symbol's resolved type.
360 pub fn get(&self, symbol: SymbolRef) -> Option<TypeId> {
361 self.types.get(&symbol.0).copied()
362 }
363
364 /// Get a symbol's type parameters.
365 pub fn get_params(&self, symbol: SymbolRef) -> Option<&Vec<TypeParamInfo>> {
366 self.type_params.get(&symbol.0)
367 }
368
369 /// Check if the environment contains a symbol.
370 pub fn contains(&self, symbol: SymbolRef) -> bool {
371 self.types.contains_key(&symbol.0)
372 }
373
374 /// Number of resolved types.
375 pub fn len(&self) -> usize {
376 self.types.len()
377 }
378
379 /// Check if empty.
380 pub fn is_empty(&self) -> bool {
381 self.types.is_empty()
382 }
383
384 // =========================================================================
385 // DefId Resolution
386 // =========================================================================
387
388 /// Register a `DefId`'s resolved type.
389 pub fn insert_def(&mut self, def_id: DefId, type_id: TypeId) {
390 self.def_types.insert(def_id.0, type_id);
391 }
392
393 /// Register a class `DefId`'s instance type.
394 pub fn insert_class_instance_type(&mut self, def_id: DefId, instance_type: TypeId) {
395 self.class_instance_types.insert(def_id.0, instance_type);
396 }
397
398 /// Register a `DefId`'s resolved type with type parameters.
399 pub fn insert_def_with_params(
400 &mut self,
401 def_id: DefId,
402 type_id: TypeId,
403 params: Vec<TypeParamInfo>,
404 ) {
405 self.def_types.insert(def_id.0, type_id);
406 if !params.is_empty() {
407 self.def_type_params.insert(def_id.0, params);
408 }
409 }
410
411 /// Get a `DefId`'s resolved type.
412 pub fn get_def(&self, def_id: DefId) -> Option<TypeId> {
413 self.def_types.get(&def_id.0).copied()
414 }
415
416 /// Get a `DefId`'s type parameters.
417 pub fn get_def_params(&self, def_id: DefId) -> Option<&Vec<TypeParamInfo>> {
418 self.def_type_params.get(&def_id.0)
419 }
420
421 /// Check if the environment contains a `DefId`.
422 pub fn contains_def(&self, def_id: DefId) -> bool {
423 self.def_types.contains_key(&def_id.0)
424 }
425
426 /// Merge def entries (types and type params) from this environment into another.
427 pub fn merge_defs_into(&self, target: &mut Self) {
428 for (&key, &type_id) in &self.def_types {
429 target.def_types.entry(key).or_insert(type_id);
430 }
431 for (key, params) in &self.def_type_params {
432 target
433 .def_type_params
434 .entry(*key)
435 .or_insert_with(|| params.clone());
436 }
437 }
438
439 // =========================================================================
440 // DefKind Storage (Task #32: Graph Isomorphism)
441 // =========================================================================
442
443 /// Register a `DefId`'s `DefKind`.
444 pub fn insert_def_kind(&mut self, def_id: DefId, kind: crate::def::DefKind) {
445 self.def_kinds.insert(def_id.0, kind);
446 }
447
448 /// Get a `DefId`'s `DefKind`.
449 pub fn get_def_kind(&self, def_id: DefId) -> Option<crate::def::DefKind> {
450 self.def_kinds.get(&def_id.0).copied()
451 }
452
453 // =========================================================================
454 // DefId <-> SymbolId Bridge
455 // =========================================================================
456
457 /// Register a mapping from `DefId` to `SymbolId` for `InheritanceGraph` lookups.
458 ///
459 /// Also registers the reverse mapping (`SymbolId` -> `DefId`).
460 pub fn register_def_symbol_mapping(&mut self, def_id: DefId, sym_id: SymbolId) {
461 self.def_to_symbol.insert(def_id.0, sym_id);
462 self.symbol_to_def.insert(sym_id.0, def_id);
463 }
464
465 /// Register a `DefId` as a numeric enum.
466 pub fn register_numeric_enum(&mut self, def_id: DefId) {
467 self.numeric_enums.insert(def_id.0);
468 }
469
470 /// Check if a `DefId` is a numeric enum.
471 pub fn is_numeric_enum(&self, def_id: DefId) -> bool {
472 self.numeric_enums.contains(&def_id.0)
473 }
474
475 // =========================================================================
476 // Enum Parent Relationships
477 // =========================================================================
478
479 /// Register an enum member's parent enum `DefId`.
480 pub fn register_enum_parent(&mut self, member_def_id: DefId, parent_def_id: DefId) {
481 self.enum_parents.insert(member_def_id.0, parent_def_id);
482 }
483
484 /// Get the parent enum `DefId` for an enum member `DefId`.
485 pub fn get_enum_parent(&self, member_def_id: DefId) -> Option<DefId> {
486 self.enum_parents.get(&member_def_id.0).copied()
487 }
488}
489
490impl TypeResolver for TypeEnvironment {
491 fn resolve_ref(&self, symbol: SymbolRef, _interner: &dyn TypeDatabase) -> Option<TypeId> {
492 self.get(symbol)
493 }
494
495 fn resolve_lazy(&self, def_id: DefId, _interner: &dyn TypeDatabase) -> Option<TypeId> {
496 // For classes, return the instance type (type position) instead of the constructor type
497 if let Some(&instance_type) = self.class_instance_types.get(&def_id.0) {
498 return Some(instance_type);
499 }
500 self.get_def(def_id).or_else(|| {
501 // Fallback: `interner.reference(SymbolRef(N))` creates `Lazy(DefId(N))`
502 // where N is the raw SymbolId. Look up the real DefId via symbol_to_def.
503 let real_def = self.symbol_to_def.get(&def_id.0)?;
504 if let Some(&instance_type) = self.class_instance_types.get(&real_def.0) {
505 return Some(instance_type);
506 }
507 self.get_def(*real_def)
508 })
509 }
510
511 fn get_type_params(&self, symbol: SymbolRef) -> Option<Vec<TypeParamInfo>> {
512 self.get_params(symbol).cloned()
513 }
514
515 fn get_lazy_type_params(&self, def_id: DefId) -> Option<Vec<TypeParamInfo>> {
516 self.get_def_params(def_id).cloned().or_else(|| {
517 // Fallback: resolve raw SymbolId-based DefIds to real DefIds
518 let real_def = self.symbol_to_def.get(&def_id.0)?;
519 self.get_def_params(*real_def).cloned()
520 })
521 }
522
523 fn get_boxed_type(&self, kind: IntrinsicKind) -> Option<TypeId> {
524 Self::get_boxed_type(self, kind)
525 }
526
527 fn is_boxed_def_id(&self, def_id: DefId, kind: IntrinsicKind) -> bool {
528 Self::is_boxed_def_id(self, def_id, kind)
529 }
530
531 fn is_boxed_type_id(&self, type_id: TypeId, kind: IntrinsicKind) -> bool {
532 Self::is_boxed_type_id(self, type_id, kind)
533 }
534
535 fn get_array_base_type(&self) -> Option<TypeId> {
536 Self::get_array_base_type(self)
537 }
538
539 fn get_array_base_type_params(&self) -> &[TypeParamInfo] {
540 Self::get_array_base_type_params(self)
541 }
542
543 fn def_to_symbol_id(&self, def_id: DefId) -> Option<SymbolId> {
544 self.def_to_symbol.get(&def_id.0).copied()
545 }
546
547 fn symbol_to_def_id(&self, symbol: SymbolRef) -> Option<DefId> {
548 self.symbol_to_def.get(&symbol.0).copied()
549 }
550
551 fn get_def_kind(&self, def_id: DefId) -> Option<crate::def::DefKind> {
552 Self::get_def_kind(self, def_id)
553 }
554
555 fn is_numeric_enum(&self, def_id: DefId) -> bool {
556 Self::is_numeric_enum(self, def_id)
557 }
558
559 fn get_enum_parent_def_id(&self, member_def_id: DefId) -> Option<DefId> {
560 Self::get_enum_parent(self, member_def_id)
561 }
562
563 fn is_user_enum_def(&self, _def_id: DefId) -> bool {
564 // TypeEnvironment doesn't have access to binder symbol information
565 false
566 }
567}