1use crate::TypeDatabase;
14#[cfg(test)]
15use crate::types::*;
16use crate::types::{
17 CallableShapeId, FunctionShapeId, InferencePriority, IntrinsicKind, LiteralValue, MappedTypeId,
18 ObjectShapeId, TemplateLiteralId, TemplateSpan, TupleElement, TupleListId, TypeApplicationId,
19 TypeData, TypeId, TypeListId,
20};
21use crate::visitor::is_literal_type;
22use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
23use rustc_hash::{FxHashMap, FxHashSet};
24use std::cell::RefCell;
25use tsz_common::interner::Atom;
26
27fn extend_dedup<T>(target: &mut Vec<T>, items: &[T])
30where
31 T: Clone + Eq + std::hash::Hash,
32{
33 if items.is_empty() {
34 return;
35 }
36
37 if items.len() == 1 {
40 let item = &items[0];
41 if !target.contains(item) {
42 target.push(item.clone());
43 }
44 return;
45 }
46
47 let mut existing: FxHashSet<_> = target.iter().cloned().collect();
48 for item in items {
49 if existing.insert(item.clone()) {
50 target.push(item.clone());
51 }
52 }
53}
54
55#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
58pub struct InferenceVar(pub u32);
59
60#[derive(Clone, Debug, PartialEq, Eq, Hash)]
64pub struct InferenceCandidate {
65 pub type_id: TypeId,
66 pub priority: InferencePriority,
67 pub is_fresh_literal: bool,
68}
69
70#[derive(Clone, Debug, Default)]
72pub struct InferenceInfo {
73 pub candidates: Vec<InferenceCandidate>,
74 pub upper_bounds: Vec<TypeId>,
75 pub resolved: Option<TypeId>,
76}
77
78impl InferenceInfo {
79 pub const fn is_empty(&self) -> bool {
80 self.candidates.is_empty() && self.upper_bounds.is_empty()
81 }
82}
83
84impl UnifyKey for InferenceVar {
85 type Value = InferenceInfo;
86
87 fn index(&self) -> u32 {
88 self.0
89 }
90
91 fn from_index(u: u32) -> Self {
92 Self(u)
93 }
94
95 fn tag() -> &'static str {
96 "InferenceVar"
97 }
98}
99
100impl UnifyValue for InferenceInfo {
101 type Error = NoError;
102
103 fn unify_values(a: &Self, b: &Self) -> Result<Self, Self::Error> {
104 let mut merged = a.clone();
105
106 extend_dedup(&mut merged.candidates, &b.candidates);
108
109 extend_dedup(&mut merged.upper_bounds, &b.upper_bounds);
111
112 if merged.resolved.is_none() {
113 merged.resolved = b.resolved;
114 }
115 Ok(merged)
116 }
117}
118
119#[derive(Clone, Debug)]
121pub enum InferenceError {
122 Conflict(TypeId, TypeId),
124 Unresolved(InferenceVar),
126 OccursCheck { var: InferenceVar, ty: TypeId },
128 BoundsViolation {
130 var: InferenceVar,
131 lower: TypeId,
132 upper: TypeId,
133 },
134 VarianceViolation {
136 var: InferenceVar,
137 expected_variance: &'static str,
138 position: TypeId,
139 },
140}
141
142#[derive(Clone, Debug, Default)]
145pub struct ConstraintSet {
146 pub lower_bounds: Vec<TypeId>,
149 pub upper_bounds: Vec<TypeId>,
152}
153
154impl ConstraintSet {
155 pub const fn new() -> Self {
156 Self {
157 lower_bounds: Vec::new(),
158 upper_bounds: Vec::new(),
159 }
160 }
161
162 pub fn from_info(info: &InferenceInfo) -> Self {
163 let mut lower_bounds = Vec::new();
164 let mut upper_bounds = Vec::new();
165 let mut seen_lower = FxHashSet::default();
166 let mut seen_upper = FxHashSet::default();
167
168 for candidate in &info.candidates {
169 if seen_lower.insert(candidate.type_id) {
170 lower_bounds.push(candidate.type_id);
171 }
172 }
173
174 for &upper in &info.upper_bounds {
175 if seen_upper.insert(upper) {
176 upper_bounds.push(upper);
177 }
178 }
179
180 Self {
181 lower_bounds,
182 upper_bounds,
183 }
184 }
185
186 pub fn add_lower_bound(&mut self, ty: TypeId) {
188 if !self.lower_bounds.contains(&ty) {
189 self.lower_bounds.push(ty);
190 }
191 }
192
193 pub fn add_upper_bound(&mut self, ty: TypeId) {
195 if !self.upper_bounds.contains(&ty) {
196 self.upper_bounds.push(ty);
197 }
198 }
199
200 pub const fn is_empty(&self) -> bool {
202 self.lower_bounds.is_empty() && self.upper_bounds.is_empty()
203 }
204
205 pub fn merge_from(&mut self, other: Self) {
206 for ty in other.lower_bounds {
207 self.add_lower_bound(ty);
208 }
209 for ty in other.upper_bounds {
210 self.add_upper_bound(ty);
211 }
212 }
213
214 pub fn transitive_reduction(&mut self, interner: &dyn TypeDatabase) {
221 if self.upper_bounds.len() < 2 {
222 return;
223 }
224
225 let mut redundant = FxHashSet::default();
226 let bounds = &self.upper_bounds;
227
228 for (i, &u1) in bounds.iter().enumerate() {
229 for (j, &u2) in bounds.iter().enumerate() {
230 if i == j || redundant.contains(&u1) || redundant.contains(&u2) {
231 continue;
232 }
233
234 if crate::subtype::is_subtype_of(interner, u1, u2) {
236 redundant.insert(u2);
237 }
238 }
239 }
240
241 if !redundant.is_empty() {
242 self.upper_bounds.retain(|ty| !redundant.contains(ty));
243 }
244 }
245
246 pub fn detect_conflicts(&self, interner: &dyn TypeDatabase) -> Option<ConstraintConflict> {
249 let mut reduced_upper = self.upper_bounds.clone();
251 if reduced_upper.len() >= 2 {
252 let mut redundant = FxHashSet::default();
253 for (i, &u1) in reduced_upper.iter().enumerate() {
254 for (j, &u2) in reduced_upper.iter().enumerate() {
255 if i == j || redundant.contains(&u1) || redundant.contains(&u2) {
256 continue;
257 }
258 if crate::subtype::is_subtype_of(interner, u1, u2) {
259 redundant.insert(u2);
260 }
261 }
262 }
263 if !redundant.is_empty() {
264 reduced_upper.retain(|ty| !redundant.contains(ty));
265 }
266 }
267
268 for (i, &u1) in reduced_upper.iter().enumerate() {
270 for &u2 in &reduced_upper[i + 1..] {
271 if are_disjoint(interner, u1, u2) {
272 return Some(ConstraintConflict::DisjointUpperBounds(u1, u2));
273 }
274 }
275 }
276
277 for &lower in &self.lower_bounds {
279 for &upper in &reduced_upper {
280 if lower == TypeId::ERROR
282 || upper == TypeId::ERROR
283 || lower == TypeId::ANY
284 || upper == TypeId::ANY
285 {
286 continue;
287 }
288 if !crate::subtype::is_subtype_of(interner, lower, upper) {
289 return Some(ConstraintConflict::LowerExceedsUpper(lower, upper));
290 }
291 }
292 }
293
294 None
295 }
296}
297
298#[derive(Clone, Debug)]
300pub enum ConstraintConflict {
301 DisjointUpperBounds(TypeId, TypeId),
303 LowerExceedsUpper(TypeId, TypeId),
305}
306
307fn are_disjoint(interner: &dyn TypeDatabase, a: TypeId, b: TypeId) -> bool {
309 if a == b {
310 return false;
311 }
312 if a.is_any_or_unknown() || b.is_any_or_unknown() {
313 return false;
314 }
315
316 let key_a = interner.lookup(a);
317 let key_b = interner.lookup(b);
318
319 match (key_a, key_b) {
320 (Some(TypeData::Intrinsic(k1)), Some(TypeData::Intrinsic(k2))) => {
321 use IntrinsicKind::*;
322 k1 != k2 && !matches!((k1, k2), (Object | Function, _) | (_, Object | Function))
324 }
325 (Some(TypeData::Literal(l1)), Some(TypeData::Literal(l2))) => l1 != l2,
326 (Some(TypeData::Literal(l1)), Some(TypeData::Intrinsic(k2))) => {
327 !is_literal_compatible_with_intrinsic(&l1, k2)
328 }
329 (Some(TypeData::Intrinsic(k1)), Some(TypeData::Literal(l2))) => {
330 !is_literal_compatible_with_intrinsic(&l2, k1)
331 }
332 _ => false,
333 }
334}
335
336fn is_literal_compatible_with_intrinsic(lit: &LiteralValue, kind: IntrinsicKind) -> bool {
337 match lit {
338 LiteralValue::String(_) => kind == IntrinsicKind::String,
339 LiteralValue::Number(_) => kind == IntrinsicKind::Number,
340 LiteralValue::BigInt(_) => kind == IntrinsicKind::Bigint,
341 LiteralValue::Boolean(_) => kind == IntrinsicKind::Boolean,
342 }
343}
344
345pub const MAX_CONSTRAINT_ITERATIONS: usize = 100;
347
348pub const MAX_TYPE_RECURSION_DEPTH: usize = 100;
350
351pub struct InferenceContext<'a> {
353 pub(crate) interner: &'a dyn TypeDatabase,
354 pub(crate) resolver: Option<&'a dyn crate::TypeResolver>,
356 pub(crate) subtype_cache: RefCell<FxHashMap<(TypeId, TypeId), bool>>,
358 pub(crate) table: InPlaceUnificationTable<InferenceVar>,
360 pub(crate) type_params: Vec<(Atom, InferenceVar, bool)>,
362}
363
364impl<'a> InferenceContext<'a> {
365 pub(crate) const UPPER_BOUND_INTERSECTION_FAST_PATH_LIMIT: usize = 8;
366 pub(crate) const UPPER_BOUND_INTERSECTION_LARGE_SET_THRESHOLD: usize = 64;
367
368 pub fn new(interner: &'a dyn TypeDatabase) -> Self {
369 InferenceContext {
370 interner,
371 resolver: None,
372 subtype_cache: RefCell::new(FxHashMap::default()),
373 table: InPlaceUnificationTable::new(),
374 type_params: Vec::new(),
375 }
376 }
377
378 pub fn with_resolver(
379 interner: &'a dyn TypeDatabase,
380 resolver: &'a dyn crate::TypeResolver,
381 ) -> Self {
382 InferenceContext {
383 interner,
384 resolver: Some(resolver),
385 subtype_cache: RefCell::new(FxHashMap::default()),
386 table: InPlaceUnificationTable::new(),
387 type_params: Vec::new(),
388 }
389 }
390
391 pub fn fresh_var(&mut self) -> InferenceVar {
393 self.table.new_key(InferenceInfo::default())
394 }
395
396 pub fn fresh_type_param(&mut self, name: Atom, is_const: bool) -> InferenceVar {
398 let var = self.fresh_var();
399 self.type_params.push((name, var, is_const));
400 var
401 }
402
403 pub fn register_type_param(&mut self, name: Atom, var: InferenceVar, is_const: bool) {
408 self.type_params.push((name, var, is_const));
409 }
410
411 pub fn find_type_param(&self, name: Atom) -> Option<InferenceVar> {
413 self.type_params
414 .iter()
415 .find(|(n, _, _)| *n == name)
416 .map(|(_, v, _)| *v)
417 }
418
419 pub fn is_var_const(&mut self, var: InferenceVar) -> bool {
421 let root = self.table.find(var);
422 self.type_params
423 .iter()
424 .any(|(_, v, is_const)| self.table.find(*v) == root && *is_const)
425 }
426
427 pub fn probe(&mut self, var: InferenceVar) -> Option<TypeId> {
429 self.table.probe_value(var).resolved
430 }
431
432 pub fn unify_var_type(&mut self, var: InferenceVar, ty: TypeId) -> Result<(), InferenceError> {
434 let root = self.table.find(var);
436
437 if self.occurs_in(root, ty) {
438 return Err(InferenceError::OccursCheck { var: root, ty });
439 }
440
441 match self.table.probe_value(root).resolved {
443 None => {
444 self.table.union_value(
445 root,
446 InferenceInfo {
447 resolved: Some(ty),
448 ..InferenceInfo::default()
449 },
450 );
451 Ok(())
452 }
453 Some(existing) => {
454 if self.types_compatible(existing, ty) {
455 Ok(())
456 } else {
457 Err(InferenceError::Conflict(existing, ty))
458 }
459 }
460 }
461 }
462
463 pub fn unify_vars(&mut self, a: InferenceVar, b: InferenceVar) -> Result<(), InferenceError> {
465 let root_a = self.table.find(a);
466 let root_b = self.table.find(b);
467
468 if root_a == root_b {
469 return Ok(());
470 }
471
472 let value_a = self.table.probe_value(root_a).resolved;
473 let value_b = self.table.probe_value(root_b).resolved;
474 if let (Some(a_ty), Some(b_ty)) = (value_a, value_b)
475 && !self.types_compatible(a_ty, b_ty)
476 {
477 return Err(InferenceError::Conflict(a_ty, b_ty));
478 }
479
480 self.table
481 .unify_var_var(root_a, root_b)
482 .map_err(|_| InferenceError::Conflict(TypeId::ERROR, TypeId::ERROR))?;
483 Ok(())
484 }
485
486 fn types_compatible(&self, a: TypeId, b: TypeId) -> bool {
488 if a == b {
489 return true;
490 }
491
492 if a == TypeId::ANY || b == TypeId::ANY {
494 return true;
495 }
496
497 if a == TypeId::UNKNOWN || b == TypeId::UNKNOWN {
499 return true;
500 }
501
502 if a == TypeId::NEVER || b == TypeId::NEVER {
504 return true;
505 }
506
507 false
508 }
509
510 pub(crate) fn occurs_in(&mut self, var: InferenceVar, ty: TypeId) -> bool {
511 let root = self.table.find(var);
512 if self.type_params.is_empty() {
513 return false;
514 }
515
516 let mut visited = FxHashSet::default();
517 for &(atom, param_var, _) in &self.type_params {
518 if self.table.find(param_var) == root
519 && self.type_contains_param(ty, atom, &mut visited)
520 {
521 return true;
522 }
523 }
524 false
525 }
526
527 pub(crate) fn type_param_names_for_root(&mut self, root: InferenceVar) -> Vec<Atom> {
528 self.type_params
529 .iter()
530 .filter(|&(_name, var, _)| self.table.find(*var) == root)
531 .map(|(name, _var, _)| *name)
532 .collect()
533 }
534
535 pub(crate) fn upper_bound_cycles_param(&mut self, bound: TypeId, targets: &[Atom]) -> bool {
536 let mut params = FxHashSet::default();
537 let mut visited = FxHashSet::default();
538 self.collect_type_params(bound, &mut params, &mut visited);
539
540 for name in params {
541 let mut seen = FxHashSet::default();
542 if self.param_depends_on_targets(name, targets, &mut seen) {
543 return true;
544 }
545 }
546
547 false
548 }
549
550 pub(crate) fn expand_cyclic_upper_bound(
551 &mut self,
552 root: InferenceVar,
553 bound: TypeId,
554 target_names: &[Atom],
555 candidates: &mut Vec<InferenceCandidate>,
556 upper_bounds: &mut Vec<TypeId>,
557 ) {
558 let name = match self.interner.lookup(bound) {
559 Some(TypeData::TypeParameter(info) | TypeData::Infer(info)) => info.name,
560 _ => return,
561 };
562
563 let Some(var) = self.find_type_param(name) else {
564 return;
565 };
566
567 if let Some(resolved) = self.probe(var) {
568 if !upper_bounds.contains(&resolved) {
569 upper_bounds.push(resolved);
570 }
571 return;
572 }
573
574 let bound_root = self.table.find(var);
575 let info = self.table.probe_value(bound_root);
576
577 for candidate in info.candidates {
578 if self.occurs_in(root, candidate.type_id) {
579 continue;
580 }
581 candidates.push(InferenceCandidate {
582 type_id: candidate.type_id,
583 priority: InferencePriority::Circular,
584 is_fresh_literal: candidate.is_fresh_literal,
585 });
586 }
587
588 for ty in info.upper_bounds {
589 if self.occurs_in(root, ty) {
590 continue;
591 }
592 if !target_names.is_empty() && self.upper_bound_cycles_param(ty, target_names) {
593 continue;
594 }
595 if !upper_bounds.contains(&ty) {
596 upper_bounds.push(ty);
597 }
598 }
599 }
600
601 fn collect_type_params(
602 &self,
603 ty: TypeId,
604 params: &mut FxHashSet<Atom>,
605 visited: &mut FxHashSet<TypeId>,
606 ) {
607 if !visited.insert(ty) {
608 return;
609 }
610 let Some(key) = self.interner.lookup(ty) else {
611 return;
612 };
613
614 match key {
615 TypeData::TypeParameter(info) | TypeData::Infer(info) => {
616 params.insert(info.name);
617 }
618 TypeData::Array(elem) => {
619 self.collect_type_params(elem, params, visited);
620 }
621 TypeData::Tuple(elements) => {
622 let elements = self.interner.tuple_list(elements);
623 for element in elements.iter() {
624 self.collect_type_params(element.type_id, params, visited);
625 }
626 }
627 TypeData::Union(members) | TypeData::Intersection(members) => {
628 let members = self.interner.type_list(members);
629 for &member in members.iter() {
630 self.collect_type_params(member, params, visited);
631 }
632 }
633 TypeData::Object(shape_id) => {
634 let shape = self.interner.object_shape(shape_id);
635 for prop in &shape.properties {
636 self.collect_type_params(prop.type_id, params, visited);
637 }
638 }
639 TypeData::ObjectWithIndex(shape_id) => {
640 let shape = self.interner.object_shape(shape_id);
641 for prop in &shape.properties {
642 self.collect_type_params(prop.type_id, params, visited);
643 }
644 if let Some(index) = shape.string_index.as_ref() {
645 self.collect_type_params(index.key_type, params, visited);
646 self.collect_type_params(index.value_type, params, visited);
647 }
648 if let Some(index) = shape.number_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 }
653 TypeData::Application(app_id) => {
654 let app = self.interner.type_application(app_id);
655 self.collect_type_params(app.base, params, visited);
656 for &arg in &app.args {
657 self.collect_type_params(arg, params, visited);
658 }
659 }
660 TypeData::Function(shape_id) => {
661 let shape = self.interner.function_shape(shape_id);
662 for param in &shape.params {
663 self.collect_type_params(param.type_id, params, visited);
664 }
665 if let Some(this_type) = shape.this_type {
666 self.collect_type_params(this_type, params, visited);
667 }
668 self.collect_type_params(shape.return_type, params, visited);
669 }
670 TypeData::Callable(shape_id) => {
671 let shape = self.interner.callable_shape(shape_id);
672 for sig in &shape.call_signatures {
673 for param in &sig.params {
674 self.collect_type_params(param.type_id, params, visited);
675 }
676 if let Some(this_type) = sig.this_type {
677 self.collect_type_params(this_type, params, visited);
678 }
679 self.collect_type_params(sig.return_type, params, visited);
680 }
681 for sig in &shape.construct_signatures {
682 for param in &sig.params {
683 self.collect_type_params(param.type_id, params, visited);
684 }
685 if let Some(this_type) = sig.this_type {
686 self.collect_type_params(this_type, params, visited);
687 }
688 self.collect_type_params(sig.return_type, params, visited);
689 }
690 for prop in &shape.properties {
691 self.collect_type_params(prop.type_id, params, visited);
692 }
693 }
694 TypeData::Conditional(cond_id) => {
695 let cond = self.interner.conditional_type(cond_id);
696 self.collect_type_params(cond.check_type, params, visited);
697 self.collect_type_params(cond.extends_type, params, visited);
698 self.collect_type_params(cond.true_type, params, visited);
699 self.collect_type_params(cond.false_type, params, visited);
700 }
701 TypeData::Mapped(mapped_id) => {
702 let mapped = self.interner.mapped_type(mapped_id);
703 self.collect_type_params(mapped.constraint, params, visited);
704 if let Some(name_type) = mapped.name_type {
705 self.collect_type_params(name_type, params, visited);
706 }
707 self.collect_type_params(mapped.template, params, visited);
708 }
709 TypeData::IndexAccess(obj, idx) => {
710 self.collect_type_params(obj, params, visited);
711 self.collect_type_params(idx, params, visited);
712 }
713 TypeData::KeyOf(operand) | TypeData::ReadonlyType(operand) => {
714 self.collect_type_params(operand, params, visited);
715 }
716 TypeData::TemplateLiteral(spans) => {
717 let spans = self.interner.template_list(spans);
718 for span in spans.iter() {
719 if let TemplateSpan::Type(inner) = span {
720 self.collect_type_params(*inner, params, visited);
721 }
722 }
723 }
724 TypeData::StringIntrinsic { type_arg, .. } => {
725 self.collect_type_params(type_arg, params, visited);
726 }
727 TypeData::Enum(_def_id, member_type) => {
728 self.collect_type_params(member_type, params, visited);
730 }
731 TypeData::Intrinsic(_)
732 | TypeData::Literal(_)
733 | TypeData::Lazy(_)
734 | TypeData::Recursive(_)
735 | TypeData::BoundParameter(_)
736 | TypeData::TypeQuery(_)
737 | TypeData::UniqueSymbol(_)
738 | TypeData::ThisType
739 | TypeData::ModuleNamespace(_)
740 | TypeData::Error => {}
741 TypeData::NoInfer(inner) => {
742 self.collect_type_params(inner, params, visited);
743 }
744 }
745 }
746
747 fn param_depends_on_targets(
748 &mut self,
749 name: Atom,
750 targets: &[Atom],
751 visited: &mut FxHashSet<Atom>,
752 ) -> bool {
753 if targets.contains(&name) {
754 return true;
755 }
756 if !visited.insert(name) {
757 return false;
758 }
759 let Some(var) = self.find_type_param(name) else {
760 return false;
761 };
762 let root = self.table.find(var);
763 let upper_bounds = self.table.probe_value(root).upper_bounds;
764
765 for bound in upper_bounds {
766 for target in targets {
767 let mut seen = FxHashSet::default();
768 if self.type_contains_param(bound, *target, &mut seen) {
769 return true;
770 }
771 }
772 if let Some(TypeData::TypeParameter(info)) = self.interner.lookup(bound)
773 && self.param_depends_on_targets(info.name, targets, visited)
774 {
775 return true;
776 }
777 }
778
779 false
780 }
781
782 fn type_contains_param(
783 &self,
784 ty: TypeId,
785 target: Atom,
786 visited: &mut FxHashSet<TypeId>,
787 ) -> bool {
788 if !visited.insert(ty) {
789 return false;
790 }
791
792 let key = match self.interner.lookup(ty) {
793 Some(key) => key,
794 None => return false,
795 };
796
797 match key {
798 TypeData::TypeParameter(info) | TypeData::Infer(info) => info.name == target,
799 TypeData::Array(elem) => self.type_contains_param(elem, target, visited),
800 TypeData::Tuple(elements) => {
801 let elements = self.interner.tuple_list(elements);
802 elements
803 .iter()
804 .any(|e| self.type_contains_param(e.type_id, target, visited))
805 }
806 TypeData::Union(members) | TypeData::Intersection(members) => {
807 let members = self.interner.type_list(members);
808 members
809 .iter()
810 .any(|&member| self.type_contains_param(member, target, visited))
811 }
812 TypeData::Object(shape_id) => {
813 let shape = self.interner.object_shape(shape_id);
814 shape
815 .properties
816 .iter()
817 .any(|p| self.type_contains_param(p.type_id, target, visited))
818 }
819 TypeData::ObjectWithIndex(shape_id) => {
820 let shape = self.interner.object_shape(shape_id);
821 shape
822 .properties
823 .iter()
824 .any(|p| self.type_contains_param(p.type_id, target, visited))
825 || shape.string_index.as_ref().is_some_and(|idx| {
826 self.type_contains_param(idx.key_type, target, visited)
827 || self.type_contains_param(idx.value_type, target, visited)
828 })
829 || shape.number_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 }
834 TypeData::Application(app_id) => {
835 let app = self.interner.type_application(app_id);
836 self.type_contains_param(app.base, target, visited)
837 || app
838 .args
839 .iter()
840 .any(|&arg| self.type_contains_param(arg, target, visited))
841 }
842 TypeData::Function(shape_id) => {
843 let shape = self.interner.function_shape(shape_id);
844 if shape.type_params.iter().any(|tp| tp.name == target) {
845 return false;
846 }
847 shape
848 .this_type
849 .is_some_and(|this_type| self.type_contains_param(this_type, target, visited))
850 || shape
851 .params
852 .iter()
853 .any(|p| self.type_contains_param(p.type_id, target, visited))
854 || self.type_contains_param(shape.return_type, target, visited)
855 }
856 TypeData::Callable(shape_id) => {
857 let shape = self.interner.callable_shape(shape_id);
858 let in_call = shape.call_signatures.iter().any(|sig| {
859 if sig.type_params.iter().any(|tp| tp.name == target) {
860 false
861 } else {
862 sig.this_type.is_some_and(|this_type| {
863 self.type_contains_param(this_type, target, visited)
864 }) || sig
865 .params
866 .iter()
867 .any(|p| self.type_contains_param(p.type_id, target, visited))
868 || self.type_contains_param(sig.return_type, target, visited)
869 }
870 });
871 if in_call {
872 return true;
873 }
874 let in_construct = shape.construct_signatures.iter().any(|sig| {
875 if sig.type_params.iter().any(|tp| tp.name == target) {
876 false
877 } else {
878 sig.this_type.is_some_and(|this_type| {
879 self.type_contains_param(this_type, target, visited)
880 }) || sig
881 .params
882 .iter()
883 .any(|p| self.type_contains_param(p.type_id, target, visited))
884 || self.type_contains_param(sig.return_type, target, visited)
885 }
886 });
887 if in_construct {
888 return true;
889 }
890 shape
891 .properties
892 .iter()
893 .any(|p| self.type_contains_param(p.type_id, target, visited))
894 }
895 TypeData::Conditional(cond_id) => {
896 let cond = self.interner.conditional_type(cond_id);
897 self.type_contains_param(cond.check_type, target, visited)
898 || self.type_contains_param(cond.extends_type, target, visited)
899 || self.type_contains_param(cond.true_type, target, visited)
900 || self.type_contains_param(cond.false_type, target, visited)
901 }
902 TypeData::Mapped(mapped_id) => {
903 let mapped = self.interner.mapped_type(mapped_id);
904 if mapped.type_param.name == target {
905 return false;
906 }
907 self.type_contains_param(mapped.constraint, target, visited)
908 || self.type_contains_param(mapped.template, target, visited)
909 }
910 TypeData::IndexAccess(obj, idx) => {
911 self.type_contains_param(obj, target, visited)
912 || self.type_contains_param(idx, target, visited)
913 }
914 TypeData::KeyOf(operand) | TypeData::ReadonlyType(operand) => {
915 self.type_contains_param(operand, target, visited)
916 }
917 TypeData::TemplateLiteral(spans) => {
918 let spans = self.interner.template_list(spans);
919 spans.iter().any(|span| match span {
920 TemplateSpan::Text(_) => false,
921 TemplateSpan::Type(inner) => self.type_contains_param(*inner, target, visited),
922 })
923 }
924 TypeData::StringIntrinsic { type_arg, .. } => {
925 self.type_contains_param(type_arg, target, visited)
926 }
927 TypeData::Enum(_def_id, member_type) => {
928 self.type_contains_param(member_type, target, visited)
930 }
931 TypeData::Intrinsic(_)
932 | TypeData::Literal(_)
933 | TypeData::Lazy(_)
934 | TypeData::Recursive(_)
935 | TypeData::BoundParameter(_)
936 | TypeData::TypeQuery(_)
937 | TypeData::UniqueSymbol(_)
938 | TypeData::ThisType
939 | TypeData::ModuleNamespace(_)
940 | TypeData::Error => false,
941 TypeData::NoInfer(inner) => self.type_contains_param(inner, target, visited),
942 }
943 }
944
945 pub fn resolve_all(&mut self) -> Result<Vec<(Atom, TypeId)>, InferenceError> {
947 let type_params: Vec<_> = self.type_params.clone();
949 let mut results = Vec::new();
950 for (name, var, _) in type_params {
951 match self.probe(var) {
952 Some(ty) => results.push((name, ty)),
953 None => return Err(InferenceError::Unresolved(var)),
954 }
955 }
956 Ok(results)
957 }
958
959 pub fn interner(&self) -> &dyn TypeDatabase {
961 self.interner
962 }
963
964 pub fn add_lower_bound(&mut self, var: InferenceVar, ty: TypeId) {
972 self.add_candidate(var, ty, InferencePriority::NakedTypeVariable);
973 }
974
975 pub fn add_candidate(&mut self, var: InferenceVar, ty: TypeId, priority: InferencePriority) {
977 let root = self.table.find(var);
978 let candidate = InferenceCandidate {
979 type_id: ty,
980 priority,
981 is_fresh_literal: is_literal_type(self.interner, ty),
982 };
983 self.table.union_value(
984 root,
985 InferenceInfo {
986 candidates: vec![candidate],
987 ..InferenceInfo::default()
988 },
989 );
990 }
991
992 pub fn add_upper_bound(&mut self, var: InferenceVar, ty: TypeId) {
995 let root = self.table.find(var);
996 self.table.union_value(
997 root,
998 InferenceInfo {
999 upper_bounds: vec![ty],
1000 ..InferenceInfo::default()
1001 },
1002 );
1003 }
1004
1005 pub fn get_constraints(&mut self, var: InferenceVar) -> Option<ConstraintSet> {
1007 let root = self.table.find(var);
1008 let info = self.table.probe_value(root);
1009 if info.is_empty() {
1010 None
1011 } else {
1012 Some(ConstraintSet::from_info(&info))
1013 }
1014 }
1015
1016 pub fn all_candidates_are_return_type(&mut self, var: InferenceVar) -> bool {
1020 let root = self.table.find(var);
1021 let info = self.table.probe_value(root);
1022 !info.candidates.is_empty()
1023 && info
1024 .candidates
1025 .iter()
1026 .all(|c| c.priority == InferencePriority::ReturnType)
1027 }
1028
1029 pub fn get_literal_candidates(&mut self, var: InferenceVar) -> Vec<TypeId> {
1031 let root = self.table.find(var);
1032 let info = self.table.probe_value(root);
1033 info.candidates
1034 .iter()
1035 .filter(|c| c.is_fresh_literal)
1036 .map(|c| c.type_id)
1037 .collect()
1038 }
1039
1040 pub const fn collect_constraint(&mut self, _source: TypeId, _target: TypeId) {
1044 }
1048
1049 pub fn infer_from_types(
1084 &mut self,
1085 source: TypeId,
1086 target: TypeId,
1087 priority: InferencePriority,
1088 ) -> Result<(), InferenceError> {
1089 let source_key = self.interner.lookup(source);
1091 let target_key = self.interner.lookup(target);
1092
1093 if let Some(TypeData::NoInfer(_)) = target_key {
1096 return Ok(()); }
1098
1099 let source_key = if let Some(TypeData::NoInfer(inner)) = source_key {
1101 self.interner.lookup(inner)
1102 } else {
1103 source_key
1104 };
1105
1106 if let Some(TypeData::TypeParameter(ref param_info)) = target_key
1108 && let Some(var) = self.find_type_param(param_info.name)
1109 {
1110 self.add_candidate(var, source, priority);
1112 return Ok(());
1113 }
1114
1115 if let Some(TypeData::TypeParameter(ref param_info)) = source_key
1119 && let Some(var) = self.find_type_param(param_info.name)
1120 {
1121 self.add_upper_bound(var, target);
1123 return Ok(());
1124 }
1125
1126 match (source_key, target_key) {
1128 (Some(TypeData::Object(source_shape)), Some(TypeData::Object(target_shape))) => {
1130 self.infer_objects(source_shape, target_shape, priority)?;
1131 }
1132
1133 (Some(TypeData::Function(source_func)), Some(TypeData::Function(target_func))) => {
1135 self.infer_functions(source_func, target_func, priority)?;
1136 }
1137
1138 (Some(TypeData::Callable(source_call)), Some(TypeData::Callable(target_call))) => {
1140 self.infer_callables(source_call, target_call, priority)?;
1141 }
1142
1143 (Some(TypeData::Array(source_elem)), Some(TypeData::Array(target_elem))) => {
1145 self.infer_from_types(source_elem, target_elem, priority)?;
1146 }
1147
1148 (Some(TypeData::Tuple(source_elems)), Some(TypeData::Tuple(target_elems))) => {
1150 self.infer_tuples(source_elems, target_elems, priority)?;
1151 }
1152
1153 (Some(TypeData::Union(source_members)), Some(TypeData::Union(target_members))) => {
1155 self.infer_unions(source_members, target_members, priority)?;
1156 }
1157
1158 (
1160 Some(TypeData::Intersection(source_members)),
1161 Some(TypeData::Intersection(target_members)),
1162 ) => {
1163 self.infer_intersections(source_members, target_members, priority)?;
1164 }
1165
1166 (Some(TypeData::Application(source_app)), Some(TypeData::Application(target_app))) => {
1168 self.infer_applications(source_app, target_app, priority)?;
1169 }
1170
1171 (
1173 Some(TypeData::IndexAccess(source_obj, source_idx)),
1174 Some(TypeData::IndexAccess(target_obj, target_idx)),
1175 ) => {
1176 self.infer_from_types(source_obj, target_obj, priority)?;
1177 self.infer_from_types(source_idx, target_idx, priority)?;
1178 }
1179
1180 (
1182 Some(TypeData::ReadonlyType(source_inner)),
1183 Some(TypeData::ReadonlyType(target_inner)),
1184 ) => {
1185 self.infer_from_types(source_inner, target_inner, priority)?;
1186 }
1187
1188 (_, Some(TypeData::ReadonlyType(target_inner))) => {
1190 self.infer_from_types(source, target_inner, priority)?;
1191 }
1192
1193 (Some(source_key), Some(TypeData::TemplateLiteral(target_id))) => {
1196 self.infer_from_template_literal(source, Some(&source_key), target_id, priority)?;
1197 }
1198
1199 (Some(TypeData::Object(source_shape)), Some(TypeData::Mapped(mapped_id))) => {
1203 self.infer_from_mapped_type(source_shape, mapped_id, priority)?;
1204 }
1205
1206 _ => {
1209 }
1212 }
1213
1214 Ok(())
1215 }
1216
1217 fn infer_objects(
1219 &mut self,
1220 source_shape: ObjectShapeId,
1221 target_shape: ObjectShapeId,
1222 priority: InferencePriority,
1223 ) -> Result<(), InferenceError> {
1224 let source_shape = self.interner.object_shape(source_shape);
1225 let target_shape = self.interner.object_shape(target_shape);
1226
1227 for target_prop in &target_shape.properties {
1229 if let Some(source_prop) = source_shape
1230 .properties
1231 .iter()
1232 .find(|p| p.name == target_prop.name)
1233 {
1234 self.infer_from_types(source_prop.type_id, target_prop.type_id, priority)?;
1235 }
1236 }
1237
1238 if let (Some(target_string_idx), Some(source_string_idx)) =
1241 (&target_shape.string_index, &source_shape.string_index)
1242 {
1243 self.infer_from_types(
1244 source_string_idx.value_type,
1245 target_string_idx.value_type,
1246 priority,
1247 )?;
1248 }
1249
1250 if let (Some(target_number_idx), Some(source_number_idx)) =
1252 (&target_shape.number_index, &source_shape.number_index)
1253 {
1254 self.infer_from_types(
1255 source_number_idx.value_type,
1256 target_number_idx.value_type,
1257 priority,
1258 )?;
1259 }
1260
1261 Ok(())
1262 }
1263
1264 fn infer_from_mapped_type(
1270 &mut self,
1271 source_shape: ObjectShapeId,
1272 mapped_id: MappedTypeId,
1273 priority: InferencePriority,
1274 ) -> Result<(), InferenceError> {
1275 let mapped = self.interner.mapped_type(mapped_id);
1276 let source = self.interner.object_shape(source_shape);
1277
1278 if source.properties.is_empty() {
1279 return Ok(());
1280 }
1281
1282 let name_literals: Vec<TypeId> = source
1285 .properties
1286 .iter()
1287 .map(|p| self.interner.literal_string_atom(p.name))
1288 .collect();
1289 let names_union = if name_literals.len() == 1 {
1290 name_literals[0]
1291 } else {
1292 self.interner.union(name_literals)
1293 };
1294 self.infer_from_types(names_union, mapped.constraint, priority)?;
1295
1296 for prop in &source.properties {
1298 self.infer_from_types(prop.type_id, mapped.template, priority)?;
1299 }
1300
1301 Ok(())
1302 }
1303
1304 fn infer_functions(
1306 &mut self,
1307 source_func: FunctionShapeId,
1308 target_func: FunctionShapeId,
1309 priority: InferencePriority,
1310 ) -> Result<(), InferenceError> {
1311 let source_sig = self.interner.function_shape(source_func);
1312 let target_sig = self.interner.function_shape(target_func);
1313
1314 tracing::trace!(
1315 source_params = source_sig.params.len(),
1316 target_params = target_sig.params.len(),
1317 "infer_functions called"
1318 );
1319
1320 let mut source_params = source_sig.params.iter().peekable();
1322 let mut target_params = target_sig.params.iter().peekable();
1323
1324 loop {
1325 let source_rest = source_params.peek().is_some_and(|p| p.rest);
1326 let target_rest = target_params.peek().is_some_and(|p| p.rest);
1327
1328 tracing::trace!(
1329 source_rest,
1330 target_rest,
1331 "Checking rest params in loop iteration"
1332 );
1333
1334 if source_rest && target_rest {
1336 let source_param = source_params.next().unwrap();
1337 let target_param = target_params.next().unwrap();
1338 self.infer_from_types(target_param.type_id, source_param.type_id, priority)?;
1339 break;
1340 }
1341
1342 if source_rest {
1344 let source_param = source_params.next().unwrap();
1345 for target_param in target_params.by_ref() {
1346 self.infer_from_types(target_param.type_id, source_param.type_id, priority)?;
1347 }
1348 break;
1349 }
1350
1351 if target_rest {
1353 let target_param = target_params.next().unwrap();
1354
1355 let target_is_type_param = matches!(
1363 self.interner.lookup(target_param.type_id),
1364 Some(TypeData::TypeParameter(_) | TypeData::Infer(_))
1365 );
1366
1367 tracing::trace!(
1368 target_is_type_param,
1369 target_param_type = ?target_param.type_id,
1370 "Rest parameter inference - target is type param check"
1371 );
1372
1373 if target_is_type_param {
1374 let mut tuple_elements = Vec::new();
1376 for source_param in source_params.by_ref() {
1377 tuple_elements.push(TupleElement {
1378 type_id: source_param.type_id,
1379 name: source_param.name,
1380 optional: source_param.optional,
1381 rest: source_param.rest,
1382 });
1383 }
1384
1385 tracing::trace!(
1386 num_elements = tuple_elements.len(),
1387 "Collected source params into tuple"
1388 );
1389
1390 if !tuple_elements.is_empty() {
1393 let tuple_type = self.interner.tuple(tuple_elements);
1394 tracing::trace!(
1395 tuple_type = ?tuple_type,
1396 target_param = ?target_param.type_id,
1397 "Inferring tuple against type parameter"
1398 );
1399 self.infer_from_types(target_param.type_id, tuple_type, priority)?;
1400 }
1401 } else {
1402 for source_param in source_params.by_ref() {
1405 self.infer_from_types(
1406 target_param.type_id,
1407 source_param.type_id,
1408 priority,
1409 )?;
1410 }
1411 }
1412 break;
1413 }
1414
1415 match (source_params.next(), target_params.next()) {
1417 (Some(source_param), Some(target_param)) => {
1418 self.infer_from_types(target_param.type_id, source_param.type_id, priority)?;
1420 }
1421 _ => break, }
1423 }
1424
1425 self.infer_from_types(source_sig.return_type, target_sig.return_type, priority)?;
1427
1428 if let (Some(source_this), Some(target_this)) = (source_sig.this_type, target_sig.this_type)
1430 {
1431 self.infer_from_types(target_this, source_this, priority)?;
1432 }
1433
1434 if let (Some(source_pred), Some(target_pred)) =
1436 (&source_sig.type_predicate, &target_sig.type_predicate)
1437 {
1438 let targets_match = match (source_pred.parameter_index, target_pred.parameter_index) {
1440 (Some(s_idx), Some(t_idx)) => s_idx == t_idx,
1441 _ => source_pred.target == target_pred.target,
1442 };
1443
1444 tracing::trace!(
1445 targets_match,
1446 ?source_pred.parameter_index,
1447 ?target_pred.parameter_index,
1448 "Inferring from type predicates"
1449 );
1450
1451 if targets_match
1452 && source_pred.asserts == target_pred.asserts
1453 && let (Some(source_ty), Some(target_ty)) =
1454 (source_pred.type_id, target_pred.type_id)
1455 {
1456 self.infer_from_types(source_ty, target_ty, priority)?;
1457 }
1458 }
1459
1460 Ok(())
1461 }
1462
1463 fn infer_tuples(
1465 &mut self,
1466 source_elems: TupleListId,
1467 target_elems: TupleListId,
1468 priority: InferencePriority,
1469 ) -> Result<(), InferenceError> {
1470 let source_list = self.interner.tuple_list(source_elems);
1471 let target_list = self.interner.tuple_list(target_elems);
1472
1473 for (source_elem, target_elem) in source_list.iter().zip(target_list.iter()) {
1474 self.infer_from_types(source_elem.type_id, target_elem.type_id, priority)?;
1475 }
1476
1477 Ok(())
1478 }
1479
1480 fn infer_callables(
1482 &mut self,
1483 source_id: CallableShapeId,
1484 target_id: CallableShapeId,
1485 priority: InferencePriority,
1486 ) -> Result<(), InferenceError> {
1487 let source = self.interner.callable_shape(source_id);
1488 let target = self.interner.callable_shape(target_id);
1489
1490 for target_sig in &target.call_signatures {
1492 for source_sig in &source.call_signatures {
1493 if source_sig.params.len() == target_sig.params.len() {
1494 for (s_param, t_param) in source_sig.params.iter().zip(target_sig.params.iter())
1495 {
1496 self.infer_from_types(t_param.type_id, s_param.type_id, priority)?;
1497 }
1498 self.infer_from_types(
1499 source_sig.return_type,
1500 target_sig.return_type,
1501 priority,
1502 )?;
1503 break;
1504 }
1505 }
1506 }
1507
1508 for target_sig in &target.construct_signatures {
1510 for source_sig in &source.construct_signatures {
1511 if source_sig.params.len() == target_sig.params.len() {
1512 for (s_param, t_param) in source_sig.params.iter().zip(target_sig.params.iter())
1513 {
1514 self.infer_from_types(t_param.type_id, s_param.type_id, priority)?;
1515 }
1516 self.infer_from_types(
1517 source_sig.return_type,
1518 target_sig.return_type,
1519 priority,
1520 )?;
1521 break;
1522 }
1523 }
1524 }
1525
1526 for target_prop in &target.properties {
1528 if let Some(source_prop) = source
1529 .properties
1530 .iter()
1531 .find(|p| p.name == target_prop.name)
1532 {
1533 self.infer_from_types(source_prop.type_id, target_prop.type_id, priority)?;
1534 }
1535 }
1536
1537 if let (Some(target_idx), Some(source_idx)) = (&target.string_index, &source.string_index) {
1539 self.infer_from_types(source_idx.value_type, target_idx.value_type, priority)?;
1540 }
1541
1542 if let (Some(target_idx), Some(source_idx)) = (&target.number_index, &source.number_index) {
1544 self.infer_from_types(source_idx.value_type, target_idx.value_type, priority)?;
1545 }
1546
1547 Ok(())
1548 }
1549
1550 fn infer_unions(
1552 &mut self,
1553 source_members: TypeListId,
1554 target_members: TypeListId,
1555 priority: InferencePriority,
1556 ) -> Result<(), InferenceError> {
1557 let source_list = self.interner.type_list(source_members);
1558 let target_list = self.interner.type_list(target_members);
1559
1560 let (parameterized, fixed): (Vec<TypeId>, Vec<TypeId>) = target_list
1566 .iter()
1567 .partition(|&&t| self.target_contains_inference_param(t));
1568
1569 if !parameterized.is_empty() && !fixed.is_empty() {
1570 for &source_ty in source_list.iter() {
1572 let matches_fixed = fixed.contains(&source_ty);
1573 if !matches_fixed {
1574 for &target_ty in ¶meterized {
1575 self.infer_from_types(source_ty, target_ty, priority)?;
1576 }
1577 }
1578 }
1579 } else {
1580 for source_ty in source_list.iter() {
1582 for target_ty in target_list.iter() {
1583 self.infer_from_types(*source_ty, *target_ty, priority)?;
1584 }
1585 }
1586 }
1587
1588 Ok(())
1589 }
1590
1591 fn target_contains_inference_param(&self, target: TypeId) -> bool {
1593 let Some(key) = self.interner.lookup(target) else {
1594 return false;
1595 };
1596 match key {
1597 TypeData::TypeParameter(ref info) => self.find_type_param(info.name).is_some(),
1598 _ => false,
1599 }
1600 }
1601
1602 fn infer_intersections(
1604 &mut self,
1605 source_members: TypeListId,
1606 target_members: TypeListId,
1607 priority: InferencePriority,
1608 ) -> Result<(), InferenceError> {
1609 let source_list = self.interner.type_list(source_members);
1610 let target_list = self.interner.type_list(target_members);
1611
1612 for source_ty in source_list.iter() {
1614 for target_ty in target_list.iter() {
1615 let _ = self.infer_from_types(*source_ty, *target_ty, priority);
1617 }
1618 }
1619
1620 Ok(())
1621 }
1622
1623 fn infer_applications(
1625 &mut self,
1626 source_app: TypeApplicationId,
1627 target_app: TypeApplicationId,
1628 priority: InferencePriority,
1629 ) -> Result<(), InferenceError> {
1630 let source_info = self.interner.type_application(source_app);
1631 let target_info = self.interner.type_application(target_app);
1632
1633 if source_info.base != target_info.base {
1635 return Ok(());
1636 }
1637
1638 for (source_arg, target_arg) in source_info.args.iter().zip(target_info.args.iter()) {
1640 self.infer_from_types(*source_arg, *target_arg, priority)?;
1641 }
1642
1643 Ok(())
1644 }
1645
1646 fn infer_from_template_literal(
1677 &mut self,
1678 source: TypeId,
1679 source_key: Option<&TypeData>,
1680 target_template: TemplateLiteralId,
1681 priority: InferencePriority,
1682 ) -> Result<(), InferenceError> {
1683 let spans = self.interner.template_list(target_template);
1684
1685 if source == TypeId::ANY
1687 || matches!(source_key, Some(TypeData::Intrinsic(IntrinsicKind::String)))
1688 {
1689 for span in spans.iter() {
1690 if let TemplateSpan::Type(type_id) = span
1691 && let Some(TypeData::Infer(param_info)) = self.interner.lookup(*type_id)
1692 && let Some(var) = self.find_type_param(param_info.name)
1693 {
1694 self.add_candidate(var, source, priority);
1696 }
1697 }
1698 return Ok(());
1699 }
1700
1701 if let Some(TypeData::Union(source_members)) = source_key {
1703 let members = self.interner.type_list(*source_members);
1704 for &member in members.iter() {
1705 let member_key = self.interner.lookup(member);
1706 self.infer_from_template_literal(
1707 member,
1708 member_key.as_ref(),
1709 target_template,
1710 priority,
1711 )?;
1712 }
1713 return Ok(());
1714 }
1715
1716 if let Some(source_str) = self.extract_string_literal(source)
1718 && let Some(captures) = self.match_template_pattern(&source_str, &spans)
1719 {
1720 for (infer_var, captured_string) in captures {
1722 let literal_type = self.interner.literal_string(&captured_string);
1723 self.add_candidate(infer_var, literal_type, priority);
1724 }
1725 }
1726
1727 Ok(())
1728 }
1729
1730 fn extract_string_literal(&self, type_id: TypeId) -> Option<String> {
1734 match self.interner.lookup(type_id) {
1735 Some(TypeData::Literal(LiteralValue::String(s))) => Some(self.interner.resolve_atom(s)),
1736 _ => None,
1737 }
1738 }
1739
1740 fn match_template_pattern(
1752 &self,
1753 source: &str,
1754 spans: &[TemplateSpan],
1755 ) -> Option<Vec<(InferenceVar, String)>> {
1756 let mut bindings = Vec::new();
1757 let mut pos = 0;
1758
1759 for (i, span) in spans.iter().enumerate() {
1760 let is_last = i == spans.len() - 1;
1761
1762 match span {
1763 TemplateSpan::Text(text_atom) => {
1764 let text = self.interner.resolve_atom(*text_atom).to_string();
1766 if !source.get(pos..)?.starts_with(&text) {
1767 return None; }
1769 pos += text.len();
1770 }
1771
1772 TemplateSpan::Type(type_id) => {
1773 if let Some(TypeData::Infer(param_info)) = self.interner.lookup(*type_id)
1775 && let Some(var) = self.find_type_param(param_info.name)
1776 {
1777 if is_last {
1778 let captured = source[pos..].to_string();
1780 bindings.push((var, captured));
1781 pos = source.len();
1782 } else {
1783 if let Some(anchor_text) = self.find_next_text_anchor(spans, i) {
1786 let anchor = self.interner.resolve_atom(anchor_text).to_string();
1787 let capture_end = source[pos..].find(&anchor)? + pos;
1789 let captured = source[pos..capture_end].to_string();
1790 bindings.push((var, captured));
1791 pos = capture_end;
1792 } else {
1793 bindings.push((var, String::new()));
1796 }
1798 }
1799 }
1800 }
1801 }
1802 }
1803
1804 (pos == source.len()).then_some(bindings)
1806 }
1807
1808 fn find_next_text_anchor(&self, spans: &[TemplateSpan], start_idx: usize) -> Option<Atom> {
1810 spans.iter().skip(start_idx + 1).find_map(|span| {
1811 if let TemplateSpan::Text(text) = span {
1812 Some(*text)
1813 } else {
1814 None
1815 }
1816 })
1817 }
1818}
1819
1820#[cfg(test)]
1823#[path = "../tests/infer_tests.rs"]
1824mod tests;