tinymist_analysis/ty/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Types and type operations for Typst.

#![allow(missing_docs)]

mod apply;
mod bound;
mod builtin;
mod convert;
mod def;
mod describe;
mod iface;
mod mutate;
mod prelude;
mod select;
mod sig;
mod simplify;
mod subst;

pub use apply::*;
pub use bound::*;
pub use builtin::*;
pub use convert::*;
pub use def::*;
pub use iface::*;
pub use mutate::*;
pub use select::*;
pub use sig::*;

use typst::foundations::{self, Func, Module, Value};
use typst::syntax::FileId;

/// A type context.
pub trait TyCtx {
    /// Get local binding of a variable.
    fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty>;
    /// Get the type of a variable.
    fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<DynTypeBounds>;
}

impl TyCtx for () {
    fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty> {
        None
    }

    fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<DynTypeBounds> {
        None
    }
}

/// A mutable type context.
pub trait TyCtxMut: TyCtx {
    /// The type of a snapshot of the scope.
    type Snap;

    /// Start a new scope.
    #[must_use]
    fn start_scope(&mut self) -> Self::Snap;
    /// End the current scope.
    fn end_scope(&mut self, snap: Self::Snap);
    /// Execute a function with a new scope.
    fn with_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
        let snap = self.start_scope();
        let res = f(self);
        self.end_scope(snap);
        res
    }

    /// Bind a variable locally.
    fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty);
    /// Get the type of a runtime function.
    fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>>;
    /// Get the type of a runtime value.
    fn type_of_value(&mut self, val: &Value) -> Ty;
    /// Get the type of a runtime dict.
    fn type_of_dict(&mut self, dict: &foundations::Dict) -> Interned<RecordTy> {
        let ty = self.type_of_value(&Value::Dict(dict.clone()));
        let Ty::Dict(ty) = ty else {
            panic!("expected dict type, found {ty:?}");
        };
        ty
    }
    /// Get the type of a runtime module.
    fn type_of_module(&mut self, module: &Module) -> Interned<RecordTy> {
        let ty = self.type_of_value(&Value::Module(module.clone()));
        let Ty::Dict(ty) = ty else {
            panic!("expected dict type, found {ty:?}");
        };
        ty
    }
    /// Check a module item.
    fn check_module_item(&mut self, module: FileId, key: &StrRef) -> Option<Ty>;
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::adt::interner::Interned;
    use crate::syntax::Decl;

    pub fn var_ins(s: &str) -> Ty {
        Ty::Var(TypeVar::new(s.into(), Decl::lit(s).into()))
    }

    pub fn str_sig(
        pos: &[&str],
        named: &[(&str, &str)],
        rest: Option<&str>,
        ret: Option<&str>,
    ) -> Interned<SigTy> {
        let pos = pos.iter().map(|s| var_ins(s));
        let named = named.iter().map(|(n, t)| ((*n).into(), var_ins(t)));
        let rest = rest.map(var_ins);
        let ret = ret.map(var_ins);
        SigTy::new(pos, named, None, rest, ret).into()
    }

    // args*, (keys: values)*, ...rest -> ret
    macro_rules! literal_sig {
        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)? ...$rest:ident -> $ret:ident) => {
            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], Some(stringify!($rest)), Some(stringify!($ret)))
        };
        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)? -> $ret:ident) => {
            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], None, Some(stringify!($ret)))
        };
        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)? ...$rest:ident) => {
            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], Some(stringify!($rest)), None)
        };
        ($($pos:ident),* $(!$named:ident: $named_ty:ident),* $(,)?) => {
            str_sig(&[$(stringify!($pos)),*], &[$((stringify!($named), stringify!($named_ty))),*], None, None)
        };
    }

    pub(crate) use literal_sig;
    pub(crate) use literal_sig as literal_args;
}