Skip to main content

tsz_solver/instantiation/
instantiate.rs

1//! Generic type instantiation and substitution.
2//!
3//! This module implements type parameter substitution for generic types.
4//! When a generic function/type is instantiated, we replace type parameters
5//! with concrete types throughout the type structure.
6//!
7//! Key features:
8//! - Type substitution map (type parameter name -> `TypeId`)
9//! - Deep recursive substitution through nested types
10//! - Handling of constraints and defaults
11
12use crate::TypeDatabase;
13#[cfg(test)]
14use crate::types::*;
15use crate::types::{
16    CallSignature, CallableShape, ConditionalType, FunctionShape, IndexSignature, IntrinsicKind,
17    LiteralValue, MappedType, ObjectShape, ParamInfo, PropertyInfo, TemplateSpan, TupleElement,
18    TypeData, TypeId, TypeParamInfo, TypePredicate,
19};
20use rustc_hash::FxHashMap;
21use tsz_common::interner::Atom;
22
23/// Maximum depth for recursive type instantiation.
24pub const MAX_INSTANTIATION_DEPTH: u32 = 50;
25
26/// A substitution map from type parameter names to concrete types.
27#[derive(Clone, Debug, Default)]
28pub struct TypeSubstitution {
29    /// Maps type parameter names to their substituted types
30    map: FxHashMap<Atom, TypeId>,
31}
32
33impl TypeSubstitution {
34    /// Create an empty substitution.
35    pub fn new() -> Self {
36        Self {
37            map: FxHashMap::default(),
38        }
39    }
40
41    /// Create a substitution from type parameters and arguments.
42    ///
43    /// `type_params` - The declared type parameters (e.g., `<T, U>`)
44    /// `type_args` - The provided type arguments (e.g., `<string, number>`)
45    ///
46    /// When `type_args` has fewer elements than `type_params`, default values
47    /// from the type parameters are used for the remaining parameters.
48    ///
49    /// IMPORTANT: Defaults may reference earlier type parameters, so they need
50    /// to be instantiated with the substitution built so far.
51    pub fn from_args(
52        interner: &dyn TypeDatabase,
53        type_params: &[TypeParamInfo],
54        type_args: &[TypeId],
55    ) -> Self {
56        let mut map = FxHashMap::default();
57        for (i, param) in type_params.iter().enumerate() {
58            let type_id = if i < type_args.len() {
59                type_args[i]
60            } else {
61                // Use default value if type argument not provided
62                match param.default {
63                    Some(default) => {
64                        // Defaults may reference earlier type parameters, so instantiate them
65                        if i > 0 && !map.is_empty() {
66                            let subst = Self { map: map.clone() };
67                            instantiate_type(interner, default, &subst)
68                        } else {
69                            default
70                        }
71                    }
72                    None => {
73                        // No default and no argument - leave this parameter unsubstituted
74                        // It will remain as a TypeParameter in the result
75                        continue;
76                    }
77                }
78            };
79            map.insert(param.name, type_id);
80        }
81        Self { map }
82    }
83
84    /// Add a single substitution.
85    pub fn insert(&mut self, name: Atom, type_id: TypeId) {
86        self.map.insert(name, type_id);
87    }
88
89    /// Look up a substitution.
90    pub fn get(&self, name: Atom) -> Option<TypeId> {
91        self.map.get(&name).copied()
92    }
93
94    /// Check if substitution is empty.
95    pub fn is_empty(&self) -> bool {
96        self.map.is_empty()
97    }
98
99    /// Number of substitutions.
100    pub fn len(&self) -> usize {
101        self.map.len()
102    }
103
104    /// Get a reference to the internal substitution map.
105    ///
106    /// This is useful for building new substitutions based on existing ones.
107    pub const fn map(&self) -> &FxHashMap<Atom, TypeId> {
108        &self.map
109    }
110}
111
112/// Instantiator for applying type substitutions.
113pub struct TypeInstantiator<'a> {
114    interner: &'a dyn TypeDatabase,
115    substitution: &'a TypeSubstitution,
116    /// Track visited types to handle cycles
117    visiting: FxHashMap<TypeId, TypeId>,
118    /// Type parameter names that are shadowed in the current scope.
119    shadowed: Vec<Atom>,
120    substitute_infer: bool,
121    /// When set, substitutes `ThisType` with this concrete type.
122    pub this_type: Option<TypeId>,
123    depth: u32,
124    max_depth: u32,
125    depth_exceeded: bool,
126}
127
128impl<'a> TypeInstantiator<'a> {
129    /// Create a new instantiator.
130    pub fn new(interner: &'a dyn TypeDatabase, substitution: &'a TypeSubstitution) -> Self {
131        TypeInstantiator {
132            interner,
133            substitution,
134            visiting: FxHashMap::default(),
135            shadowed: Vec::new(),
136            substitute_infer: false,
137            this_type: None,
138            depth: 0,
139            max_depth: MAX_INSTANTIATION_DEPTH,
140            depth_exceeded: false,
141        }
142    }
143
144    fn is_shadowed(&self, name: Atom) -> bool {
145        self.shadowed.contains(&name)
146    }
147
148    /// Instantiate a slice of properties by substituting type IDs.
149    fn instantiate_properties(&mut self, properties: &[PropertyInfo]) -> Vec<PropertyInfo> {
150        properties
151            .iter()
152            .map(|p| PropertyInfo {
153                name: p.name,
154                type_id: self.instantiate(p.type_id),
155                write_type: self.instantiate(p.write_type),
156                optional: p.optional,
157                readonly: p.readonly,
158                is_method: p.is_method,
159                visibility: p.visibility,
160                parent_id: p.parent_id,
161            })
162            .collect()
163    }
164
165    /// Instantiate an optional index signature.
166    fn instantiate_index_signature(
167        &mut self,
168        idx: Option<&IndexSignature>,
169    ) -> Option<IndexSignature> {
170        idx.map(|idx| IndexSignature {
171            key_type: self.instantiate(idx.key_type),
172            value_type: self.instantiate(idx.value_type),
173            readonly: idx.readonly,
174        })
175    }
176
177    /// Instantiate type parameter constraints and defaults.
178    fn instantiate_type_params(&mut self, type_params: &[TypeParamInfo]) -> Vec<TypeParamInfo> {
179        type_params
180            .iter()
181            .map(|tp| TypeParamInfo {
182                is_const: false,
183                name: tp.name,
184                constraint: tp.constraint.map(|c| self.instantiate(c)),
185                default: tp.default.map(|d| self.instantiate(d)),
186            })
187            .collect()
188    }
189
190    /// Instantiate function/signature parameters.
191    fn instantiate_params(&mut self, params: &[ParamInfo]) -> Vec<ParamInfo> {
192        params
193            .iter()
194            .map(|p| ParamInfo {
195                name: p.name,
196                type_id: self.instantiate(p.type_id),
197                optional: p.optional,
198                rest: p.rest,
199            })
200            .collect()
201    }
202
203    /// Enter a shadowing scope for type parameters.
204    ///
205    /// Returns `(saved_shadowed_len, saved_visiting)` for restoring via
206    /// [`exit_shadowing_scope`].
207    fn enter_shadowing_scope(
208        &mut self,
209        type_params: &[TypeParamInfo],
210    ) -> (usize, Option<FxHashMap<TypeId, TypeId>>) {
211        let shadowed_len = self.shadowed.len();
212        let saved_visiting = (!type_params.is_empty()).then(|| {
213            let saved = self.visiting.clone();
214            for tp in type_params {
215                let tp_id = self.interner.type_param(tp.clone());
216                self.visiting.remove(&tp_id);
217            }
218            saved
219        });
220        self.shadowed.extend(type_params.iter().map(|tp| tp.name));
221        (shadowed_len, saved_visiting)
222    }
223
224    /// Exit a shadowing scope, restoring the previous state.
225    fn exit_shadowing_scope(
226        &mut self,
227        shadowed_len: usize,
228        saved_visiting: Option<FxHashMap<TypeId, TypeId>>,
229    ) {
230        self.shadowed.truncate(shadowed_len);
231        if let Some(saved) = saved_visiting {
232            self.visiting = saved;
233        }
234    }
235
236    /// Apply the substitution to a type, returning the instantiated type.
237    pub fn instantiate(&mut self, type_id: TypeId) -> TypeId {
238        // Fast path: intrinsic types don't need instantiation
239        if type_id.is_intrinsic() {
240            return type_id;
241        }
242
243        if self.depth_exceeded {
244            return TypeId::ERROR;
245        }
246
247        if self.depth >= self.max_depth {
248            self.depth_exceeded = true;
249            return TypeId::ERROR;
250        }
251
252        self.depth += 1;
253        let result = self.instantiate_inner(type_id);
254        self.depth -= 1;
255        result
256    }
257
258    fn instantiate_inner(&mut self, type_id: TypeId) -> TypeId {
259        // Check if we're already processing this type (cycle detection)
260        if let Some(&cached) = self.visiting.get(&type_id) {
261            return cached;
262        }
263
264        // Look up the type structure
265        let key = match self.interner.lookup(type_id) {
266            Some(k) => k,
267            None => return type_id,
268        };
269
270        // Mark as visiting (with original ID as placeholder for cycles)
271        self.visiting.insert(type_id, type_id);
272
273        let result = self.instantiate_key(&key);
274
275        // Update the cache with the actual result
276        self.visiting.insert(type_id, result);
277
278        result
279    }
280
281    /// Instantiate a call signature.
282    fn instantiate_call_signature(&mut self, sig: &CallSignature) -> CallSignature {
283        let (shadowed_len, saved_visiting) = self.enter_shadowing_scope(&sig.type_params);
284
285        let type_predicate = sig
286            .type_predicate
287            .as_ref()
288            .map(|predicate| self.instantiate_type_predicate(predicate));
289        let this_type = sig.this_type.map(|type_id| self.instantiate(type_id));
290        let type_params = self.instantiate_type_params(&sig.type_params);
291        let params = self.instantiate_params(&sig.params);
292        let return_type = self.instantiate(sig.return_type);
293
294        self.exit_shadowing_scope(shadowed_len, saved_visiting);
295
296        CallSignature {
297            type_params,
298            params,
299            this_type,
300            return_type,
301            type_predicate,
302            is_method: sig.is_method,
303        }
304    }
305
306    fn instantiate_type_predicate(&mut self, predicate: &TypePredicate) -> TypePredicate {
307        TypePredicate {
308            asserts: predicate.asserts,
309            target: predicate.target.clone(),
310            type_id: predicate.type_id.map(|type_id| self.instantiate(type_id)),
311            parameter_index: predicate.parameter_index,
312        }
313    }
314
315    /// Instantiate a `TypeData`.
316    fn instantiate_key(&mut self, key: &TypeData) -> TypeId {
317        match key {
318            // Type parameters get substituted
319            TypeData::TypeParameter(info) => {
320                if self.is_shadowed(info.name) {
321                    return self.interner.intern(key.clone());
322                }
323                if let Some(substituted) = self.substitution.get(info.name) {
324                    substituted
325                } else {
326                    // No direct substitution found. If the type parameter has a constraint
327                    // that references substituted type parameters, instantiate the constraint.
328                    // Example: Actions extends ActionsObject<State>, with {State: number}
329                    // → use ActionsObject<number> instead of Actions
330                    if let Some(constraint) = info.constraint {
331                        let instantiated_constraint = self.instantiate(constraint);
332                        // Only use the constraint if instantiation changed it
333                        if instantiated_constraint != constraint {
334                            return instantiated_constraint;
335                        }
336                    }
337                    // No substitution and no instantiated constraint, return original
338                    self.interner.intern(key.clone())
339                }
340            }
341
342            // Intrinsics don't change
343            TypeData::Intrinsic(_) | TypeData::Literal(_) | TypeData::Error => {
344                self.interner.intern(key.clone())
345            }
346
347            // Lazy types might resolve to something that needs substitution
348            TypeData::Lazy(_)
349            | TypeData::Recursive(_)
350            | TypeData::BoundParameter(_)
351            | TypeData::TypeQuery(_)
352            | TypeData::UniqueSymbol(_)
353            | TypeData::ModuleNamespace(_) => self.interner.intern(key.clone()),
354
355            // Enum types: instantiate the member type (structural part)
356            // The DefId (nominal identity) stays the same
357            TypeData::Enum(def_id, member_type) => {
358                let instantiated_member = self.instantiate(*member_type);
359                self.interner.enum_type(*def_id, instantiated_member)
360            }
361
362            // Application: instantiate base and args
363            TypeData::Application(app_id) => {
364                let app = self.interner.type_application(*app_id);
365                let base = self.instantiate(app.base);
366                let args: Vec<TypeId> = app.args.iter().map(|&arg| self.instantiate(arg)).collect();
367                self.interner.application(base, args)
368            }
369
370            // This type: substitute with concrete this_type if provided
371            TypeData::ThisType => {
372                if let Some(this_type) = self.this_type {
373                    this_type
374                } else {
375                    self.interner.intern(key.clone())
376                }
377            }
378
379            // Union: instantiate all members
380            TypeData::Union(members) => {
381                let members = self.interner.type_list(*members);
382                let instantiated: Vec<TypeId> =
383                    members.iter().map(|&m| self.instantiate(m)).collect();
384                self.interner.union(instantiated)
385            }
386
387            // Intersection: instantiate all members
388            TypeData::Intersection(members) => {
389                let members = self.interner.type_list(*members);
390                let instantiated: Vec<TypeId> =
391                    members.iter().map(|&m| self.instantiate(m)).collect();
392                self.interner.intersection(instantiated)
393            }
394
395            // Array: instantiate element type
396            TypeData::Array(elem) => {
397                let instantiated_elem = self.instantiate(*elem);
398                self.interner.array(instantiated_elem)
399            }
400
401            // Tuple: instantiate all elements
402            TypeData::Tuple(elements) => {
403                let elements = self.interner.tuple_list(*elements);
404                let instantiated: Vec<TupleElement> = elements
405                    .iter()
406                    .map(|e| TupleElement {
407                        type_id: self.instantiate(e.type_id),
408                        name: e.name,
409                        optional: e.optional,
410                        rest: e.rest,
411                    })
412                    .collect();
413                self.interner.tuple(instantiated)
414            }
415
416            // Object: instantiate all property types
417            TypeData::Object(shape_id) => {
418                let shape = self.interner.object_shape(*shape_id);
419                let instantiated = self.instantiate_properties(&shape.properties);
420                self.interner
421                    .object_with_flags_and_symbol(instantiated, shape.flags, shape.symbol)
422            }
423
424            // Object with index signatures: instantiate all types
425            TypeData::ObjectWithIndex(shape_id) => {
426                let shape = self.interner.object_shape(*shape_id);
427                let instantiated_props = self.instantiate_properties(&shape.properties);
428                let instantiated_string_idx =
429                    self.instantiate_index_signature(shape.string_index.as_ref());
430                let instantiated_number_idx =
431                    self.instantiate_index_signature(shape.number_index.as_ref());
432                self.interner.object_with_index(ObjectShape {
433                    flags: shape.flags,
434                    properties: instantiated_props,
435                    string_index: instantiated_string_idx,
436                    number_index: instantiated_number_idx,
437                    symbol: shape.symbol,
438                })
439            }
440
441            // Function: instantiate params and return type
442            // Note: Type params in the function create a new scope - don't substitute those
443            TypeData::Function(shape_id) => {
444                let shape = self.interner.function_shape(*shape_id);
445                let (shadowed_len, saved_visiting) = self.enter_shadowing_scope(&shape.type_params);
446
447                let type_predicate = shape
448                    .type_predicate
449                    .as_ref()
450                    .map(|predicate| self.instantiate_type_predicate(predicate));
451                let this_type = shape.this_type.map(|type_id| self.instantiate(type_id));
452                let instantiated_type_params = self.instantiate_type_params(&shape.type_params);
453                let instantiated_params = self.instantiate_params(&shape.params);
454                let instantiated_return = self.instantiate(shape.return_type);
455
456                self.exit_shadowing_scope(shadowed_len, saved_visiting);
457
458                self.interner.function(FunctionShape {
459                    type_params: instantiated_type_params,
460                    params: instantiated_params,
461                    this_type,
462                    return_type: instantiated_return,
463                    type_predicate,
464                    is_constructor: shape.is_constructor,
465                    is_method: shape.is_method,
466                })
467            }
468
469            // Callable: instantiate all signatures and properties
470            TypeData::Callable(shape_id) => {
471                let shape = self.interner.callable_shape(*shape_id);
472                let instantiated_call: Vec<CallSignature> = shape
473                    .call_signatures
474                    .iter()
475                    .map(|sig| self.instantiate_call_signature(sig))
476                    .collect();
477                let instantiated_construct: Vec<CallSignature> = shape
478                    .construct_signatures
479                    .iter()
480                    .map(|sig| self.instantiate_call_signature(sig))
481                    .collect();
482                let instantiated_props = self.instantiate_properties(&shape.properties);
483
484                self.interner.callable(CallableShape {
485                    call_signatures: instantiated_call,
486                    construct_signatures: instantiated_construct,
487                    properties: instantiated_props,
488                    string_index: shape.string_index.clone(),
489                    number_index: shape.number_index.clone(),
490                    symbol: shape.symbol,
491                })
492            }
493
494            // Conditional: instantiate all parts
495            TypeData::Conditional(cond_id) => {
496                let cond = self.interner.conditional_type(*cond_id);
497                if cond.is_distributive
498                    && let Some(TypeData::TypeParameter(info)) =
499                        self.interner.lookup(cond.check_type)
500                    && !self.is_shadowed(info.name)
501                    && let Some(substituted) = self.substitution.get(info.name)
502                {
503                    // When substituting with `never`, the result is `never`
504                    if substituted == crate::types::TypeId::NEVER {
505                        return substituted;
506                    }
507                    // For `any`, we need to let evaluation handle it properly
508                    // so it can distribute to both branches
509                    // TypeScript treats `boolean` as `true | false` for distributive conditionals
510                    if substituted == TypeId::BOOLEAN {
511                        let cond_type = self.interner.conditional(cond.as_ref().clone());
512                        let mut results = Vec::with_capacity(2);
513                        for &member in &[TypeId::BOOLEAN_TRUE, TypeId::BOOLEAN_FALSE] {
514                            if self.depth_exceeded {
515                                return TypeId::ERROR;
516                            }
517                            let mut member_subst = self.substitution.clone();
518                            member_subst.insert(info.name, member);
519                            let instantiated =
520                                instantiate_type(self.interner, cond_type, &member_subst);
521                            if instantiated == TypeId::ERROR {
522                                self.depth_exceeded = true;
523                                return TypeId::ERROR;
524                            }
525                            let evaluated = crate::evaluation::evaluate::evaluate_type(
526                                self.interner,
527                                instantiated,
528                            );
529                            if evaluated == TypeId::ERROR {
530                                self.depth_exceeded = true;
531                                return TypeId::ERROR;
532                            }
533                            results.push(evaluated);
534                        }
535                        return self.interner.union(results);
536                    }
537                    if let Some(TypeData::Union(members)) = self.interner.lookup(substituted) {
538                        let members = self.interner.type_list(members);
539                        // Limit distribution to prevent OOM with large unions
540                        // (e.g., string literal unions with thousands of members)
541                        const MAX_DISTRIBUTION_SIZE: usize = 100;
542                        if members.len() > MAX_DISTRIBUTION_SIZE {
543                            self.depth_exceeded = true;
544                            return TypeId::ERROR;
545                        }
546                        let cond_type = self.interner.conditional(cond.as_ref().clone());
547                        let mut results = Vec::with_capacity(members.len());
548                        for &member in members.iter() {
549                            // Check depth before each distribution step
550                            if self.depth_exceeded {
551                                return TypeId::ERROR;
552                            }
553                            let mut member_subst = self.substitution.clone();
554                            member_subst.insert(info.name, member);
555                            let instantiated =
556                                instantiate_type(self.interner, cond_type, &member_subst);
557                            // Check if instantiation hit depth limit
558                            if instantiated == TypeId::ERROR {
559                                self.depth_exceeded = true;
560                                return TypeId::ERROR;
561                            }
562                            let evaluated = crate::evaluation::evaluate::evaluate_type(
563                                self.interner,
564                                instantiated,
565                            );
566                            // Check if evaluation hit depth limit
567                            if evaluated == TypeId::ERROR {
568                                self.depth_exceeded = true;
569                                return TypeId::ERROR;
570                            }
571                            results.push(evaluated);
572                        }
573                        return self.interner.union(results);
574                    }
575                }
576                let instantiated = ConditionalType {
577                    check_type: self.instantiate(cond.check_type),
578                    extends_type: self.instantiate(cond.extends_type),
579                    true_type: self.instantiate(cond.true_type),
580                    false_type: self.instantiate(cond.false_type),
581                    is_distributive: cond.is_distributive,
582                };
583                self.interner.conditional(instantiated)
584            }
585
586            // Mapped: instantiate constraint and template
587            TypeData::Mapped(mapped_id) => {
588                let mapped = self.interner.mapped_type(*mapped_id);
589                let tp_slice = std::slice::from_ref(&mapped.type_param);
590                let (shadowed_len, saved_visiting) = self.enter_shadowing_scope(tp_slice);
591
592                let new_constraint = self.instantiate(mapped.constraint);
593                let new_template = self.instantiate(mapped.template);
594                let new_name_type = mapped.name_type.map(|t| self.instantiate(t));
595                let new_param_constraint =
596                    mapped.type_param.constraint.map(|c| self.instantiate(c));
597                let new_param_default = mapped.type_param.default.map(|d| self.instantiate(d));
598
599                self.exit_shadowing_scope(shadowed_len, saved_visiting);
600
601                // If the mapped type is unchanged after substitution (e.g., because
602                // the mapped type's own type parameter shadowed the outer substitution),
603                // return the original to avoid eager evaluation that would collapse it.
604                let unchanged = new_constraint == mapped.constraint
605                    && new_template == mapped.template
606                    && new_name_type == mapped.name_type
607                    && new_param_constraint == mapped.type_param.constraint
608                    && new_param_default == mapped.type_param.default;
609
610                if unchanged {
611                    return self.interner.mapped((*mapped).clone());
612                }
613
614                let instantiated = MappedType {
615                    type_param: TypeParamInfo {
616                        is_const: false,
617                        name: mapped.type_param.name,
618                        constraint: new_param_constraint,
619                        default: new_param_default,
620                    },
621                    constraint: new_constraint,
622                    name_type: new_name_type,
623                    template: new_template,
624                    readonly_modifier: mapped.readonly_modifier,
625                    optional_modifier: mapped.optional_modifier,
626                };
627
628                // Trigger evaluation immediately for changed mapped types.
629                // This converts MappedType { constraint: "host"|"port", ... }
630                // into Object { host?: string, port?: number }
631                // Without this, the MappedType is returned unevaluated, causing subtype checks to fail.
632                let mapped_type = self.interner.mapped(instantiated);
633                crate::evaluation::evaluate::evaluate_type(self.interner, mapped_type)
634            }
635
636            // Index access: instantiate both parts and evaluate immediately
637            // Task #46: Meta-type reduction for O(1) equality
638            TypeData::IndexAccess(obj, idx) => {
639                let inst_obj = self.instantiate(*obj);
640                let inst_idx = self.instantiate(*idx);
641                // Don't eagerly evaluate if either part still contains type parameters.
642                // This prevents premature evaluation of `T[K]` or `T[keyof T]` where T
643                // is an inference placeholder, which would resolve through the constraint
644                // instead of waiting for the actual inferred type.
645                if crate::visitor::contains_type_parameters(self.interner, inst_obj)
646                    || crate::visitor::contains_type_parameters(self.interner, inst_idx)
647                {
648                    return self.interner.index_access(inst_obj, inst_idx);
649                }
650                // Evaluate immediately to achieve O(1) equality
651                crate::evaluation::evaluate::evaluate_index_access(
652                    self.interner,
653                    inst_obj,
654                    inst_idx,
655                )
656            }
657
658            // KeyOf: instantiate the operand and evaluate immediately
659            // Task #46: Meta-type reduction for O(1) equality
660            TypeData::KeyOf(operand) => {
661                let inst_operand = self.instantiate(*operand);
662                // Don't eagerly evaluate if the operand still contains type parameters.
663                // This prevents premature evaluation of `keyof T` where T is an inference
664                // placeholder (e.g. during compute_contextual_types), which would resolve
665                // to `keyof <constraint>` instead of waiting for T to be inferred.
666                // Without this, mapped types like `{ [P in keyof T]: ... }` collapse to `{}`
667                // because `keyof object` = `never`.
668                if crate::visitor::contains_type_parameters(self.interner, inst_operand) {
669                    return self.interner.keyof(inst_operand);
670                }
671                // Evaluate immediately to expand keyof { a: 1 } -> "a"
672                crate::evaluation::evaluate::evaluate_keyof(self.interner, inst_operand)
673            }
674
675            // ReadonlyType: instantiate the operand
676            TypeData::ReadonlyType(operand) => {
677                let inst_operand = self.instantiate(*operand);
678                self.interner.readonly_type(inst_operand)
679            }
680
681            // NoInfer: preserve wrapper, instantiate inner
682            TypeData::NoInfer(inner) => {
683                let inst_inner = self.instantiate(*inner);
684                self.interner.no_infer(inst_inner)
685            }
686
687            // Template literal: instantiate embedded types
688            // After substitution, if any type span becomes a union of string literals,
689            // we trigger evaluation to expand the template literal into a union of strings.
690            TypeData::TemplateLiteral(spans) => {
691                let spans = self.interner.template_list(*spans);
692                let mut instantiated: Vec<TemplateSpan> = Vec::with_capacity(spans.len());
693                let mut needs_evaluation = false;
694
695                for span in spans.iter() {
696                    match span {
697                        TemplateSpan::Text(t) => instantiated.push(TemplateSpan::Text(*t)),
698                        TemplateSpan::Type(t) => {
699                            let inst_type = self.instantiate(*t);
700                            // Check if this type became something that can be evaluated:
701                            // - A union of string literals
702                            // - A single string literal
703                            // - The string intrinsic type
704                            if let Some(
705                                TypeData::Union(_)
706                                | TypeData::Literal(
707                                    LiteralValue::String(_)
708                                    | LiteralValue::Number(_)
709                                    | LiteralValue::Boolean(_),
710                                )
711                                | TypeData::Intrinsic(
712                                    IntrinsicKind::String
713                                    | IntrinsicKind::Number
714                                    | IntrinsicKind::Boolean,
715                                ),
716                            ) = self.interner.lookup(inst_type)
717                            {
718                                needs_evaluation = true;
719                            }
720                            instantiated.push(TemplateSpan::Type(inst_type));
721                        }
722                    }
723                }
724
725                let template_type = self.interner.template_literal(instantiated);
726
727                // If we detected types that can be evaluated, trigger evaluation
728                // to potentially expand the template literal to a union of string literals
729                if needs_evaluation {
730                    crate::evaluation::evaluate::evaluate_type(self.interner, template_type)
731                } else {
732                    template_type
733                }
734            }
735
736            // StringIntrinsic: instantiate the type argument
737            // After substitution, if the type argument becomes a concrete type that can
738            // be evaluated (like a string literal or union), trigger evaluation.
739            TypeData::StringIntrinsic { kind, type_arg } => {
740                let inst_arg = self.instantiate(*type_arg);
741                let string_intrinsic = self.interner.string_intrinsic(*kind, inst_arg);
742
743                // Check if we can evaluate the result
744                if let Some(key) = self.interner.lookup(inst_arg) {
745                    match key {
746                        TypeData::Union(_)
747                        | TypeData::Literal(LiteralValue::String(_))
748                        | TypeData::TemplateLiteral(_)
749                        | TypeData::Intrinsic(IntrinsicKind::String) => {
750                            crate::evaluation::evaluate::evaluate_type(
751                                self.interner,
752                                string_intrinsic,
753                            )
754                        }
755                        _ => string_intrinsic,
756                    }
757                } else {
758                    string_intrinsic
759                }
760            }
761
762            // Infer: keep as-is unless explicitly substituting inference variables
763            TypeData::Infer(info) => {
764                if self.substitute_infer
765                    && !self.is_shadowed(info.name)
766                    && let Some(substituted) = self.substitution.get(info.name)
767                {
768                    return substituted;
769                }
770                self.interner.infer(info.clone())
771            }
772        }
773    }
774}
775
776/// Convenience function for instantiating a type with a substitution.
777pub fn instantiate_type(
778    interner: &dyn TypeDatabase,
779    type_id: TypeId,
780    substitution: &TypeSubstitution,
781) -> TypeId {
782    if substitution.is_empty() {
783        return type_id;
784    }
785    let mut instantiator = TypeInstantiator::new(interner, substitution);
786    let result = instantiator.instantiate(type_id);
787    if instantiator.depth_exceeded {
788        TypeId::ERROR
789    } else {
790        result
791    }
792}
793
794/// Convenience function for instantiating a type while substituting infer variables.
795pub fn instantiate_type_with_infer(
796    interner: &dyn TypeDatabase,
797    type_id: TypeId,
798    substitution: &TypeSubstitution,
799) -> TypeId {
800    if substitution.is_empty() {
801        return type_id;
802    }
803    let mut instantiator = TypeInstantiator::new(interner, substitution);
804    instantiator.substitute_infer = true;
805    let result = instantiator.instantiate(type_id);
806    if instantiator.depth_exceeded {
807        TypeId::ERROR
808    } else {
809        result
810    }
811}
812
813/// Convenience function for instantiating a generic type with type arguments.
814pub fn instantiate_generic(
815    interner: &dyn TypeDatabase,
816    type_id: TypeId,
817    type_params: &[TypeParamInfo],
818    type_args: &[TypeId],
819) -> TypeId {
820    if type_params.is_empty() || type_args.is_empty() {
821        return type_id;
822    }
823    let substitution = TypeSubstitution::from_args(interner, type_params, type_args);
824    instantiate_type(interner, type_id, &substitution)
825}
826
827/// Substitute `ThisType` with a concrete type throughout a type.
828///
829/// Used for method call return types where `this` refers to the receiver's type.
830/// For example, in a fluent builder pattern:
831/// ```typescript
832/// class Builder { setName(n: string): this { ... } }
833/// const b: Builder = new Builder().setName("foo"); // this → Builder
834/// ```
835pub fn substitute_this_type(
836    interner: &dyn TypeDatabase,
837    type_id: TypeId,
838    this_type: TypeId,
839) -> TypeId {
840    // Quick check: if the type is intrinsic, no substitution needed
841    if type_id.is_intrinsic() {
842        return type_id;
843    }
844    let empty_subst = TypeSubstitution::new();
845    let mut instantiator = TypeInstantiator::new(interner, &empty_subst);
846    instantiator.this_type = Some(this_type);
847    let result = instantiator.instantiate(type_id);
848    if instantiator.depth_exceeded {
849        TypeId::ERROR
850    } else {
851        result
852    }
853}
854
855#[cfg(test)]
856#[path = "../../tests/instantiate_tests.rs"]
857mod tests;