1use crate::TypeDatabase;
14#[cfg(test)]
15use crate::types::*;
16use crate::types::{
17 InferencePriority, IntrinsicKind, LiteralValue, TemplateSpan, TypeData, TypeId,
18};
19use crate::visitor::is_literal_type;
20use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
21use rustc_hash::{FxHashMap, FxHashSet};
22use std::cell::RefCell;
23use tsz_common::interner::Atom;
24
25fn extend_dedup<T>(target: &mut Vec<T>, items: &[T])
28where
29 T: Clone + Eq + std::hash::Hash,
30{
31 if items.is_empty() {
32 return;
33 }
34
35 if items.len() == 1 {
38 let item = &items[0];
39 if !target.contains(item) {
40 target.push(item.clone());
41 }
42 return;
43 }
44
45 let mut existing: FxHashSet<_> = target.iter().cloned().collect();
46 for item in items {
47 if existing.insert(item.clone()) {
48 target.push(item.clone());
49 }
50 }
51}
52
53#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
56pub struct InferenceVar(pub u32);
57
58#[derive(Clone, Debug, PartialEq, Eq, Hash)]
62pub struct InferenceCandidate {
63 pub type_id: TypeId,
64 pub priority: InferencePriority,
65 pub is_fresh_literal: bool,
66 pub from_object_property: bool,
67 pub object_property_index: Option<u32>,
68 pub object_property_name: Option<Atom>,
69}
70
71#[derive(Clone, Debug, Default)]
73pub struct InferenceInfo {
74 pub candidates: Vec<InferenceCandidate>,
75 pub upper_bounds: Vec<TypeId>,
76 pub resolved: Option<TypeId>,
77}
78
79impl InferenceInfo {
80 pub const fn is_empty(&self) -> bool {
81 self.candidates.is_empty() && self.upper_bounds.is_empty()
82 }
83}
84
85impl UnifyKey for InferenceVar {
86 type Value = InferenceInfo;
87
88 fn index(&self) -> u32 {
89 self.0
90 }
91
92 fn from_index(u: u32) -> Self {
93 Self(u)
94 }
95
96 fn tag() -> &'static str {
97 "InferenceVar"
98 }
99}
100
101impl UnifyValue for InferenceInfo {
102 type Error = NoError;
103
104 fn unify_values(a: &Self, b: &Self) -> Result<Self, Self::Error> {
105 let mut merged = a.clone();
106
107 extend_dedup(&mut merged.candidates, &b.candidates);
109
110 extend_dedup(&mut merged.upper_bounds, &b.upper_bounds);
112
113 if merged.resolved.is_none() {
114 merged.resolved = b.resolved;
115 }
116 Ok(merged)
117 }
118}
119
120#[derive(Clone, Debug)]
122pub enum InferenceError {
123 Conflict(TypeId, TypeId),
125 Unresolved(InferenceVar),
127 OccursCheck { var: InferenceVar, ty: TypeId },
129 BoundsViolation {
131 var: InferenceVar,
132 lower: TypeId,
133 upper: TypeId,
134 },
135 VarianceViolation {
137 var: InferenceVar,
138 expected_variance: &'static str,
139 position: TypeId,
140 },
141}
142
143#[derive(Clone, Debug, Default)]
146pub struct ConstraintSet {
147 pub lower_bounds: Vec<TypeId>,
150 pub upper_bounds: Vec<TypeId>,
153}
154
155impl ConstraintSet {
156 pub const fn new() -> Self {
157 Self {
158 lower_bounds: Vec::new(),
159 upper_bounds: Vec::new(),
160 }
161 }
162
163 pub fn from_info(info: &InferenceInfo) -> Self {
164 let mut lower_bounds = Vec::new();
165 let mut upper_bounds = Vec::new();
166 let mut seen_lower = FxHashSet::default();
167 let mut seen_upper = FxHashSet::default();
168
169 for candidate in &info.candidates {
170 if seen_lower.insert(candidate.type_id) {
171 lower_bounds.push(candidate.type_id);
172 }
173 }
174
175 for &upper in &info.upper_bounds {
176 if seen_upper.insert(upper) {
177 upper_bounds.push(upper);
178 }
179 }
180
181 Self {
182 lower_bounds,
183 upper_bounds,
184 }
185 }
186
187 pub fn add_lower_bound(&mut self, ty: TypeId) {
189 if !self.lower_bounds.contains(&ty) {
190 self.lower_bounds.push(ty);
191 }
192 }
193
194 pub fn add_upper_bound(&mut self, ty: TypeId) {
196 if !self.upper_bounds.contains(&ty) {
197 self.upper_bounds.push(ty);
198 }
199 }
200
201 pub const fn is_empty(&self) -> bool {
203 self.lower_bounds.is_empty() && self.upper_bounds.is_empty()
204 }
205
206 pub fn merge_from(&mut self, other: Self) {
207 for ty in other.lower_bounds {
208 self.add_lower_bound(ty);
209 }
210 for ty in other.upper_bounds {
211 self.add_upper_bound(ty);
212 }
213 }
214
215 pub fn transitive_reduction(&mut self, interner: &dyn TypeDatabase) {
222 if self.upper_bounds.len() < 2 {
223 return;
224 }
225
226 let mut redundant = FxHashSet::default();
227 let bounds = &self.upper_bounds;
228
229 for (i, &u1) in bounds.iter().enumerate() {
230 for (j, &u2) in bounds.iter().enumerate() {
231 if i == j || redundant.contains(&u1) || redundant.contains(&u2) {
232 continue;
233 }
234
235 if crate::relations::subtype::is_subtype_of(interner, u1, u2) {
237 redundant.insert(u2);
238 }
239 }
240 }
241
242 if !redundant.is_empty() {
243 self.upper_bounds.retain(|ty| !redundant.contains(ty));
244 }
245 }
246
247 pub fn detect_conflicts(&self, interner: &dyn TypeDatabase) -> Option<ConstraintConflict> {
250 let mut reduced_upper = self.upper_bounds.clone();
252 if reduced_upper.len() >= 2 {
253 let mut redundant = FxHashSet::default();
254 for (i, &u1) in reduced_upper.iter().enumerate() {
255 for (j, &u2) in reduced_upper.iter().enumerate() {
256 if i == j || redundant.contains(&u1) || redundant.contains(&u2) {
257 continue;
258 }
259 if crate::relations::subtype::is_subtype_of(interner, u1, u2) {
260 redundant.insert(u2);
261 }
262 }
263 }
264 if !redundant.is_empty() {
265 reduced_upper.retain(|ty| !redundant.contains(ty));
266 }
267 }
268
269 for (i, &u1) in reduced_upper.iter().enumerate() {
271 for &u2 in &reduced_upper[i + 1..] {
272 if are_disjoint(interner, u1, u2) {
273 return Some(ConstraintConflict::DisjointUpperBounds(u1, u2));
274 }
275 }
276 }
277
278 for &lower in &self.lower_bounds {
280 for &upper in &reduced_upper {
281 if lower == TypeId::ERROR
283 || upper == TypeId::ERROR
284 || lower == TypeId::ANY
285 || upper == TypeId::ANY
286 {
287 continue;
288 }
289 if !crate::relations::subtype::is_subtype_of(interner, lower, upper) {
290 return Some(ConstraintConflict::LowerExceedsUpper(lower, upper));
291 }
292 }
293 }
294
295 None
296 }
297}
298
299#[derive(Clone, Debug)]
301pub enum ConstraintConflict {
302 DisjointUpperBounds(TypeId, TypeId),
304 LowerExceedsUpper(TypeId, TypeId),
306}
307
308fn are_disjoint(interner: &dyn TypeDatabase, a: TypeId, b: TypeId) -> bool {
310 if a == b {
311 return false;
312 }
313 if a.is_any_or_unknown() || b.is_any_or_unknown() {
314 return false;
315 }
316
317 let key_a = interner.lookup(a);
318 let key_b = interner.lookup(b);
319
320 match (key_a, key_b) {
321 (Some(TypeData::Intrinsic(k1)), Some(TypeData::Intrinsic(k2))) => {
322 use IntrinsicKind::*;
323 k1 != k2 && !matches!((k1, k2), (Object | Function, _) | (_, Object | Function))
325 }
326 (Some(TypeData::Literal(l1)), Some(TypeData::Literal(l2))) => l1 != l2,
327 (Some(TypeData::Literal(l1)), Some(TypeData::Intrinsic(k2))) => {
328 !is_literal_compatible_with_intrinsic(&l1, k2)
329 }
330 (Some(TypeData::Intrinsic(k1)), Some(TypeData::Literal(l2))) => {
331 !is_literal_compatible_with_intrinsic(&l2, k1)
332 }
333 _ => false,
334 }
335}
336
337fn is_literal_compatible_with_intrinsic(lit: &LiteralValue, kind: IntrinsicKind) -> bool {
338 match lit {
339 LiteralValue::String(_) => kind == IntrinsicKind::String,
340 LiteralValue::Number(_) => kind == IntrinsicKind::Number,
341 LiteralValue::BigInt(_) => kind == IntrinsicKind::Bigint,
342 LiteralValue::Boolean(_) => kind == IntrinsicKind::Boolean,
343 }
344}
345
346pub const MAX_CONSTRAINT_ITERATIONS: usize = 100;
348
349pub const MAX_TYPE_RECURSION_DEPTH: usize = 100;
351
352pub struct InferenceContext<'a> {
354 pub(crate) interner: &'a dyn TypeDatabase,
355 pub(crate) resolver: Option<&'a dyn crate::TypeResolver>,
357 pub(crate) subtype_cache: RefCell<FxHashMap<(TypeId, TypeId), bool>>,
359 pub(crate) table: InPlaceUnificationTable<InferenceVar>,
361 pub(crate) type_params: Vec<(Atom, InferenceVar, bool)>,
363}
364
365impl<'a> InferenceContext<'a> {
366 pub(crate) const UPPER_BOUND_INTERSECTION_FAST_PATH_LIMIT: usize = 8;
367 pub(crate) const UPPER_BOUND_INTERSECTION_LARGE_SET_THRESHOLD: usize = 64;
368
369 pub fn new(interner: &'a dyn TypeDatabase) -> Self {
370 InferenceContext {
371 interner,
372 resolver: None,
373 subtype_cache: RefCell::new(FxHashMap::default()),
374 table: InPlaceUnificationTable::new(),
375 type_params: Vec::new(),
376 }
377 }
378
379 pub fn with_resolver(
380 interner: &'a dyn TypeDatabase,
381 resolver: &'a dyn crate::TypeResolver,
382 ) -> Self {
383 InferenceContext {
384 interner,
385 resolver: Some(resolver),
386 subtype_cache: RefCell::new(FxHashMap::default()),
387 table: InPlaceUnificationTable::new(),
388 type_params: Vec::new(),
389 }
390 }
391
392 pub fn fresh_var(&mut self) -> InferenceVar {
394 self.table.new_key(InferenceInfo::default())
395 }
396
397 pub fn fresh_type_param(&mut self, name: Atom, is_const: bool) -> InferenceVar {
399 let var = self.fresh_var();
400 self.type_params.push((name, var, is_const));
401 var
402 }
403
404 pub fn register_type_param(&mut self, name: Atom, var: InferenceVar, is_const: bool) {
409 self.type_params.push((name, var, is_const));
410 }
411
412 pub fn find_type_param(&self, name: Atom) -> Option<InferenceVar> {
414 self.type_params
415 .iter()
416 .find(|(n, _, _)| *n == name)
417 .map(|(_, v, _)| *v)
418 }
419
420 pub fn is_var_const(&mut self, var: InferenceVar) -> bool {
422 let root = self.table.find(var);
423 self.type_params
424 .iter()
425 .any(|(_, v, is_const)| self.table.find(*v) == root && *is_const)
426 }
427
428 pub fn probe(&mut self, var: InferenceVar) -> Option<TypeId> {
430 self.table.probe_value(var).resolved
431 }
432
433 pub fn unify_var_type(&mut self, var: InferenceVar, ty: TypeId) -> Result<(), InferenceError> {
435 let root = self.table.find(var);
437
438 if self.occurs_in(root, ty) {
439 return Err(InferenceError::OccursCheck { var: root, ty });
440 }
441
442 match self.table.probe_value(root).resolved {
444 None => {
445 self.table.union_value(
446 root,
447 InferenceInfo {
448 resolved: Some(ty),
449 ..InferenceInfo::default()
450 },
451 );
452 Ok(())
453 }
454 Some(existing) => {
455 if self.types_compatible(existing, ty) {
456 Ok(())
457 } else {
458 Err(InferenceError::Conflict(existing, ty))
459 }
460 }
461 }
462 }
463
464 pub fn unify_vars(&mut self, a: InferenceVar, b: InferenceVar) -> Result<(), InferenceError> {
466 let root_a = self.table.find(a);
467 let root_b = self.table.find(b);
468
469 if root_a == root_b {
470 return Ok(());
471 }
472
473 let value_a = self.table.probe_value(root_a).resolved;
474 let value_b = self.table.probe_value(root_b).resolved;
475 if let (Some(a_ty), Some(b_ty)) = (value_a, value_b)
476 && !self.types_compatible(a_ty, b_ty)
477 {
478 return Err(InferenceError::Conflict(a_ty, b_ty));
479 }
480
481 self.table
482 .unify_var_var(root_a, root_b)
483 .map_err(|_| InferenceError::Conflict(TypeId::ERROR, TypeId::ERROR))?;
484 Ok(())
485 }
486
487 fn types_compatible(&self, a: TypeId, b: TypeId) -> bool {
489 if a == b {
490 return true;
491 }
492
493 if a == TypeId::ANY || b == TypeId::ANY {
495 return true;
496 }
497
498 if a == TypeId::UNKNOWN || b == TypeId::UNKNOWN {
500 return true;
501 }
502
503 if a == TypeId::NEVER || b == TypeId::NEVER {
505 return true;
506 }
507
508 false
509 }
510
511 pub(crate) fn occurs_in(&mut self, var: InferenceVar, ty: TypeId) -> bool {
512 let root = self.table.find(var);
513 if self.type_params.is_empty() {
514 return false;
515 }
516
517 let mut visited = FxHashSet::default();
518 for &(atom, param_var, _) in &self.type_params {
519 if self.table.find(param_var) == root
520 && self.type_contains_param(ty, atom, &mut visited)
521 {
522 return true;
523 }
524 }
525 false
526 }
527
528 pub(crate) fn type_param_names_for_root(&mut self, root: InferenceVar) -> Vec<Atom> {
529 self.type_params
530 .iter()
531 .filter(|&(_name, var, _)| self.table.find(*var) == root)
532 .map(|(name, _var, _)| *name)
533 .collect()
534 }
535
536 pub(crate) fn upper_bound_cycles_param(&mut self, bound: TypeId, targets: &[Atom]) -> bool {
537 let mut params = FxHashSet::default();
538 let mut visited = FxHashSet::default();
539 self.collect_type_params(bound, &mut params, &mut visited);
540
541 for name in params {
542 let mut seen = FxHashSet::default();
543 if self.param_depends_on_targets(name, targets, &mut seen) {
544 return true;
545 }
546 }
547
548 false
549 }
550
551 pub(crate) fn expand_cyclic_upper_bound(
552 &mut self,
553 root: InferenceVar,
554 bound: TypeId,
555 target_names: &[Atom],
556 candidates: &mut Vec<InferenceCandidate>,
557 upper_bounds: &mut Vec<TypeId>,
558 ) {
559 let name = match self.interner.lookup(bound) {
560 Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) => info.name,
561 _ => return,
562 };
563
564 let Some(var) = self.find_type_param(name) else {
565 return;
566 };
567
568 if let Some(resolved) = self.probe(var) {
569 if !upper_bounds.contains(&resolved) {
570 upper_bounds.push(resolved);
571 }
572 return;
573 }
574
575 let bound_root = self.table.find(var);
576 let info = self.table.probe_value(bound_root);
577
578 for candidate in info.candidates {
579 if self.occurs_in(root, candidate.type_id) {
580 continue;
581 }
582 candidates.push(InferenceCandidate {
583 type_id: candidate.type_id,
584 priority: InferencePriority::Circular,
585 is_fresh_literal: candidate.is_fresh_literal,
586 from_object_property: candidate.from_object_property,
587 object_property_index: candidate.object_property_index,
588 object_property_name: candidate.object_property_name,
589 });
590 }
591
592 for ty in info.upper_bounds {
593 if self.occurs_in(root, ty) {
594 continue;
595 }
596 if !target_names.is_empty() && self.upper_bound_cycles_param(ty, target_names) {
597 continue;
598 }
599 if !upper_bounds.contains(&ty) {
600 upper_bounds.push(ty);
601 }
602 }
603 }
604
605 fn collect_type_params(
606 &self,
607 ty: TypeId,
608 params: &mut FxHashSet<Atom>,
609 visited: &mut FxHashSet<TypeId>,
610 ) {
611 if !visited.insert(ty) {
612 return;
613 }
614 let Some(key) = self.interner.lookup(ty) else {
615 return;
616 };
617
618 match key {
619 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
620 params.insert(info.name);
621 }
622 TypeData::Array(elem) => {
623 self.collect_type_params(elem, params, visited);
624 }
625 TypeData::Tuple(elements) => {
626 let elements = self.interner.tuple_list(elements);
627 for element in elements.iter() {
628 self.collect_type_params(element.type_id, params, visited);
629 }
630 }
631 TypeData::Union(members) | TypeData::Intersection(members) => {
632 let members = self.interner.type_list(members);
633 for &member in members.iter() {
634 self.collect_type_params(member, params, visited);
635 }
636 }
637 TypeData::Object(shape_id) => {
638 let shape = self.interner.object_shape(shape_id);
639 for prop in &shape.properties {
640 self.collect_type_params(prop.type_id, params, visited);
641 }
642 }
643 TypeData::ObjectWithIndex(shape_id) => {
644 let shape = self.interner.object_shape(shape_id);
645 for prop in &shape.properties {
646 self.collect_type_params(prop.type_id, params, visited);
647 }
648 if let Some(index) = shape.string_index.as_ref() {
649 self.collect_type_params(index.key_type, params, visited);
650 self.collect_type_params(index.value_type, params, visited);
651 }
652 if let Some(index) = shape.number_index.as_ref() {
653 self.collect_type_params(index.key_type, params, visited);
654 self.collect_type_params(index.value_type, params, visited);
655 }
656 }
657 TypeData::Application(app_id) => {
658 let app = self.interner.type_application(app_id);
659 self.collect_type_params(app.base, params, visited);
660 for &arg in &app.args {
661 self.collect_type_params(arg, params, visited);
662 }
663 }
664 TypeData::Function(shape_id) => {
665 let shape = self.interner.function_shape(shape_id);
666 for param in &shape.params {
667 self.collect_type_params(param.type_id, params, visited);
668 }
669 if let Some(this_type) = shape.this_type {
670 self.collect_type_params(this_type, params, visited);
671 }
672 self.collect_type_params(shape.return_type, params, visited);
673 }
674 TypeData::Callable(shape_id) => {
675 let shape = self.interner.callable_shape(shape_id);
676 for sig in &shape.call_signatures {
677 for param in &sig.params {
678 self.collect_type_params(param.type_id, params, visited);
679 }
680 if let Some(this_type) = sig.this_type {
681 self.collect_type_params(this_type, params, visited);
682 }
683 self.collect_type_params(sig.return_type, params, visited);
684 }
685 for sig in &shape.construct_signatures {
686 for param in &sig.params {
687 self.collect_type_params(param.type_id, params, visited);
688 }
689 if let Some(this_type) = sig.this_type {
690 self.collect_type_params(this_type, params, visited);
691 }
692 self.collect_type_params(sig.return_type, params, visited);
693 }
694 for prop in &shape.properties {
695 self.collect_type_params(prop.type_id, params, visited);
696 }
697 }
698 TypeData::Conditional(cond_id) => {
699 let cond = self.interner.conditional_type(cond_id);
700 self.collect_type_params(cond.check_type, params, visited);
701 self.collect_type_params(cond.extends_type, params, visited);
702 self.collect_type_params(cond.true_type, params, visited);
703 self.collect_type_params(cond.false_type, params, visited);
704 }
705 TypeData::Mapped(mapped_id) => {
706 let mapped = self.interner.mapped_type(mapped_id);
707 self.collect_type_params(mapped.constraint, params, visited);
708 if let Some(name_type) = mapped.name_type {
709 self.collect_type_params(name_type, params, visited);
710 }
711 self.collect_type_params(mapped.template, params, visited);
712 }
713 TypeData::IndexAccess(obj, idx) => {
714 self.collect_type_params(obj, params, visited);
715 self.collect_type_params(idx, params, visited);
716 }
717 TypeData::KeyOf(operand) | TypeData::ReadonlyType(operand) => {
718 self.collect_type_params(operand, params, visited);
719 }
720 TypeData::TemplateLiteral(spans) => {
721 let spans = self.interner.template_list(spans);
722 for span in spans.iter() {
723 if let TemplateSpan::Type(inner) = span {
724 self.collect_type_params(*inner, params, visited);
725 }
726 }
727 }
728 TypeData::StringIntrinsic { type_arg, .. } => {
729 self.collect_type_params(type_arg, params, visited);
730 }
731 TypeData::Enum(_def_id, member_type) => {
732 self.collect_type_params(member_type, params, visited);
734 }
735 TypeData::Intrinsic(_)
736 | TypeData::Literal(_)
737 | TypeData::Lazy(_)
738 | TypeData::Recursive(_)
739 | TypeData::BoundParameter(_)
740 | TypeData::TypeQuery(_)
741 | TypeData::UniqueSymbol(_)
742 | TypeData::ThisType
743 | TypeData::ModuleNamespace(_)
744 | TypeData::Error => {}
745 TypeData::NoInfer(inner) => {
746 self.collect_type_params(inner, params, visited);
747 }
748 }
749 }
750
751 fn param_depends_on_targets(
752 &mut self,
753 name: Atom,
754 targets: &[Atom],
755 visited: &mut FxHashSet<Atom>,
756 ) -> bool {
757 if targets.contains(&name) {
758 return true;
759 }
760 if !visited.insert(name) {
761 return false;
762 }
763 let Some(var) = self.find_type_param(name) else {
764 return false;
765 };
766 let root = self.table.find(var);
767 let upper_bounds = self.table.probe_value(root).upper_bounds;
768
769 for bound in upper_bounds {
770 for target in targets {
771 let mut seen = FxHashSet::default();
772 if self.type_contains_param(bound, *target, &mut seen) {
773 return true;
774 }
775 }
776 if let Some(TypeData::TypeParameter(info)) = self.interner.lookup(bound)
777 && self.param_depends_on_targets(info.name, targets, visited)
778 {
779 return true;
780 }
781 }
782
783 false
784 }
785
786 fn type_contains_param(
787 &self,
788 ty: TypeId,
789 target: Atom,
790 visited: &mut FxHashSet<TypeId>,
791 ) -> bool {
792 if !visited.insert(ty) {
793 return false;
794 }
795
796 let key = match self.interner.lookup(ty) {
797 Some(key) => key,
798 None => return false,
799 };
800
801 match key {
802 TypeData::TypeParameter(info) | TypeData::Infer(info) => info.name == target,
803 TypeData::Array(elem) => self.type_contains_param(elem, target, visited),
804 TypeData::Tuple(elements) => {
805 let elements = self.interner.tuple_list(elements);
806 elements
807 .iter()
808 .any(|e| self.type_contains_param(e.type_id, target, visited))
809 }
810 TypeData::Union(members) | TypeData::Intersection(members) => {
811 let members = self.interner.type_list(members);
812 members
813 .iter()
814 .any(|&member| self.type_contains_param(member, target, visited))
815 }
816 TypeData::Object(shape_id) => {
817 let shape = self.interner.object_shape(shape_id);
818 shape
819 .properties
820 .iter()
821 .any(|p| self.type_contains_param(p.type_id, target, visited))
822 }
823 TypeData::ObjectWithIndex(shape_id) => {
824 let shape = self.interner.object_shape(shape_id);
825 shape
826 .properties
827 .iter()
828 .any(|p| self.type_contains_param(p.type_id, target, visited))
829 || shape.string_index.as_ref().is_some_and(|idx| {
830 self.type_contains_param(idx.key_type, target, visited)
831 || self.type_contains_param(idx.value_type, target, visited)
832 })
833 || shape.number_index.as_ref().is_some_and(|idx| {
834 self.type_contains_param(idx.key_type, target, visited)
835 || self.type_contains_param(idx.value_type, target, visited)
836 })
837 }
838 TypeData::Application(app_id) => {
839 let app = self.interner.type_application(app_id);
840 self.type_contains_param(app.base, target, visited)
841 || app
842 .args
843 .iter()
844 .any(|&arg| self.type_contains_param(arg, target, visited))
845 }
846 TypeData::Function(shape_id) => {
847 let shape = self.interner.function_shape(shape_id);
848 if shape.type_params.iter().any(|tp| tp.name == target) {
849 return false;
850 }
851 shape
852 .this_type
853 .is_some_and(|this_type| self.type_contains_param(this_type, target, visited))
854 || shape
855 .params
856 .iter()
857 .any(|p| self.type_contains_param(p.type_id, target, visited))
858 || self.type_contains_param(shape.return_type, target, visited)
859 }
860 TypeData::Callable(shape_id) => {
861 let shape = self.interner.callable_shape(shape_id);
862 let in_call = shape.call_signatures.iter().any(|sig| {
863 if sig.type_params.iter().any(|tp| tp.name == target) {
864 false
865 } else {
866 sig.this_type.is_some_and(|this_type| {
867 self.type_contains_param(this_type, target, visited)
868 }) || sig
869 .params
870 .iter()
871 .any(|p| self.type_contains_param(p.type_id, target, visited))
872 || self.type_contains_param(sig.return_type, target, visited)
873 }
874 });
875 if in_call {
876 return true;
877 }
878 let in_construct = shape.construct_signatures.iter().any(|sig| {
879 if sig.type_params.iter().any(|tp| tp.name == target) {
880 false
881 } else {
882 sig.this_type.is_some_and(|this_type| {
883 self.type_contains_param(this_type, target, visited)
884 }) || sig
885 .params
886 .iter()
887 .any(|p| self.type_contains_param(p.type_id, target, visited))
888 || self.type_contains_param(sig.return_type, target, visited)
889 }
890 });
891 if in_construct {
892 return true;
893 }
894 shape
895 .properties
896 .iter()
897 .any(|p| self.type_contains_param(p.type_id, target, visited))
898 }
899 TypeData::Conditional(cond_id) => {
900 let cond = self.interner.conditional_type(cond_id);
901 self.type_contains_param(cond.check_type, target, visited)
902 || self.type_contains_param(cond.extends_type, target, visited)
903 || self.type_contains_param(cond.true_type, target, visited)
904 || self.type_contains_param(cond.false_type, target, visited)
905 }
906 TypeData::Mapped(mapped_id) => {
907 let mapped = self.interner.mapped_type(mapped_id);
908 if mapped.type_param.name == target {
909 return false;
910 }
911 self.type_contains_param(mapped.constraint, target, visited)
912 || self.type_contains_param(mapped.template, target, visited)
913 }
914 TypeData::IndexAccess(obj, idx) => {
915 self.type_contains_param(obj, target, visited)
916 || self.type_contains_param(idx, target, visited)
917 }
918 TypeData::KeyOf(operand) | TypeData::ReadonlyType(operand) => {
919 self.type_contains_param(operand, target, visited)
920 }
921 TypeData::TemplateLiteral(spans) => {
922 let spans = self.interner.template_list(spans);
923 spans.iter().any(|span| match span {
924 TemplateSpan::Text(_) => false,
925 TemplateSpan::Type(inner) => self.type_contains_param(*inner, target, visited),
926 })
927 }
928 TypeData::StringIntrinsic { type_arg, .. } => {
929 self.type_contains_param(type_arg, target, visited)
930 }
931 TypeData::Enum(_def_id, member_type) => {
932 self.type_contains_param(member_type, target, visited)
934 }
935 TypeData::Intrinsic(_)
936 | TypeData::Literal(_)
937 | TypeData::Lazy(_)
938 | TypeData::Recursive(_)
939 | TypeData::BoundParameter(_)
940 | TypeData::TypeQuery(_)
941 | TypeData::UniqueSymbol(_)
942 | TypeData::ThisType
943 | TypeData::ModuleNamespace(_)
944 | TypeData::Error => false,
945 TypeData::NoInfer(inner) => self.type_contains_param(inner, target, visited),
946 }
947 }
948
949 pub fn resolve_all(&mut self) -> Result<Vec<(Atom, TypeId)>, InferenceError> {
951 let type_params: Vec<_> = self.type_params.clone();
953 let mut results = Vec::new();
954 for (name, var, _) in type_params {
955 match self.probe(var) {
956 Some(ty) => results.push((name, ty)),
957 None => return Err(InferenceError::Unresolved(var)),
958 }
959 }
960 Ok(results)
961 }
962
963 pub fn interner(&self) -> &dyn TypeDatabase {
965 self.interner
966 }
967
968 pub fn add_lower_bound(&mut self, var: InferenceVar, ty: TypeId) {
976 self.add_candidate(var, ty, InferencePriority::NakedTypeVariable);
977 }
978
979 pub fn add_candidate(&mut self, var: InferenceVar, ty: TypeId, priority: InferencePriority) {
981 self.add_candidate_with_context(var, ty, priority, false, None, None);
982 }
983
984 pub fn add_property_candidate(
988 &mut self,
989 var: InferenceVar,
990 ty: TypeId,
991 priority: InferencePriority,
992 ) {
993 self.add_candidate_with_context(var, ty, priority, true, None, None);
994 }
995
996 pub fn add_property_candidate_with_index(
1000 &mut self,
1001 var: InferenceVar,
1002 ty: TypeId,
1003 priority: InferencePriority,
1004 object_property_index: u32,
1005 object_property_name: Option<Atom>,
1006 ) {
1007 self.add_candidate_with_context(
1008 var,
1009 ty,
1010 priority,
1011 true,
1012 Some(object_property_index),
1013 object_property_name,
1014 );
1015 }
1016
1017 fn add_candidate_with_context(
1018 &mut self,
1019 var: InferenceVar,
1020 ty: TypeId,
1021 priority: InferencePriority,
1022 from_object_property: bool,
1023 object_property_index: Option<u32>,
1024 object_property_name: Option<Atom>,
1025 ) {
1026 let root = self.table.find(var);
1027 let candidate = InferenceCandidate {
1028 type_id: ty,
1029 priority,
1030 is_fresh_literal: is_literal_type(self.interner, ty),
1031 from_object_property,
1032 object_property_index,
1033 object_property_name,
1034 };
1035 self.table.union_value(
1036 root,
1037 InferenceInfo {
1038 candidates: vec![candidate],
1039 ..InferenceInfo::default()
1040 },
1041 );
1042 }
1043
1044 pub fn add_upper_bound(&mut self, var: InferenceVar, ty: TypeId) {
1047 let root = self.table.find(var);
1048 self.table.union_value(
1049 root,
1050 InferenceInfo {
1051 upper_bounds: vec![ty],
1052 ..InferenceInfo::default()
1053 },
1054 );
1055 }
1056
1057 pub fn get_constraints(&mut self, var: InferenceVar) -> Option<ConstraintSet> {
1059 let root = self.table.find(var);
1060 let info = self.table.probe_value(root);
1061 if info.is_empty() {
1062 None
1063 } else {
1064 Some(ConstraintSet::from_info(&info))
1065 }
1066 }
1067
1068 pub fn all_candidates_are_return_type(&mut self, var: InferenceVar) -> bool {
1072 let root = self.table.find(var);
1073 let info = self.table.probe_value(root);
1074 !info.candidates.is_empty()
1075 && info
1076 .candidates
1077 .iter()
1078 .all(|c| c.priority == InferencePriority::ReturnType)
1079 }
1080
1081 pub fn get_literal_candidates(&mut self, var: InferenceVar) -> Vec<TypeId> {
1083 let root = self.table.find(var);
1084 let info = self.table.probe_value(root);
1085 info.candidates
1086 .iter()
1087 .filter(|c| c.is_fresh_literal)
1088 .map(|c| c.type_id)
1089 .collect()
1090 }
1091
1092 pub const fn collect_constraint(&mut self, _source: TypeId, _target: TypeId) {
1096 }
1100}
1101
1102#[cfg(test)]
1105#[path = "../../tests/infer_tests.rs"]
1106mod tests;