tinymist_query/analysis/
tyck.rs

1//! Type checking on source file
2
3use 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/// Type checking at the source unit level.
36#[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    // Retrieve expression information for the source.
50    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    // A binder remains live while the current flow value can still be the function input.
101    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            // todo: record decl types
221            // let should_record = matches!(root.kind(), SyntaxKind::FuncCall).then(||
222            // root.span());
223            // if let Some(s) = should_record {
224            //     self.info.witness_at_least(s, w.clone());
225            // }
226
227            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        // todo: check types in cycle
253        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        // todo: rest expressions
261        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(&param.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(&param.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            // Rebound inputs keep call inference, but never retain a live caller flow variable.
500            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(&param.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                // strict constraint on upper bound
820                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                // Constrain field access `base.field` by constraining `base` with a record type
845                // that contains the field. This enables propagating expected types back into
846                // dictionary literals, e.g. `(cjk: "")` from `fonts.cjk` used as `text(font:
847                // ...)`.
848                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                    // if !sl.is_detached() {
870                    //     self.info.witness_at_most(*sl, rhs.clone());
871                    // }
872                    // if !sr.is_detached() {
873                    //     self.info.witness_at_least(*sr, lhs.clone());
874                    // }
875                }
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                // empty array is also a constructing dict but we can safely ignore it during
889                // type checking, since no fields are added yet.
890                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                // todo: more information could be extracted from unary constraint structure
951                // e.g. type(l) == type(r)
952                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                // if !lhs.1.is_detached() {
977                //     self.info.witness_at_most(lhs.1, rhs.clone());
978                // }
979            }
980            (lhs, Ty::Value(rhs)) => {
981                crate::log_debug_ct!("constrain value {lhs:?} ⪯ {rhs:?}");
982                // if !rhs.1.is_detached() {
983                //     self.info.witness_at_least(rhs.1, lhs.clone());
984                // }
985            }
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        // `Any` is the identity of an upper-bound intersection and carries no contract fact.
1022        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        // Preserve the input contract before the body-flow variable is overwritten.
1221        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(&param.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            // todo: remove not element of
1318            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        // todo: instantiataion
1326        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        // let mut definite = definite.clone();
1570        // for p in &possibles {
1571        //     definite = definite.join(p);
1572        // }
1573
1574        // crate::log_debug_ct!("possibles: {:?} {:?}", definite, possibles);
1575
1576        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            // todo: check possibles
1679            (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            // todo: mystery flow none
1684            // todo: possible some style (auto)
1685            (Ty::Builtin(ty), Ty::Builtin(BuiltinTy::None)) => self.definite = Ty::Builtin(ty),
1686            (Ty::Builtin(..), _) => self.definite = Ty::undef(),
1687            // todo: value join
1688            (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}