tinymist_analysis/ty/
subst.rs

1use super::{Sig, SigShape, TyMutator};
2use crate::ty::prelude::*;
3
4impl Sig<'_> {
5    /// Calls the signature with the given arguments.
6    pub fn call(&self, args: &Interned<ArgsTy>, pol: bool, ctx: &mut impl TyCtxMut) -> Option<Ty> {
7        crate::log_debug_ct!("call {self:?} {args:?} {pol:?}");
8        ctx.with_scope(|ctx| {
9            let body = self.check_bind(args, ctx)?;
10
11            // Substitute the bound variables in the body or just body
12            let mut checker = SubstituteChecker { ctx };
13            Some(checker.ty(&body, pol).unwrap_or(body))
14        })
15    }
16
17    /// Checks the binding of the signature.
18    pub fn check_bind(&self, args: &Interned<ArgsTy>, ctx: &mut impl TyCtxMut) -> Option<Ty> {
19        let SigShape { sig, withs } = self.shape(ctx)?;
20
21        // todo: check if the signature has free variables
22        // let has_free_vars = sig.has_free_variables;
23
24        for (arg_recv, arg_ins) in sig.matches(args, withs) {
25            if let Ty::Var(arg_recv) = arg_recv {
26                crate::log_debug_ct!("bind {arg_recv:?} {arg_ins:?}");
27                ctx.bind_local(arg_recv, arg_ins.clone());
28            }
29        }
30
31        sig.body.clone()
32    }
33}
34
35/// A checker to substitute the bound variables.
36struct SubstituteChecker<'a, T: TyCtxMut> {
37    ctx: &'a mut T,
38}
39
40impl<T: TyCtxMut> SubstituteChecker<'_, T> {
41    /// Substitutes the bound variables in the given type.
42    fn ty(&mut self, body: &Ty, pol: bool) -> Option<Ty> {
43        body.mutate(pol, self)
44    }
45}
46
47impl<T: TyCtxMut> TyMutator for SubstituteChecker<'_, T> {
48    fn mutate(&mut self, ty: &Ty, pol: bool) -> Option<Ty> {
49        // todo: extrude the type into a polarized type
50        let _ = pol;
51
52        if let Ty::Var(v) = ty {
53            self.ctx.local_bind_of(v)
54        } else {
55            self.mutate_rec(ty, pol)
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use insta::{assert_debug_snapshot, assert_snapshot};
63    use tinymist_derive::BindTyCtx;
64
65    use super::{DynTypeBounds, Interned, Ty, TyCtx, TypeInfo, TypeVar};
66    use crate::ty::ApplyChecker;
67    use crate::ty::tests::*;
68    #[test]
69    fn test_ty() {
70        use super::*;
71        let ty = Ty::Builtin(BuiltinTy::Clause);
72        let ty_ref = TyRef::new(ty.clone());
73        assert_debug_snapshot!(ty_ref, @"Clause");
74    }
75
76    #[derive(Default, BindTyCtx)]
77    #[bind(0)]
78    struct CallCollector(TypeInfo, Vec<Ty>);
79
80    impl ApplyChecker for CallCollector {
81        fn apply(
82            &mut self,
83            sig: super::Sig,
84            arguments: &crate::adt::interner::Interned<super::ArgsTy>,
85            pol: bool,
86        ) {
87            let ty = sig.call(arguments, pol, &mut self.0);
88            if let Some(ty) = ty {
89                self.1.push(ty);
90            }
91        }
92    }
93
94    #[test]
95    fn test_sig_call() {
96        use super::*;
97
98        fn call(sig: Interned<SigTy>, args: Interned<SigTy>) -> String {
99            let sig_ty = Ty::Func(sig);
100            let mut collector = CallCollector::default();
101            sig_ty.call(&args, false, &mut collector);
102
103            collector.1.iter().fold(String::new(), |mut acc, ty| {
104                if !acc.is_empty() {
105                    acc.push_str(", ");
106                }
107
108                acc.push_str(&format!("{ty:?}"));
109                acc
110            })
111        }
112
113        assert_snapshot!(call(literal_sig!(p1 -> p1), literal_args!(q1)), @"@q1");
114        assert_snapshot!(call(literal_sig!(!u1: w1 -> w1), literal_args!(!u1: w2)), @"@w2");
115    }
116}