1use std::{
4 collections::hash_map::Entry,
5 sync::{Arc, OnceLock},
6};
7
8use rustc_hash::{FxHashMap, FxHashSet};
9use tinymist_derive::BindTyCtx;
10
11use super::{
12 BuiltinTy, DynTypeBounds, FlowVarKind, SharedContext, TyCtxMut, TypeInfo, TypeVar,
13 TypeVarBounds, prelude::*,
14};
15use crate::{
16 docs::UntypedDefDocs,
17 syntax::{Decl, DeclExpr, Expr, ExprInfo, UnaryOp},
18 ty::*,
19};
20
21mod apply;
22mod docs;
23mod select;
24mod syntax;
25
26pub(crate) use apply::*;
27pub(crate) use select::*;
28
29#[derive(Default)]
30pub struct TypeEnv {
31 visiting: FxHashMap<TypstFileId, Arc<TypeInfo>>,
32 exprs: FxHashMap<TypstFileId, Option<ExprInfo>>,
33}
34
35#[typst_macros::time(span = ei.source.root().span())]
37pub(crate) fn type_check(
38 ctx: Arc<SharedContext>,
39 ei: ExprInfo,
40 env: &mut TypeEnv,
41) -> Arc<TypeInfo> {
42 let mut info = TypeInfo::default();
43 info.valid = true;
44 info.fid = Some(ei.fid);
45 info.revision = ei.revision;
46
47 env.visiting.insert(ei.fid, Arc::new(TypeInfo::default()));
48
49 let root = ei.root.clone();
51
52 let mut checker = TypeChecker {
53 ctx,
54 ei,
55 info,
56 env,
57 call_cache: Default::default(),
58 module_exports: Default::default(),
59 overwritten_vars: Default::default(),
60 live_input_vars: Default::default(),
61 input_contract_bounds: Default::default(),
62 };
63
64 let type_check_start = tinymist_std::time::Instant::now();
65
66 checker.check(&root);
67
68 let exports = checker
69 .ei
70 .exports
71 .clone()
72 .into_iter()
73 .map(|(k, v)| (k.clone(), checker.check(v)))
74 .collect();
75 checker.info.exports = exports;
76
77 let elapsed = type_check_start.elapsed();
78 crate::log_debug_ct!("Type checking on {:?} took {elapsed:?}", checker.ei.fid);
79
80 checker.env.visiting.remove(&checker.ei.fid);
81
82 Arc::new(checker.info)
83}
84
85type CallCacheDesc = (
86 Interned<SigTy>,
87 Interned<SigTy>,
88 Option<Vec<Interned<SigTy>>>,
89);
90
91pub(crate) struct TypeChecker<'a> {
92 ctx: Arc<SharedContext>,
93 ei: ExprInfo,
94
95 info: TypeInfo,
96 module_exports: FxHashMap<(TypstFileId, Interned<str>), OnceLock<Option<Ty>>>,
97
98 call_cache: FxHashSet<CallCacheDesc>,
99 overwritten_vars: FxHashSet<DeclExpr>,
100 live_input_vars: FxHashSet<DeclExpr>,
102 input_contract_bounds: FxHashMap<DeclExpr, DynTypeBounds>,
103
104 env: &'a mut TypeEnv,
105}
106
107impl TyCtx for TypeChecker<'_> {
108 fn global_bounds(&self, var: &Interned<TypeVar>, pol: bool) -> Option<DynTypeBounds> {
109 self.info.global_bounds(var, pol)
110 }
111
112 fn local_bind_of(&self, var: &Interned<TypeVar>) -> Option<Ty> {
113 self.info.local_bind_of(var)
114 }
115}
116
117impl TyCtxMut for TypeChecker<'_> {
118 type Snap = <TypeInfo as TyCtxMut>::Snap;
119
120 fn start_scope(&mut self) -> Self::Snap {
121 self.info.start_scope()
122 }
123
124 fn end_scope(&mut self, snap: Self::Snap) {
125 self.info.end_scope(snap)
126 }
127
128 fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty) {
129 self.info.bind_local(var, ty);
130 }
131
132 fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>> {
133 Some(self.ctx.type_of_func(func.clone()).type_sig())
134 }
135
136 fn type_of_value(&mut self, val: &Value) -> Ty {
137 self.ctx.type_of_value(val)
138 }
139
140 fn check_module_item(&mut self, fid: TypstFileId, name: &StrRef) -> Option<Ty> {
141 self.module_exports
142 .entry((fid, name.clone()))
143 .or_default()
144 .clone()
145 .get_or_init(|| {
146 let ei = self
147 .env
148 .exprs
149 .entry(fid)
150 .or_insert_with(|| self.ctx.expr_stage_by_id(fid))
151 .clone()?;
152
153 Some(self.check(ei.exports.get(name)?))
154 })
155 .clone()
156 }
157}
158
159impl TypeChecker<'_> {
160 fn check(&mut self, expr: &Expr) -> Ty {
161 self.check_syntax(expr).unwrap_or(Ty::undef())
162 }
163
164 fn copy_doc_vars(
165 &mut self,
166 fr: &TypeVarBounds,
167 var: &Interned<TypeVar>,
168 base: &Interned<Decl>,
169 ) -> Ty {
170 let mut gen_var = var.as_ref().clone();
171 let encoded = Interned::new(Decl::docs(base.clone(), var.clone()));
172 gen_var.def = encoded.clone();
173 crate::log_debug_ct!("copy var {fr:?} as {encoded:?}");
174 let bounds = TypeVarBounds::new(gen_var, fr.bounds.bounds().read().clone());
175 let var = bounds.as_type();
176 self.info.vars.insert(encoded, bounds);
177 var
178 }
179
180 fn get_var(&mut self, decl: &DeclExpr) -> Interned<TypeVar> {
181 crate::log_debug_ct!("get_var {decl:?}");
182 let mut imported_docs = None;
183 let mut imported_input_bounds = vec![];
184 let var = match self.info.vars.entry(decl.clone()) {
185 Entry::Occupied(entry) => entry.get().var.clone(),
186 Entry::Vacant(entry) => {
187 let name = decl.name().clone();
188 let init = Self::external_var_bounds(&self.ctx, self.env, self.ei.fid, decl, &name)
189 .map(|(bounds, docs, input_bounds)| {
190 imported_docs = docs;
191 imported_input_bounds = input_bounds;
192 bounds
193 })
194 .unwrap_or_default();
195 let bounds = TypeVarBounds::new(
196 TypeVar {
197 name,
198 def: decl.clone(),
199 },
200 init,
201 );
202 let var = bounds.var.clone();
203 entry.insert(bounds);
204 var
205 }
206 };
207
208 if let Some(docs) = imported_docs {
209 self.info.var_docs.entry(decl.clone()).or_insert(docs);
210 }
211 for bounds in imported_input_bounds {
212 self.info
213 .vars
214 .entry(bounds.var.def.clone())
215 .or_insert(bounds);
216 }
217
218 let s = decl.span();
219 if !s.is_detached() {
220 TypeInfo::witness_(s, Ty::Var(var.clone()), &mut self.info.mapping);
228 }
229 var
230 }
231
232 fn external_var_bounds(
233 ctx: &Arc<SharedContext>,
234 env: &mut TypeEnv,
235 current_fid: TypstFileId,
236 decl: &DeclExpr,
237 name: &Interned<str>,
238 ) -> Option<(
239 DynTypeBounds,
240 Option<Arc<UntypedDefDocs>>,
241 Vec<TypeVarBounds>,
242 )> {
243 let fid = decl.file_id()?;
244 if fid == current_fid {
245 return None;
246 }
247
248 crate::log_debug_ct!("import_ty {name} from {fid:?}");
249
250 let ext_def_use_info = ctx.expr_stage_by_id(fid)?;
251 let source = &ext_def_use_info.source;
252 let ext_type_info = if let Some(scheme) = env.visiting.get(&source.id()) {
254 scheme.clone()
255 } else {
256 ctx.clone().type_check_(source, env)
257 };
258 let ext_def = ext_def_use_info.exports.get(name)?;
259
260 let Expr::Decl(decl) = ext_def else {
262 return None;
263 };
264
265 let ext_ty = ext_type_info.vars.get(decl)?.as_type();
266 let docs = ext_type_info.var_docs.get(decl).cloned();
267
268 let def = ext_type_info.simplify(ext_ty, true);
269 let input_bounds = Self::copy_external_input_bounds(&ext_type_info, &def);
270 let mut bounds = DynTypeBounds::default();
271 bounds.lbs.insert_mut(def);
272 Some((bounds, docs, input_bounds))
273 }
274
275 fn copy_external_input_bounds(type_info: &TypeInfo, ty: &Ty) -> Vec<TypeVarBounds> {
276 let mut pending = vec![];
277 let mut seen = FxHashSet::default();
278 Self::collect_signature_input_binders(ty, &FxHashSet::default(), &mut seen, &mut pending);
279
280 let mut copied = vec![];
281 let mut idx = 0;
282 while idx < pending.len() {
283 let (binder, escaped) = pending[idx].clone();
284 idx += 1;
285
286 let Some(source) = type_info.vars.get(&binder.def) else {
287 continue;
288 };
289 let source_is_weak = matches!(&source.bounds, FlowVarKind::Weak(_));
290 let bounds = source.bounds.bounds().read().freeze();
291
292 for bound in bounds.lbs.iter().chain(&bounds.ubs) {
293 Self::collect_signature_input_binders(bound, &escaped, &mut seen, &mut pending);
294 }
295
296 let mut closer = FunctionResultantCloser {
297 vars: &type_info.vars,
298 params: escaped,
299 visiting: FxHashSet::default(),
300 visited: 0,
301 };
302 let bounds = closer.close_bounds(&bounds);
303 let mut imported =
304 TypeVarBounds::new(binder.as_ref().clone(), DynTypeBounds::from(bounds));
305 if source_is_weak {
306 imported.weaken();
307 }
308 copied.push(imported);
309 }
310
311 copied
312 }
313
314 fn collect_signature_input_binders(
315 ty: &Ty,
316 escaped: &FxHashSet<DeclExpr>,
317 seen: &mut FxHashSet<DeclExpr>,
318 binders: &mut Vec<(Interned<TypeVar>, FxHashSet<DeclExpr>)>,
319 ) {
320 match ty {
321 Ty::Func(sig) => {
322 let mut direct = vec![];
323 let mut direct_seen = FxHashSet::default();
324 for input in sig.inputs() {
325 Self::collect_input_binders(input, &mut direct_seen, &mut direct);
326 }
327
328 let mut scope = escaped.clone();
329 for binder in direct {
330 if seen.insert(binder.def.clone()) {
331 binders.push((binder.clone(), scope.clone()));
332 }
333 scope.insert(binder.def.clone());
334 }
335
336 for input in sig.inputs() {
337 Self::collect_signature_input_binders(input, &scope, seen, binders);
338 }
339 if let Some(body) = &sig.body {
340 Self::collect_signature_input_binders(body, &scope, seen, binders);
341 }
342 }
343 Ty::With(with) => {
344 Self::collect_signature_input_binders(&with.sig, escaped, seen, binders);
345 for input in with.with.inputs() {
346 Self::collect_signature_input_binders(input, escaped, seen, binders);
347 }
348 if let Some(body) = &with.with.body {
349 Self::collect_signature_input_binders(body, escaped, seen, binders);
350 }
351 }
352 Ty::Args(sig) | Ty::Pattern(sig) => {
353 for input in sig.inputs() {
354 Self::collect_signature_input_binders(input, escaped, seen, binders);
355 }
356 if let Some(body) = &sig.body {
357 Self::collect_signature_input_binders(body, escaped, seen, binders);
358 }
359 }
360 Ty::Param(param) => {
361 Self::collect_signature_input_binders(¶m.ty, escaped, seen, binders)
362 }
363 Ty::Union(types) | Ty::Tuple(types) => {
364 for ty in types.iter() {
365 Self::collect_signature_input_binders(ty, escaped, seen, binders);
366 }
367 }
368 Ty::Let(bounds) => {
369 for ty in bounds.lbs.iter().chain(&bounds.ubs) {
370 Self::collect_signature_input_binders(ty, escaped, seen, binders);
371 }
372 }
373 Ty::Dict(record) => {
374 for ty in record.types.iter() {
375 Self::collect_signature_input_binders(ty, escaped, seen, binders);
376 }
377 }
378 Ty::Array(elem) => Self::collect_signature_input_binders(elem, escaped, seen, binders),
379 Ty::Select(select) => {
380 Self::collect_signature_input_binders(&select.ty, escaped, seen, binders)
381 }
382 Ty::Unary(unary) => {
383 Self::collect_signature_input_binders(&unary.lhs, escaped, seen, binders)
384 }
385 Ty::Binary(binary) => {
386 let [lhs, rhs] = binary.operands();
387 Self::collect_signature_input_binders(lhs, escaped, seen, binders);
388 Self::collect_signature_input_binders(rhs, escaped, seen, binders);
389 }
390 Ty::If(if_ty) => {
391 Self::collect_signature_input_binders(&if_ty.cond, escaped, seen, binders);
392 Self::collect_signature_input_binders(&if_ty.then, escaped, seen, binders);
393 Self::collect_signature_input_binders(&if_ty.else_, escaped, seen, binders);
394 }
395 Ty::Var(_) | Ty::Any | Ty::Boolean(_) | Ty::Builtin(_) | Ty::Value(_) => {}
396 }
397 }
398
399 fn collect_input_binders(
400 ty: &Ty,
401 seen: &mut FxHashSet<DeclExpr>,
402 binders: &mut Vec<Interned<TypeVar>>,
403 ) {
404 match ty {
405 Ty::Var(var) => {
406 if seen.insert(var.def.clone()) {
407 binders.push(var.clone());
408 }
409 }
410 Ty::Func(_) | Ty::With(_) => {}
411 Ty::Param(param) => Self::collect_input_binders(¶m.ty, seen, binders),
412 Ty::Union(types) | Ty::Tuple(types) => {
413 for ty in types.iter() {
414 Self::collect_input_binders(ty, seen, binders);
415 }
416 }
417 Ty::Let(bounds) => {
418 for ty in bounds.lbs.iter().chain(&bounds.ubs) {
419 Self::collect_input_binders(ty, seen, binders);
420 }
421 }
422 Ty::Dict(record) => {
423 for ty in record.types.iter() {
424 Self::collect_input_binders(ty, seen, binders);
425 }
426 }
427 Ty::Array(elem) => Self::collect_input_binders(elem, seen, binders),
428 Ty::Args(sig) | Ty::Pattern(sig) => {
429 for input in sig.inputs() {
430 Self::collect_input_binders(input, seen, binders);
431 }
432 }
433 Ty::Select(select) => Self::collect_input_binders(&select.ty, seen, binders),
434 Ty::Unary(unary) => Self::collect_input_binders(&unary.lhs, seen, binders),
435 Ty::Binary(binary) => {
436 let [lhs, rhs] = binary.operands();
437 Self::collect_input_binders(lhs, seen, binders);
438 Self::collect_input_binders(rhs, seen, binders);
439 }
440 Ty::If(if_ty) => {
441 Self::collect_input_binders(&if_ty.cond, seen, binders);
442 Self::collect_input_binders(&if_ty.then, seen, binders);
443 Self::collect_input_binders(&if_ty.else_, seen, binders);
444 }
445 Ty::Any | Ty::Boolean(_) | Ty::Builtin(_) | Ty::Value(_) => {}
446 }
447 }
448
449 fn constrain_sig_inputs(
450 &mut self,
451 sig: &Interned<SigTy>,
452 args: &Interned<SigTy>,
453 with: Option<&Vec<Interned<SigTy>>>,
454 ) {
455 let call_desc = (sig.clone(), args.clone(), with.cloned());
456 if !self.call_cache.insert(call_desc) {
457 return;
458 }
459
460 let rest_bind = Self::rest_arg_bind(sig, args, with);
461
462 for (arg_recv, arg_ins) in sig.matches(args, with) {
463 if rest_bind.as_ref().is_some_and(
464 |(rest_var, _)| matches!(arg_recv, Ty::Var(var) if var.def == rest_var.def),
465 ) {
466 continue;
467 }
468 if matches!(arg_recv, Ty::Var(var) if !self.info.vars.contains_key(&var.def)) {
469 continue;
470 }
471
472 self.constrain_sig_input(arg_ins, arg_recv);
473 }
474
475 if let Some((rest_var, rest_ty)) = rest_bind
476 && self.info.vars.contains_key(&rest_var.def)
477 {
478 self.constrain_sig_input(&rest_ty, &Ty::Var(rest_var));
479 }
480 }
481
482 fn constrain_sig_input(&mut self, actual: &Ty, input: &Ty) {
483 let Ty::Var(input_var) = input else {
484 self.constrain(actual, input);
485 return;
486 };
487
488 let is_external = input_var
489 .def
490 .file_id()
491 .is_some_and(|fid| !Self::same_file_id(fid, self.ei.fid));
492 if is_external {
493 let contract = self.info.simplify(input.clone(), false);
494 self.constrain(actual, &contract);
495 return;
496 }
497
498 if matches!(input_var.def.as_ref(), Decl::Docs(..)) {
499 let input_bounds = self
501 .info
502 .vars
503 .get(&input_var.def)
504 .map(|input| input.bounds.bounds().read().freeze())
505 .unwrap_or_default();
506 let has_contract = !input_bounds.lbs.is_empty() || !input_bounds.ubs.is_empty();
507 if has_contract {
508 let contract = self.info.simplify(Ty::Let(input_bounds.into()), false);
509 let actual_snapshot = self.close_lower_snapshot(actual);
510 if !matches!(actual_snapshot, Ty::Any) {
511 self.constrain(&actual_snapshot, input);
512 }
513 self.constrain(actual, &contract);
514 }
515 return;
516 }
517
518 self.constrain(actual, input);
519 }
520
521 fn close_lower_snapshot(&self, ty: &Ty) -> Ty {
522 let bounds = match ty {
523 Ty::Var(var) => {
524 let Some(bounds) = self.info.vars.get(&var.def) else {
525 return Ty::Any;
526 };
527 let bounds = bounds.bounds.bounds().read();
528 TypeBounds {
529 lbs: bounds.lbs.iter().cloned().collect(),
530 ubs: vec![],
531 }
532 }
533 Ty::Let(bounds) => TypeBounds {
534 lbs: bounds.lbs.clone(),
535 ubs: vec![],
536 },
537 ty => TypeBounds {
538 lbs: vec![ty.clone()],
539 ubs: vec![],
540 },
541 };
542 if bounds.lbs.is_empty() {
543 return Ty::Any;
544 }
545
546 let mut closer = FunctionResultantCloser {
547 vars: &self.info.vars,
548 params: FxHashSet::default(),
549 visiting: FxHashSet::default(),
550 visited: 0,
551 };
552 let mut bounds = closer.close_bounds(&bounds);
553 if bounds.ubs.is_empty() && bounds.lbs.len() == 1 {
554 return bounds.lbs.pop().unwrap();
555 }
556 Ty::Let(bounds.into())
557 }
558
559 fn same_file_id(left: TypstFileId, right: TypstFileId) -> bool {
560 left.root() == right.root() && left.vpath() == right.vpath()
561 }
562
563 fn snapshot_function_input_bounds(&self, sig: &SigTy) -> Vec<(Interned<TypeVar>, TypeBounds)> {
564 let mut seen = FxHashSet::default();
565 let mut binders = vec![];
566 for input in sig.inputs() {
567 Self::collect_input_binders(input, &mut seen, &mut binders);
568 }
569
570 binders
571 .into_iter()
572 .map(|binder| {
573 let bounds = self
574 .info
575 .vars
576 .get(&binder.def)
577 .map(|bounds| bounds.bounds.bounds().read().freeze())
578 .unwrap_or_default();
579 (binder, bounds)
580 })
581 .collect()
582 }
583
584 fn close_function_resultant_type(
585 &self,
586 body: Ty,
587 input_bounds: &[(Interned<TypeVar>, TypeBounds)],
588 escaped: &FxHashSet<DeclExpr>,
589 ) -> Ty {
590 let mut resultant_params = escaped.clone();
591 resultant_params.extend(
592 input_bounds
593 .iter()
594 .map(|(binder, _)| &binder.def)
595 .filter(|def| !self.overwritten_vars.contains(*def))
596 .cloned(),
597 );
598
599 let mut closer = FunctionResultantCloser {
600 vars: &self.info.vars,
601 params: resultant_params,
602 visiting: FxHashSet::default(),
603 visited: 0,
604 };
605
606 closer.mutate(&body, true).unwrap_or(body)
607 }
608
609 fn rebind_overwritten_function_inputs(
610 &mut self,
611 mut sig: SigTy,
612 input_bounds: Vec<(Interned<TypeVar>, TypeBounds)>,
613 escaped: &FxHashSet<DeclExpr>,
614 ) -> SigTy {
615 let mut scope = escaped.clone();
616 let mut replacements: Vec<(Interned<TypeVar>, Interned<TypeVar>)> = Vec::new();
617
618 for (binder, mut bounds) in input_bounds {
619 if !self.overwritten_vars.contains(&binder.def) {
620 scope.insert(binder.def.clone());
621 continue;
622 }
623 if let Some(input_bounds) = self.input_contract_bounds.get(&binder.def) {
624 let input_bounds = input_bounds.freeze();
625 bounds.lbs.extend(input_bounds.lbs);
626 bounds.ubs.extend(input_bounds.ubs);
627 }
628 bounds.lbs.sort();
629 bounds.lbs.dedup();
630 bounds.ubs.sort();
631 bounds.ubs.dedup();
632
633 let mut closer = FunctionResultantCloser {
634 vars: &self.info.vars,
635 params: scope.clone(),
636 visiting: FxHashSet::default(),
637 visited: 0,
638 };
639 let mut bounds = closer.close_bounds(&bounds);
640 bounds.lbs.sort();
641 bounds.lbs.dedup();
642 bounds.ubs.sort();
643 bounds.ubs.dedup();
644
645 for (original, replacement) in &replacements {
646 for bound in bounds.lbs.iter_mut().chain(&mut bounds.ubs) {
647 *bound =
648 Self::replace_var(bound.clone(), original, Ty::Var(replacement.clone()));
649 }
650 }
651
652 let fresh_def: DeclExpr = Decl::docs(binder.def.clone(), binder.clone()).into();
653 let fresh = TypeVar {
654 name: binder.name.clone(),
655 def: fresh_def.clone(),
656 };
657 let fresh = TypeVarBounds::new(fresh, DynTypeBounds::from(bounds));
658 let fresh_var = fresh.var.clone();
659 self.info.vars.insert(fresh_def, fresh);
660 replacements.push((binder.clone(), fresh_var));
661 scope.insert(binder.def.clone());
662 }
663
664 if !replacements.is_empty() {
665 let mut inputs = sig.inputs.as_ref().clone();
666 for input in &mut inputs {
667 for (original, replacement) in &replacements {
668 *input =
669 Self::replace_var(input.clone(), original, Ty::Var(replacement.clone()));
670 }
671 }
672 sig.inputs = inputs.into();
673 }
674
675 sig
676 }
677
678 fn rest_arg_bind(
679 sig: &Interned<SigTy>,
680 args: &Interned<SigTy>,
681 with: Option<&Vec<Interned<SigTy>>>,
682 ) -> Option<(Interned<TypeVar>, Ty)> {
683 let Ty::Var(rest_var) = sig.rest_param()? else {
684 return None;
685 };
686
687 let fixed_pos = sig.positional_params().len();
688 let rest_pos = with
689 .into_iter()
690 .flat_map(|withs| withs.iter().rev())
691 .flat_map(|with| with.positional_params())
692 .chain(args.positional_params())
693 .skip(fixed_pos)
694 .cloned()
695 .collect::<Vec<_>>();
696
697 let rest_named = args
698 .named_params()
699 .filter(|(name, _)| sig.named(name).is_none())
700 .map(|(name, ty)| (name.clone(), ty.clone()))
701 .collect::<Vec<_>>();
702
703 let rest = args.rest_param().cloned();
704 let rest_args = ArgsTy::new(rest_pos.into_iter(), rest_named, None, rest, None);
705
706 Some((rest_var.clone(), Ty::Args(rest_args.into())))
707 }
708
709 fn collect_type_vars(
710 ty: &Ty,
711 vars: &mut FxHashSet<DeclExpr>,
712 binders: &mut Vec<Interned<TypeVar>>,
713 ) {
714 match ty {
715 Ty::Var(var) => {
716 if vars.insert(var.def.clone()) {
717 binders.push(var.clone());
718 }
719 }
720 Ty::Param(param) => Self::collect_type_vars(¶m.ty, vars, binders),
721 Ty::Union(types) | Ty::Tuple(types) => {
722 for ty in types.iter() {
723 Self::collect_type_vars(ty, vars, binders);
724 }
725 }
726 Ty::Let(bounds) => {
727 for ty in bounds.lbs.iter().chain(bounds.ubs.iter()) {
728 Self::collect_type_vars(ty, vars, binders);
729 }
730 }
731 Ty::Dict(record) => {
732 for ty in record.types.iter() {
733 Self::collect_type_vars(ty, vars, binders);
734 }
735 }
736 Ty::Array(elem) => Self::collect_type_vars(elem, vars, binders),
737 Ty::Func(sig) | Ty::Args(sig) | Ty::Pattern(sig) => {
738 for ty in sig.inputs() {
739 Self::collect_type_vars(ty, vars, binders);
740 }
741 if let Some(body) = &sig.body {
742 Self::collect_type_vars(body, vars, binders);
743 }
744 }
745 Ty::With(with) => {
746 Self::collect_type_vars(&with.sig, vars, binders);
747 for ty in with.with.inputs() {
748 Self::collect_type_vars(ty, vars, binders);
749 }
750 if let Some(body) = &with.with.body {
751 Self::collect_type_vars(body, vars, binders);
752 }
753 }
754 Ty::Select(sel) => Self::collect_type_vars(&sel.ty, vars, binders),
755 Ty::Unary(unary) => Self::collect_type_vars(&unary.lhs, vars, binders),
756 Ty::Binary(binary) => {
757 let [lhs, rhs] = binary.operands();
758 Self::collect_type_vars(lhs, vars, binders);
759 Self::collect_type_vars(rhs, vars, binders);
760 }
761 Ty::If(if_ty) => {
762 Self::collect_type_vars(&if_ty.cond, vars, binders);
763 Self::collect_type_vars(&if_ty.then, vars, binders);
764 Self::collect_type_vars(&if_ty.else_, vars, binders);
765 }
766 Ty::Any | Ty::Boolean(_) | Ty::Builtin(_) | Ty::Value(_) => {}
767 }
768 }
769
770 fn constrain(&mut self, lhs: &Ty, rhs: &Ty) {
771 static FLOW_STROKE_DICT_TYPE: LazyLock<Ty> =
772 LazyLock::new(|| Ty::Dict(FLOW_STROKE_DICT.clone()));
773 static FLOW_MARGIN_DICT_TYPE: LazyLock<Ty> =
774 LazyLock::new(|| Ty::Dict(FLOW_MARGIN_DICT.clone()));
775 static FLOW_INSET_DICT_TYPE: LazyLock<Ty> =
776 LazyLock::new(|| Ty::Dict(FLOW_INSET_DICT.clone()));
777 static FLOW_OUTSET_DICT_TYPE: LazyLock<Ty> =
778 LazyLock::new(|| Ty::Dict(FLOW_OUTSET_DICT.clone()));
779 static FLOW_RADIUS_DICT_TYPE: LazyLock<Ty> =
780 LazyLock::new(|| Ty::Dict(FLOW_RADIUS_DICT.clone()));
781 static FLOW_TEXT_FONT_DICT_TYPE: LazyLock<Ty> =
782 LazyLock::new(|| Ty::Dict(FLOW_TEXT_FONT_DICT.clone()));
783
784 fn type_value_instance(ty: &Ty) -> Option<Ty> {
785 match ty {
786 Ty::Builtin(ty @ BuiltinTy::Type(..)) => Some(Ty::Builtin(ty.clone())),
787 Ty::Value(val) => match val.val {
788 Value::Type(ty) => Some(Ty::Builtin(BuiltinTy::Type(ty))),
789 _ => None,
790 },
791 _ => None,
792 }
793 }
794
795 if lhs == rhs {
796 return;
797 }
798
799 match (lhs, rhs) {
800 (Ty::Var(v), Ty::Var(w)) => {
801 if v.def == w.def {
802 return;
803 }
804 let Some(rhs) = self.info.vars.get(&w.def) else {
805 return;
806 };
807 match &rhs.bounds {
808 FlowVarKind::Strong(bounds) | FlowVarKind::Weak(bounds) => {
809 bounds.write().lbs.insert_mut(Ty::Var(v.clone()));
810 }
811 }
812 self.record_input_lower_bound(&w.def, Ty::Var(v.clone()));
813 }
814 (Ty::Var(v), rhs) => {
815 crate::log_debug_ct!("constrain var {v:?} ⪯ {rhs:?}");
816 let Some(w) = self.info.vars.get_mut(&v.def) else {
817 return;
818 };
819 let bound = rhs.clone();
821 match &w.bounds {
822 FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
823 let mut w = w.write();
824 w.ubs.insert_mut(bound.clone());
825 }
826 }
827 self.record_input_upper_bound(&v.def, bound);
828 }
829 (lhs, Ty::Var(v)) => {
830 let Some(w) = self.info.vars.get(&v.def) else {
831 return;
832 };
833 let bound = self.weaken_constraint(lhs, &w.bounds);
834 crate::log_debug_ct!("constrain var {v:?} ⪰ {bound:?}");
835 match &w.bounds {
836 FlowVarKind::Strong(v) | FlowVarKind::Weak(v) => {
837 let mut v = v.write();
838 v.lbs.insert_mut(bound.clone());
839 }
840 };
841 self.record_input_lower_bound(&v.def, bound);
842 }
843 (Ty::Select(sel), rhs) => {
844 let dict = Ty::Dict(RecordTy::new(vec![(sel.select.clone(), rhs.clone())]));
849 self.constrain(sel.ty.as_ref(), &dict);
850 }
851 (Ty::Array(lhs), Ty::Array(rhs)) => {
852 self.constrain(lhs, rhs);
853 }
854 (Ty::Tuple(lhs), Ty::Array(rhs)) => {
855 for lhs in lhs.iter() {
856 self.constrain(lhs, rhs);
857 }
858 }
859 (Ty::Tuple(lhs), Ty::Tuple(rhs)) => {
860 self.constrain_tuple_positions(lhs, rhs.iter());
861 }
862 (Ty::Tuple(lhs), Ty::Pattern(rhs)) => {
863 self.constrain_tuple_positions(lhs, rhs.positional_params());
864 }
865 (Ty::Dict(lhs), Ty::Dict(rhs)) => {
866 for (key, lhs, rhs) in lhs.common_iface_fields(rhs) {
867 crate::log_debug_ct!("constrain record item {key} {lhs:?} ⪯ {rhs:?}");
868 self.constrain(lhs, rhs);
869 }
876 }
877 (Ty::Union(types), rhs) => {
878 for ty in types.iter() {
879 self.constrain(ty, rhs);
880 }
881 }
882 (lhs, Ty::Union(types)) => {
883 for ty in types.iter() {
884 self.constrain(lhs, ty);
885 }
886 }
887 (lhs, Ty::Builtin(BuiltinTy::Stroke)) => {
888 if lhs.is_dict() {
891 self.constrain(lhs, &FLOW_STROKE_DICT_TYPE);
892 }
893 }
894 (Ty::Builtin(BuiltinTy::Stroke), rhs) => {
895 if rhs.is_dict() {
896 self.constrain(&FLOW_STROKE_DICT_TYPE, rhs);
897 }
898 }
899 (lhs, Ty::Builtin(BuiltinTy::Margin)) => {
900 if lhs.is_dict() {
901 self.constrain(lhs, &FLOW_MARGIN_DICT_TYPE);
902 }
903 }
904 (Ty::Builtin(BuiltinTy::Margin), rhs) => {
905 if rhs.is_dict() {
906 self.constrain(&FLOW_MARGIN_DICT_TYPE, rhs);
907 }
908 }
909 (lhs, Ty::Builtin(BuiltinTy::Inset)) => {
910 if lhs.is_dict() {
911 self.constrain(lhs, &FLOW_INSET_DICT_TYPE);
912 }
913 }
914 (Ty::Builtin(BuiltinTy::Inset), rhs) => {
915 if rhs.is_dict() {
916 self.constrain(&FLOW_INSET_DICT_TYPE, rhs);
917 }
918 }
919 (lhs, Ty::Builtin(BuiltinTy::Outset)) => {
920 if lhs.is_dict() {
921 self.constrain(lhs, &FLOW_OUTSET_DICT_TYPE);
922 }
923 }
924 (Ty::Builtin(BuiltinTy::Outset), rhs) => {
925 if rhs.is_dict() {
926 self.constrain(&FLOW_OUTSET_DICT_TYPE, rhs);
927 }
928 }
929 (lhs, Ty::Builtin(BuiltinTy::Radius)) => {
930 if lhs.is_dict() {
931 self.constrain(lhs, &FLOW_RADIUS_DICT_TYPE);
932 }
933 }
934 (Ty::Builtin(BuiltinTy::Radius), rhs) => {
935 if rhs.is_dict() {
936 self.constrain(&FLOW_RADIUS_DICT_TYPE, rhs);
937 }
938 }
939 (lhs, Ty::Builtin(BuiltinTy::TextFont)) => {
940 if lhs.is_dict() {
941 self.constrain(lhs, &FLOW_TEXT_FONT_DICT_TYPE);
942 }
943 }
944 (Ty::Builtin(BuiltinTy::TextFont), rhs) => {
945 if rhs.is_dict() {
946 self.constrain(&FLOW_TEXT_FONT_DICT_TYPE, rhs);
947 }
948 }
949 (Ty::Unary(lhs), Ty::Unary(rhs)) if lhs.op == rhs.op => {
950 self.constrain(&lhs.lhs, &rhs.lhs);
953 }
954 (Ty::Unary(lhs), rhs) if lhs.op == UnaryOp::TypeOf => {
955 if let Some(rhs) = type_value_instance(rhs) {
956 crate::log_debug_ct!("constrain type of {lhs:?} ⪯ {rhs:?}");
957 self.constrain(&lhs.lhs, &rhs);
958 }
959 }
960 (lhs, Ty::Unary(rhs)) if rhs.op == UnaryOp::TypeOf => {
961 if let Some(lhs) = type_value_instance(lhs) {
962 crate::log_debug_ct!(
963 "constrain type of {lhs:?} ⪯ {rhs:?} {:?}",
964 matches!(lhs, Ty::Builtin(..)),
965 );
966 self.constrain(&lhs, &rhs.lhs);
967 }
968 }
969 (Ty::Func(lhs), Ty::Func(rhs)) => {
970 crate::log_debug_ct!("constrain func {lhs:?} ⪯ {rhs:?}");
971 self.constrain_sig_inputs(lhs, rhs, None);
972 }
973 (Ty::Value(lhs), rhs) => {
974 crate::log_debug_ct!("constrain value {lhs:?} ⪯ {rhs:?}");
975 let _ = TypeInfo::witness_at_most;
976 }
980 (lhs, Ty::Value(rhs)) => {
981 crate::log_debug_ct!("constrain value {lhs:?} ⪯ {rhs:?}");
982 }
986 _ => {
987 crate::log_debug_ct!("constrain {lhs:?} ⪯ {rhs:?}");
988 }
989 }
990 }
991
992 fn constrain_tuple_positions<'a>(
993 &mut self,
994 lhs: &[Ty],
995 rhs: impl ExactSizeIterator<Item = &'a Ty>,
996 ) {
997 for (idx, rhs) in rhs.enumerate() {
998 let mut any = false;
999 for lhs in self.tuple_pos_candidates(lhs, idx) {
1000 any = true;
1001 self.constrain(&lhs, rhs);
1002 }
1003
1004 if !any && let Some(spread) = self.tuple_open_spread(lhs, idx) {
1005 self.constrain(spread, &Ty::Array(rhs.clone().into()));
1006 }
1007 }
1008 }
1009
1010 fn record_input_lower_bound(&mut self, def: &DeclExpr, bound: Ty) {
1011 if self.live_input_vars.contains(def) {
1012 self.input_contract_bounds
1013 .entry(def.clone())
1014 .or_default()
1015 .lbs
1016 .insert_mut(bound);
1017 }
1018 }
1019
1020 fn record_input_upper_bound(&mut self, def: &DeclExpr, bound: Ty) {
1021 if !matches!(bound, Ty::Any) && self.live_input_vars.contains(def) {
1023 self.input_contract_bounds
1024 .entry(def.clone())
1025 .or_default()
1026 .ubs
1027 .insert_mut(bound);
1028 }
1029 }
1030
1031 fn tuple_pos_candidates(&self, elems: &[Ty], idx: usize) -> Vec<Ty> {
1032 let mut pos = 0;
1033 let mut candidates = vec![];
1034
1035 for elem in elems {
1036 if let Some(spread) = Self::spread_operand(elem) {
1037 let spread_idx = idx.saturating_sub(pos);
1038 self.collect_spread_pos_candidates(spread, spread_idx, &mut candidates);
1039 if !candidates.is_empty() {
1040 return candidates;
1041 }
1042
1043 if let Some(len) = self.fixed_spread_len(spread) {
1044 pos += len;
1045 continue;
1046 }
1047
1048 if idx >= pos {
1049 return candidates;
1050 }
1051 } else if pos == idx {
1052 candidates.push(elem.clone());
1053 return candidates;
1054 } else {
1055 pos += 1;
1056 }
1057 }
1058
1059 candidates
1060 }
1061
1062 fn tuple_open_spread<'a>(&self, elems: &'a [Ty], idx: usize) -> Option<&'a Ty> {
1063 let mut pos = 0;
1064
1065 for elem in elems {
1066 if let Some(spread) = Self::spread_operand(elem) {
1067 if idx >= pos {
1068 if let Some(len) = self.fixed_spread_len(spread) {
1069 if idx < pos + len {
1070 return Some(spread);
1071 }
1072 pos += len;
1073 continue;
1074 }
1075 return Some(spread);
1076 }
1077 } else if pos == idx {
1078 return None;
1079 } else {
1080 pos += 1;
1081 }
1082 }
1083
1084 None
1085 }
1086
1087 fn spread_operand(ty: &Ty) -> Option<&Ty> {
1088 let Ty::Unary(unary) = ty else {
1089 return None;
1090 };
1091 (unary.op == UnaryOp::Spread).then_some(&unary.lhs)
1092 }
1093
1094 fn fixed_spread_len(&self, ty: &Ty) -> Option<usize> {
1095 match ty {
1096 Ty::Tuple(elems) => Some(elems.len()),
1097 Ty::Args(args) if args.rest_param().is_none() => Some(args.positional_params().len()),
1098 Ty::Var(var) => {
1099 let bounds = self.info.vars.get(&var.def)?;
1100 let lbs = bounds.bounds.bounds().read().lbs.clone();
1101 let mut len = None;
1102 for lb in lbs.iter() {
1103 let next = self.fixed_spread_len(lb)?;
1104 if len.is_some_and(|prev| prev != next) {
1105 return None;
1106 }
1107 len = Some(next);
1108 }
1109 len
1110 }
1111 Ty::Let(bounds) => {
1112 let mut len = None;
1113 for lb in bounds.lbs.iter() {
1114 let next = self.fixed_spread_len(lb)?;
1115 if len.is_some_and(|prev| prev != next) {
1116 return None;
1117 }
1118 len = Some(next);
1119 }
1120 len
1121 }
1122 _ => None,
1123 }
1124 }
1125
1126 fn collect_spread_pos_candidates(&self, ty: &Ty, idx: usize, candidates: &mut Vec<Ty>) {
1127 match ty {
1128 Ty::Array(elem) => candidates.push(elem.as_ref().clone()),
1129 Ty::Tuple(elems) => {
1130 if let Some(elem) = elems.get(idx) {
1131 candidates.push(elem.clone());
1132 }
1133 }
1134 Ty::Args(args) => {
1135 if let Some(elem) = args.pos_or_rest(idx) {
1136 candidates.push(elem);
1137 }
1138 }
1139 Ty::Var(var) => {
1140 if let Some(bounds) = self.info.vars.get(&var.def) {
1141 let lbs = bounds.bounds.bounds().read().lbs.clone();
1142 for lb in lbs.iter() {
1143 self.collect_spread_pos_candidates(lb, idx, candidates);
1144 }
1145 }
1146 }
1147 Ty::Let(bounds) => {
1148 for lb in bounds.lbs.iter() {
1149 self.collect_spread_pos_candidates(lb, idx, candidates);
1150 }
1151 }
1152 _ => {}
1153 }
1154 }
1155
1156 fn check_comparable(&self, lhs: &Ty, rhs: &Ty) {
1157 let _ = lhs;
1158 let _ = rhs;
1159 }
1160
1161 fn check_assignable(&self, lhs: &Ty, rhs: &Ty) {
1162 let _ = lhs;
1163 let _ = rhs;
1164 }
1165
1166 fn constrain_assignment(&mut self, lhs: &Ty, rhs: &Ty) {
1167 match lhs {
1168 Ty::Var(var) => {
1169 if !self.assign_var(var, rhs) {
1170 self.possible_ever_be(lhs, rhs);
1171 }
1172 }
1173 Ty::Tuple(_) | Ty::Pattern(_) => self.constrain(rhs, lhs),
1174 Ty::Union(types) => {
1175 for lhs in types.iter() {
1176 self.constrain_assignment(lhs, rhs);
1177 }
1178 }
1179 Ty::Let(bounds) => {
1180 for lhs in bounds.lbs.iter().chain(bounds.ubs.iter()) {
1181 self.constrain_assignment(lhs, rhs);
1182 }
1183 }
1184 _ => self.possible_ever_be(lhs, rhs),
1185 }
1186
1187 let mut vars = FxHashSet::default();
1188 let mut binders = vec![];
1189 Self::collect_type_vars(lhs, &mut vars, &mut binders);
1190 for binder in binders {
1191 self.live_input_vars.remove(&binder.def);
1192 }
1193 }
1194
1195 fn assign_var(&mut self, var: &Interned<TypeVar>, rhs: &Ty) -> bool {
1196 if !Self::assignment_rhs_overwrites(rhs) {
1197 return false;
1198 }
1199
1200 let rhs_mentions_var = match Self::type_contains_var(rhs, &var.def) {
1201 Some(rhs_mentions_var) => rhs_mentions_var,
1202 None => return false,
1203 };
1204
1205 let rhs = if rhs_mentions_var {
1206 let mut snapshot = self.shallow_lower_bound(Ty::Var(var.clone()));
1207 if !matches!(Self::type_contains_var(&snapshot, &var.def), Some(false)) {
1208 snapshot = Ty::Any;
1209 }
1210
1211 let rhs = Self::replace_var(rhs.clone(), var, snapshot);
1212 if !matches!(Self::type_contains_var(&rhs, &var.def), Some(false)) {
1213 return false;
1214 }
1215 rhs
1216 } else {
1217 rhs.clone()
1218 };
1219 let rhs = self.shallow_lower_bound(rhs);
1220 if self.live_input_vars.contains(&var.def)
1222 && let Some(bounds) = self.info.vars.get(&var.def)
1223 {
1224 let bounds = bounds.bounds.bounds().read().freeze();
1225 let input = self
1226 .input_contract_bounds
1227 .entry(var.def.clone())
1228 .or_default();
1229 for bound in bounds.lbs {
1230 input.lbs.insert_mut(bound);
1231 }
1232 for bound in bounds.ubs {
1233 input.ubs.insert_mut(bound);
1234 }
1235 }
1236 let Some(bounds) = self.info.vars.get_mut(&var.def) else {
1237 return false;
1238 };
1239 self.overwritten_vars.insert(var.def.clone());
1240 let mut bounds = bounds.bounds.bounds().write();
1241 bounds.lbs = [rhs].into_iter().collect();
1242 true
1243 }
1244
1245 fn assignment_rhs_overwrites(rhs: &Ty) -> bool {
1246 !matches!(rhs, Ty::Any | Ty::Select(_) | Ty::Binary(_))
1247 }
1248
1249 fn replace_var(ty: Ty, var: &Interned<TypeVar>, with: Ty) -> Ty {
1250 let mut replacer = VarReplacer {
1251 def: var.def.clone(),
1252 with,
1253 };
1254 ty.mutate(true, &mut replacer).unwrap_or(ty)
1255 }
1256
1257 fn type_contains_var(ty: &Ty, def: &DeclExpr) -> Option<bool> {
1258 const NODE_BUDGET: usize = 4096;
1259
1260 let mut stack = vec![ty];
1261 let mut visited = 0usize;
1262 while let Some(ty) = stack.pop() {
1263 visited += 1;
1264 if visited > NODE_BUDGET {
1265 return None;
1266 }
1267
1268 match ty {
1269 Ty::Var(var) if var.def == *def => return Some(true),
1270 Ty::Param(param) => stack.push(¶m.ty),
1271 Ty::Union(types) | Ty::Tuple(types) => stack.extend(types.iter()),
1272 Ty::Let(bounds) => {
1273 stack.extend(bounds.lbs.iter());
1274 stack.extend(bounds.ubs.iter());
1275 }
1276 Ty::Dict(record) => stack.extend(record.types.iter()),
1277 Ty::Array(elem) => stack.push(elem),
1278 Ty::Func(sig) | Ty::Args(sig) | Ty::Pattern(sig) => {
1279 stack.extend(sig.inputs());
1280 if let Some(body) = &sig.body {
1281 stack.push(body);
1282 }
1283 }
1284 Ty::With(with) => {
1285 stack.push(&with.sig);
1286 stack.extend(with.with.inputs());
1287 if let Some(body) = &with.with.body {
1288 stack.push(body);
1289 }
1290 }
1291 Ty::Select(sel) => stack.push(&sel.ty),
1292 Ty::Unary(unary) => stack.push(&unary.lhs),
1293 Ty::Binary(binary) => {
1294 let [lhs, rhs] = binary.operands();
1295 stack.push(lhs);
1296 stack.push(rhs);
1297 }
1298 Ty::If(if_ty) => {
1299 stack.push(&if_ty.cond);
1300 stack.push(&if_ty.then);
1301 stack.push(&if_ty.else_);
1302 }
1303 Ty::Var(_) | Ty::Any | Ty::Boolean(_) | Ty::Builtin(_) | Ty::Value(_) => {}
1304 }
1305 }
1306
1307 Some(false)
1308 }
1309
1310 fn check_containing(&mut self, container: &Ty, elem: &Ty, expected_in: bool) {
1311 let rhs = if expected_in {
1312 match container {
1313 Ty::Tuple(elements) => Ty::Union(elements.clone()),
1314 _ => Ty::Unary(TypeUnary::new(UnaryOp::ElementOf, container.clone())),
1315 }
1316 } else {
1317 Ty::Unary(TypeUnary::new(UnaryOp::NotElementOf, container.clone()))
1319 };
1320
1321 self.constrain(elem, &rhs);
1322 }
1323
1324 fn possible_ever_be(&mut self, lhs: &Ty, rhs: &Ty) {
1325 match rhs {
1327 Ty::Builtin(..) | Ty::Value(..) | Ty::Boolean(..) => {
1328 self.constrain(rhs, lhs);
1329 }
1330 _ => {}
1331 }
1332 }
1333
1334 fn weaken(&mut self, v: &Ty) {
1335 match v {
1336 Ty::Var(v) => {
1337 let w = self.info.vars.get_mut(&v.def).unwrap();
1338 w.weaken();
1339 }
1340 Ty::Any | Ty::Boolean(_) | Ty::Builtin(_) | Ty::Value(_) => {}
1341 Ty::Param(v) => {
1342 self.weaken(&v.ty);
1343 }
1344 Ty::Func(v) | Ty::Args(v) | Ty::Pattern(v) => {
1345 for ty in v.inputs() {
1346 self.weaken(ty);
1347 }
1348 }
1349 Ty::With(v) => {
1350 self.weaken(&v.sig);
1351 for ty in v.with.inputs() {
1352 self.weaken(ty);
1353 }
1354 }
1355 Ty::Dict(v) => {
1356 for (_, ty) in v.interface() {
1357 self.weaken(ty);
1358 }
1359 }
1360 Ty::Array(v) => {
1361 self.weaken(v);
1362 }
1363 Ty::Tuple(v) => {
1364 for ty in v.iter() {
1365 self.weaken(ty);
1366 }
1367 }
1368 Ty::Select(v) => {
1369 self.weaken(&v.ty);
1370 }
1371 Ty::Unary(v) => {
1372 self.weaken(&v.lhs);
1373 }
1374 Ty::Binary(v) => {
1375 let [lhs, rhs] = v.operands();
1376 self.weaken(lhs);
1377 self.weaken(rhs);
1378 }
1379 Ty::If(v) => {
1380 self.weaken(&v.cond);
1381 self.weaken(&v.then);
1382 self.weaken(&v.else_);
1383 }
1384 Ty::Union(v) => {
1385 for ty in v.iter() {
1386 self.weaken(ty);
1387 }
1388 }
1389 Ty::Let(v) => {
1390 for ty in v.lbs.iter() {
1391 self.weaken(ty);
1392 }
1393 for ty in v.ubs.iter() {
1394 self.weaken(ty);
1395 }
1396 }
1397 }
1398 }
1399
1400 fn weaken_constraint(&self, term: &Ty, kind: &FlowVarKind) -> Ty {
1401 if matches!(kind, FlowVarKind::Strong(_)) {
1402 return term.clone();
1403 }
1404
1405 if let Ty::Value(ins_ty) = term {
1406 return BuiltinTy::from_value(&ins_ty.val);
1407 }
1408
1409 term.clone()
1410 }
1411}
1412
1413struct ControlSplit {
1414 normal: Option<Ty>,
1415 returns: Vec<Ty>,
1416 terminal: bool,
1417}
1418
1419struct FunctionResultantCloser<'a> {
1420 vars: &'a FxHashMap<DeclExpr, TypeVarBounds>,
1421 params: FxHashSet<DeclExpr>,
1422 visiting: FxHashSet<DeclExpr>,
1423 visited: usize,
1424}
1425
1426impl FunctionResultantCloser<'_> {
1427 const NODE_BUDGET: usize = 4096;
1428
1429 fn close_scoped_sig(&mut self, sig: &SigTy, pol: bool) -> Option<SigTy> {
1430 let mut seen = FxHashSet::default();
1431 let mut binders = vec![];
1432 for input in sig.inputs() {
1433 TypeChecker::collect_input_binders(input, &mut seen, &mut binders);
1434 }
1435 let inserted = binders
1436 .into_iter()
1437 .filter_map(|binder| {
1438 self.params
1439 .insert(binder.def.clone())
1440 .then_some(binder.def.clone())
1441 })
1442 .collect::<Vec<_>>();
1443
1444 let inputs = self.mutate_vec(&sig.inputs, pol);
1445 let body = self.mutate_option(sig.body.as_ref(), pol);
1446
1447 for def in inserted {
1448 self.params.remove(&def);
1449 }
1450 if inputs.is_none() && body.is_none() {
1451 return None;
1452 }
1453
1454 let mut sig = sig.clone();
1455 if let Some(inputs) = inputs {
1456 sig.inputs = inputs;
1457 }
1458 if let Some(body) = body {
1459 sig.body = body;
1460 }
1461 Some(sig)
1462 }
1463
1464 fn lower_bounds_of(&mut self, var: &Interned<TypeVar>) -> Option<Ty> {
1465 self.visited += 1;
1466 if self.visited > Self::NODE_BUDGET {
1467 return Some(Ty::Any);
1468 }
1469
1470 if self.params.contains(&var.def) {
1471 return None;
1472 }
1473 if !self.visiting.insert(var.def.clone()) {
1474 return Some(Ty::Any);
1475 }
1476
1477 let Some(bounds) = self.vars.get(&var.def) else {
1478 self.visiting.remove(&var.def);
1479 return Some(Ty::Any);
1480 };
1481 let bounds = bounds.bounds.bounds().read().freeze();
1482 if bounds.lbs.is_empty() && bounds.ubs.is_empty() {
1483 self.visiting.remove(&var.def);
1484 return Some(Ty::Any);
1485 }
1486
1487 let bounds = self.close_bounds(&bounds);
1488 self.visiting.remove(&var.def);
1489 Some(Ty::Let(Interned::new(bounds)))
1490 }
1491
1492 fn close_bounds(&mut self, bounds: &TypeBounds) -> TypeBounds {
1493 let lbs = bounds
1494 .lbs
1495 .iter()
1496 .map(|bound| self.mutate(bound, false).unwrap_or_else(|| bound.clone()))
1497 .collect();
1498 let ubs = bounds
1499 .ubs
1500 .iter()
1501 .map(|bound| self.mutate(bound, true).unwrap_or_else(|| bound.clone()))
1502 .collect();
1503 TypeBounds { lbs, ubs }
1504 }
1505}
1506
1507impl TyMutator for FunctionResultantCloser<'_> {
1508 fn mutate(&mut self, ty: &Ty, pol: bool) -> Option<Ty> {
1509 match ty {
1510 Ty::Var(var) => self.lower_bounds_of(var),
1511 Ty::Let(bounds) => Some(Ty::Let(Interned::new(self.close_bounds(bounds)))),
1512 Ty::Func(sig) => self
1513 .close_scoped_sig(sig, pol)
1514 .map(|sig| Ty::Func(Interned::new(sig))),
1515 Ty::Pattern(sig) => self
1516 .close_scoped_sig(sig, pol)
1517 .map(|sig| Ty::Pattern(Interned::new(sig))),
1518 _ => self.mutate_rec(ty, pol),
1519 }
1520 }
1521}
1522
1523struct Joiner {
1524 break_or_continue_or_return: bool,
1525 definite: Ty,
1526 possibles: Vec<Ty>,
1527 returns: Vec<Ty>,
1528}
1529impl Joiner {
1530 fn finalize(self) -> Ty {
1531 crate::log_debug_ct!(
1532 "join: {:?} {:?} returns {:?}",
1533 self.possibles,
1534 self.definite,
1535 self.returns
1536 );
1537
1538 let normal = Self::finalize_normal(self.definite, self.possibles);
1539 if self.returns.is_empty() {
1540 if self.break_or_continue_or_return {
1541 return Ty::Builtin(BuiltinTy::Never);
1542 }
1543 return normal;
1544 }
1545
1546 let returned = Self::finalize_types(self.returns);
1547 if self.break_or_continue_or_return {
1548 return Ty::Unary(TypeUnary::new(UnaryOp::Return, returned));
1549 }
1550
1551 if Self::is_none_like(&normal) {
1552 return Ty::Any;
1553 }
1554 if normal == returned {
1555 return normal;
1556 }
1557
1558 Ty::from_types([normal, returned].into_iter())
1559 }
1560
1561 fn finalize_normal(definite: Ty, possibles: Vec<Ty>) -> Ty {
1562 if possibles.is_empty() {
1563 return definite;
1564 }
1565 if possibles.len() == 1 {
1566 return possibles.into_iter().next().unwrap();
1567 }
1568
1569 Ty::Any
1577 }
1578
1579 fn finalize_types(types: Vec<Ty>) -> Ty {
1580 if types.len() == 1 {
1581 return types.into_iter().next().unwrap();
1582 }
1583
1584 Ty::from_types(types.into_iter())
1585 }
1586
1587 fn is_none_like(ty: &Ty) -> bool {
1588 matches!(
1589 ty,
1590 Ty::Builtin(
1591 BuiltinTy::Space | BuiltinTy::None | BuiltinTy::Clause | BuiltinTy::FlowNone
1592 )
1593 )
1594 }
1595
1596 fn split_control(child: Ty) -> ControlSplit {
1597 match child {
1598 Ty::Unary(unary) if unary.op == UnaryOp::Return => ControlSplit {
1599 normal: None,
1600 returns: vec![unary.lhs.clone()],
1601 terminal: true,
1602 },
1603 Ty::Builtin(BuiltinTy::Break | BuiltinTy::Continue | BuiltinTy::Never) => {
1604 ControlSplit {
1605 normal: None,
1606 returns: vec![],
1607 terminal: true,
1608 }
1609 }
1610 Ty::If(if_ty) => {
1611 let then = Self::split_control(if_ty.then.as_ref().clone());
1612 let else_ = Self::split_control(if_ty.else_.as_ref().clone());
1613
1614 let mut returns = then.returns;
1615 returns.extend(else_.returns);
1616
1617 let then_normal = then.normal.filter(|ty| !Self::is_none_like(ty));
1618 let else_normal = else_.normal.filter(|ty| !Self::is_none_like(ty));
1619 let normal = if then_normal.is_none() && else_normal.is_none() {
1620 None
1621 } else {
1622 Some(Ty::If(IfTy::new(
1623 if_ty.cond.clone(),
1624 then_normal.unwrap_or(Ty::Builtin(BuiltinTy::None)).into(),
1625 else_normal.unwrap_or(Ty::Builtin(BuiltinTy::None)).into(),
1626 )))
1627 };
1628
1629 ControlSplit {
1630 normal,
1631 returns,
1632 terminal: then.terminal && else_.terminal,
1633 }
1634 }
1635 normal if Self::is_none_like(&normal) => ControlSplit {
1636 normal: None,
1637 returns: vec![],
1638 terminal: false,
1639 },
1640 normal => ControlSplit {
1641 normal: Some(normal),
1642 returns: vec![],
1643 terminal: false,
1644 },
1645 }
1646 }
1647
1648 fn join(&mut self, child: Ty) {
1649 if self.break_or_continue_or_return {
1650 return;
1651 }
1652
1653 let ControlSplit {
1654 normal,
1655 returns,
1656 terminal,
1657 } = Self::split_control(child);
1658
1659 self.returns.extend(returns);
1660 if let Some(normal) = normal {
1661 self.join_normal(normal);
1662 }
1663 if terminal {
1664 self.break_or_continue_or_return = true;
1665 }
1666 }
1667
1668 fn join_normal(&mut self, child: Ty) {
1669 if matches!(self.definite, Ty::Any) && !matches!(child, Ty::Any) {
1670 self.definite = Ty::Builtin(BuiltinTy::None);
1671 }
1672
1673 match (child, &self.definite) {
1674 (Ty::Builtin(BuiltinTy::Space | BuiltinTy::None), _) => {}
1675 (Ty::Builtin(BuiltinTy::Clause | BuiltinTy::FlowNone), _) => {}
1676 (Ty::Any, _) => self.definite = Ty::Any,
1677 (Ty::Var(var), _) => self.possibles.push(Ty::Var(var)),
1678 (Ty::Array(arr), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Array(arr),
1680 (Ty::Array(..), _) => self.definite = Ty::undef(),
1681 (Ty::Tuple(elems), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Tuple(elems),
1682 (Ty::Tuple(..), _) => self.definite = Ty::undef(),
1683 (Ty::Builtin(ty), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Builtin(ty),
1686 (Ty::Builtin(..), _) => self.definite = Ty::undef(),
1687 (Ty::Value(ins_ty), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Value(ins_ty),
1689 (Ty::Value(..), _) => self.definite = Ty::undef(),
1690 (Ty::Func(func), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Func(func),
1691 (Ty::Func(..), _) => self.definite = Ty::undef(),
1692 (Ty::Dict(dict), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Dict(dict),
1693 (Ty::Dict(..), _) => self.definite = Ty::undef(),
1694 (Ty::With(with), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::With(with),
1695 (Ty::With(..), _) => self.definite = Ty::undef(),
1696 (Ty::Args(args), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Args(args),
1697 (Ty::Args(..), _) => self.definite = Ty::undef(),
1698 (Ty::Pattern(pat), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Pattern(pat),
1699 (Ty::Pattern(..), _) => self.definite = Ty::undef(),
1700 (Ty::Select(sel), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Select(sel),
1701 (Ty::Select(..), _) => self.definite = Ty::undef(),
1702 (Ty::Unary(unary), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Unary(unary),
1703 (Ty::Unary(..), _) => self.definite = Ty::undef(),
1704 (Ty::Binary(binary), Ty::Builtin(BuiltinTy::None)) => {
1705 self.definite = Ty::Binary(binary)
1706 }
1707 (Ty::Binary(..), _) => self.definite = Ty::undef(),
1708 (Ty::If(if_ty), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::If(if_ty),
1709 (Ty::If(..), _) => self.definite = Ty::undef(),
1710 (Ty::Union(types), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Union(types),
1711 (Ty::Union(..), _) => self.definite = Ty::undef(),
1712 (Ty::Let(bounds), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Let(bounds),
1713 (Ty::Let(..), _) => self.definite = Ty::undef(),
1714 (Ty::Param(param), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Param(param),
1715 (Ty::Param(..), _) => self.definite = Ty::undef(),
1716 (Ty::Boolean(b), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Boolean(b),
1717 (Ty::Boolean(..), _) => self.definite = Ty::undef(),
1718 }
1719 }
1720}
1721impl Default for Joiner {
1722 fn default() -> Self {
1723 Self {
1724 break_or_continue_or_return: false,
1725 definite: Ty::Builtin(BuiltinTy::None),
1726 possibles: Vec::new(),
1727 returns: Vec::new(),
1728 }
1729 }
1730}
1731
1732struct VarReplacer {
1733 def: DeclExpr,
1734 with: Ty,
1735}
1736
1737impl TyMutator for VarReplacer {
1738 fn mutate(&mut self, ty: &Ty, pol: bool) -> Option<Ty> {
1739 if let Ty::Var(var) = ty
1740 && var.def == self.def
1741 {
1742 return Some(self.with.clone());
1743 }
1744
1745 self.mutate_rec(ty, pol)
1746 }
1747}