tinymist_analysis/ty/
mod.rs

1//! Types and type operations for Typst.
2
3mod apply;
4mod bound;
5mod builtin;
6mod convert;
7mod def;
8mod describe;
9mod iface;
10mod mutate;
11mod prelude;
12mod select;
13mod sig;
14mod simplify;
15mod subst;
16
17pub use apply::*;
18pub use bound::*;
19pub use builtin::*;
20pub use convert::*;
21pub use def::*;
22pub use iface::*;
23pub use mutate::*;
24pub use select::*;
25pub use sig::*;
26
27use typst::foundations::{self, Func, Module, Value};
28use typst::syntax::FileId;
29
30/// A type context.
31pub trait TyCtx {
32    /// Get local binding of a variable.
33    fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty>;
34    /// Get the type of a variable.
35    fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<DynTypeBounds>;
36}
37
38impl TyCtx for () {
39    fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty> {
40        None
41    }
42
43    fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<DynTypeBounds> {
44        None
45    }
46}
47
48/// A mutable type context.
49pub trait TyCtxMut: TyCtx {
50    /// The type of a snapshot of the scope.
51    type Snap;
52
53    /// Start a new scope.
54    #[must_use]
55    fn start_scope(&mut self) -> Self::Snap;
56    /// End the current scope.
57    fn end_scope(&mut self, snap: Self::Snap);
58    /// Execute a function with a new scope.
59    fn with_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
60        let snap = self.start_scope();
61        let res = f(self);
62        self.end_scope(snap);
63        res
64    }
65
66    /// Bind a variable locally.
67    fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty);
68    /// Get the type of a runtime function.
69    fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>>;
70    /// Get the type of a runtime value.
71    fn type_of_value(&mut self, val: &Value) -> Ty;
72    /// Get the type of a runtime dict.
73    fn type_of_dict(&mut self, dict: &foundations::Dict) -> Interned<RecordTy> {
74        let ty = self.type_of_value(&Value::Dict(dict.clone()));
75        let Ty::Dict(ty) = ty else {
76            panic!("expected dict type, found {ty:?}");
77        };
78        ty
79    }
80    /// Get the type of a runtime module.
81    fn type_of_module(&mut self, module: &Module) -> Interned<RecordTy> {
82        let ty = self.type_of_value(&Value::Module(module.clone()));
83        let Ty::Dict(ty) = ty else {
84            panic!("expected dict type, found {ty:?}");
85        };
86        ty
87    }
88    /// Check a module item.
89    fn check_module_item(&mut self, module: FileId, key: &StrRef) -> Option<Ty>;
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use crate::adt::interner::Interned;
96    use crate::syntax::Decl;
97
98    pub fn var_ins(s: &str) -> Ty {
99        Ty::Var(TypeVar::new(s.into(), Decl::lit(s).into()))
100    }
101
102    pub fn str_sig(
103        pos: &[&str],
104        named: &[(&str, &str)],
105        rest: Option<&str>,
106        ret: Option<&str>,
107    ) -> Interned<SigTy> {
108        let pos = pos.iter().map(|s| var_ins(s));
109        let named = named.iter().map(|(n, t)| ((*n).into(), var_ins(t)));
110        let rest = rest.map(var_ins);
111        let ret = ret.map(var_ins);
112        SigTy::new(pos, named, None, rest, ret).into()
113    }
114
115    // args*, (keys: values)*, ...rest -> ret
116    macro_rules! literal_sig {
117        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)? ...$rest:ident -> $ret:ident) => {
118            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], Some(stringify!($rest)), Some(stringify!($ret)))
119        };
120        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)? -> $ret:ident) => {
121            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], None, Some(stringify!($ret)))
122        };
123        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)? ...$rest:ident) => {
124            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], Some(stringify!($rest)), None)
125        };
126        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)?) => {
127            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], None, None)
128        };
129    }
130
131    pub(crate) use literal_sig;
132    pub(crate) use literal_sig as literal_args;
133}