tinymist_analysis/ty/
sig.rs

1use typst::foundations::{Func, Value};
2
3use super::BoundChecker;
4use crate::ty::prelude::*;
5
6/// A signature.
7#[derive(Debug, Clone, Copy)]
8pub enum Sig<'a> {
9    /// A builtin signature.        
10    Builtin(BuiltinSig<'a>),
11    /// A type signature.
12    Type(&'a Interned<SigTy>),
13    /// A type constructor.
14    TypeCons {
15        /// The type.
16        val: &'a typst::foundations::Type,
17        /// The original type.
18        at: &'a Ty,
19    },
20    /// An array constructor.
21    ArrayCons(&'a TyRef),
22    /// A tuple constructor.
23    TupleCons(&'a Interned<Vec<Ty>>),
24    /// A dictionary constructor.
25    DictCons(&'a Interned<RecordTy>),
26    /// A value signature.
27    Value {
28        /// The value.
29        val: &'a Func,
30        /// The original type.
31        at: &'a Ty,
32    },
33    /// A partialize signature.
34    Partialize(&'a Sig<'a>),
35    /// A with signature.
36    With {
37        /// The signature.
38        sig: &'a Sig<'a>,
39        /// The bounds.
40        withs: &'a Vec<Interned<ArgsTy>>,
41        /// The original type.
42        at: &'a Ty,
43    },
44}
45
46/// A shape of a signature.
47pub struct SigShape<'a> {
48    /// The signature.
49    pub sig: Interned<SigTy>,
50    /// The withs.
51    pub withs: Option<&'a Vec<Interned<SigTy>>>,
52}
53
54impl<'a> Sig<'a> {
55    /// Gets the type of the signature.
56    pub fn ty(self) -> Option<Ty> {
57        Some(match self {
58            Sig::Builtin(_) => return None,
59            Sig::Type(t) => Ty::Func(t.clone()),
60            Sig::ArrayCons(t) => Ty::Array(t.clone()),
61            Sig::TupleCons(t) => Ty::Tuple(t.clone()),
62            Sig::DictCons(t) => Ty::Dict(t.clone()),
63            Sig::TypeCons { val, .. } => Ty::Builtin(BuiltinTy::Type(*val)),
64            Sig::Value { at, .. } => at.clone(),
65            Sig::With { at, .. } => at.clone(),
66            Sig::Partialize(..) => return None,
67        })
68    }
69
70    /// Gets the shape of the signature.
71    pub fn shape(self, ctx: &mut impl TyCtxMut) -> Option<SigShape<'a>> {
72        let (sig, _is_partialize) = match self {
73            Sig::Partialize(sig) => (*sig, true),
74            sig => (sig, false),
75        };
76
77        let (cano_sig, withs) = match sig {
78            Sig::With { sig, withs, .. } => (*sig, Some(withs)),
79            sig => (sig, None),
80        };
81
82        let sig = match cano_sig {
83            Sig::Builtin(_) => return None,
84            Sig::ArrayCons(arr) => SigTy::array_cons(arr.as_ref().clone(), false),
85            Sig::TupleCons(tup) => SigTy::tuple_cons(tup.clone(), false),
86            Sig::DictCons(dict) => SigTy::dict_cons(dict, false),
87            Sig::TypeCons { val, .. } => ctx.type_of_func(&val.constructor().ok()?)?,
88            Sig::Value { val, .. } => ctx.type_of_func(val)?,
89            // todo
90            Sig::Partialize(..) => return None,
91            Sig::With { .. } => return None,
92            Sig::Type(ty) => ty.clone(),
93        };
94
95        Some(SigShape { sig, withs })
96    }
97}
98
99/// A kind of signature surface.
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum SigSurfaceKind {
102    /// A call signature.
103    Call,
104    /// An array signature.
105    Array,
106    /// A dictionary signature.
107    Dict,
108    /// An array or dictionary signature.
109    ArrayOrDict,
110}
111
112/// A trait to check a signature.
113pub trait SigChecker: TyCtx {
114    /// Checks the signature.
115    fn check(&mut self, sig: Sig, args: &mut SigCheckContext, pol: bool) -> Option<()>;
116}
117
118impl Ty {
119    /// Iterate over the signatures of the given type.
120    pub fn sig_surface(&self, pol: bool, sig_kind: SigSurfaceKind, checker: &mut impl SigChecker) {
121        let ctx = SigCheckContext {
122            sig_kind,
123            args: Vec::new(),
124            at: TyRef::new(Ty::Any),
125        };
126
127        SigCheckDriver { ctx, checker }.ty(self, pol);
128    }
129
130    /// Get the signature representation of the given type.
131    pub fn sig_repr(&self, pol: bool, ctx: &mut impl TyCtxMut) -> Option<Interned<SigTy>> {
132        // todo: union sig
133        // let mut pos = vec![];
134        // let mut named = HashMap::new();
135        // let mut rest = None;
136        // let mut ret = None;
137
138        let mut primary = None;
139
140        #[derive(BindTyCtx)]
141        #[bind(0)]
142        struct SigReprDriver<'a, C: TyCtxMut>(&'a mut C, &'a mut Option<Interned<SigTy>>);
143
144        impl<C: TyCtxMut> SigChecker for SigReprDriver<'_, C> {
145            fn check(&mut self, sig: Sig, _ctx: &mut SigCheckContext, _pol: bool) -> Option<()> {
146                let sig = sig.shape(self.0)?;
147                *self.1 = Some(sig.sig.clone());
148                Some(())
149            }
150        }
151
152        self.sig_surface(
153            pol,
154            SigSurfaceKind::Call,
155            // todo: bind type context
156            &mut SigReprDriver(ctx, &mut primary),
157        );
158
159        primary
160    }
161}
162
163/// A context to check a signature.
164pub struct SigCheckContext {
165    /// The kind of signature surface.
166    pub sig_kind: SigSurfaceKind,
167    /// The arguments.
168    pub args: Vec<Interned<SigTy>>,
169    /// The type.
170    pub at: TyRef,
171}
172
173/// A driver to check a signature.
174#[derive(BindTyCtx)]
175#[bind(checker)]
176pub struct SigCheckDriver<'a> {
177    ctx: SigCheckContext,
178    checker: &'a mut dyn SigChecker,
179}
180
181impl SigCheckDriver<'_> {
182    /// Determines whether the signature is a function.
183    fn func_as_sig(&self) -> bool {
184        matches!(self.ctx.sig_kind, SigSurfaceKind::Call)
185    }
186
187    /// Determines whether the signature is an array.
188    fn array_as_sig(&self) -> bool {
189        matches!(
190            self.ctx.sig_kind,
191            SigSurfaceKind::Array | SigSurfaceKind::ArrayOrDict
192        )
193    }
194
195    /// Determines whether the signature is a dictionary.
196    fn dict_as_sig(&self) -> bool {
197        matches!(
198            self.ctx.sig_kind,
199            SigSurfaceKind::Dict | SigSurfaceKind::ArrayOrDict
200        )
201    }
202
203    /// Checks the signature of the given type.
204    fn ty(&mut self, at: &Ty, pol: bool) {
205        crate::log_debug_ct!("check sig: {at:?}");
206        match at {
207            Ty::Builtin(BuiltinTy::Stroke) if self.dict_as_sig() => {
208                self.checker
209                    .check(Sig::DictCons(&FLOW_STROKE_DICT), &mut self.ctx, pol);
210            }
211            Ty::Builtin(BuiltinTy::Margin) if self.dict_as_sig() => {
212                self.checker
213                    .check(Sig::DictCons(&FLOW_MARGIN_DICT), &mut self.ctx, pol);
214            }
215            Ty::Builtin(BuiltinTy::Inset) if self.dict_as_sig() => {
216                self.checker
217                    .check(Sig::DictCons(&FLOW_INSET_DICT), &mut self.ctx, pol);
218            }
219            Ty::Builtin(BuiltinTy::Outset) if self.dict_as_sig() => {
220                self.checker
221                    .check(Sig::DictCons(&FLOW_OUTSET_DICT), &mut self.ctx, pol);
222            }
223            Ty::Builtin(BuiltinTy::Radius) if self.dict_as_sig() => {
224                self.checker
225                    .check(Sig::DictCons(&FLOW_RADIUS_DICT), &mut self.ctx, pol);
226            }
227            Ty::Builtin(BuiltinTy::TextFont) if self.dict_as_sig() => {
228                self.checker
229                    .check(Sig::DictCons(&FLOW_TEXT_FONT_DICT), &mut self.ctx, pol);
230            }
231            // todo: deduplicate checking early
232            Ty::Value(ins_ty) => {
233                if self.func_as_sig() {
234                    match &ins_ty.val {
235                        Value::Func(func) => {
236                            self.checker
237                                .check(Sig::Value { val: func, at }, &mut self.ctx, pol);
238                        }
239                        Value::Type(ty) => {
240                            self.checker
241                                .check(Sig::TypeCons { val: ty, at }, &mut self.ctx, pol);
242                        }
243                        _ => {}
244                    }
245                }
246            }
247            Ty::Builtin(BuiltinTy::Type(b_ty)) if self.func_as_sig() => {
248                // todo: distinguish between element and function
249                self.checker
250                    .check(Sig::TypeCons { val: b_ty, at }, &mut self.ctx, pol);
251            }
252            Ty::Builtin(BuiltinTy::Element(elem)) if self.func_as_sig() => {
253                // todo: distinguish between element and function
254                let func = (*elem).into();
255                self.checker
256                    .check(Sig::Value { val: &func, at }, &mut self.ctx, pol);
257            }
258            Ty::Func(sig) if self.func_as_sig() => {
259                self.checker.check(Sig::Type(sig), &mut self.ctx, pol);
260            }
261            Ty::Array(arr) if self.array_as_sig() => {
262                self.checker.check(Sig::ArrayCons(arr), &mut self.ctx, pol);
263            }
264            Ty::Tuple(elems) if self.array_as_sig() => {
265                self.checker
266                    .check(Sig::TupleCons(elems), &mut self.ctx, pol);
267            }
268            Ty::Dict(sig) if self.dict_as_sig() => {
269                // self.check_dict_signature(sig, pol, self.checker);
270                self.checker.check(Sig::DictCons(sig), &mut self.ctx, pol);
271            }
272            Ty::With(sig) if self.func_as_sig() => {
273                self.ctx.args.push(sig.with.clone());
274                self.ty(&sig.sig, pol);
275                self.ctx.args.pop();
276            }
277            Ty::Select(sel) => sel.ty.bounds(
278                pol,
279                &mut MethodDriver {
280                    driver: self,
281                    receiver: sel.ty.as_ref(),
282                    method: &sel.select,
283                },
284            ),
285            // todo: calculate these operators
286            Ty::Unary(_) => {}
287            Ty::Binary(_) => {}
288            Ty::If(_) => {}
289            Ty::Param(param) => {
290                // todo: keep type information
291                self.ty(&param.ty, pol);
292            }
293            _ if at.has_bounds() => at.bounds(pol, self),
294            _ => {}
295        }
296    }
297}
298
299impl BoundChecker for SigCheckDriver<'_> {
300    fn collect(&mut self, ty: &Ty, pol: bool) {
301        crate::log_debug_ct!("sig bounds: {ty:?}");
302        self.ty(ty, pol);
303    }
304}
305
306/// A driver to check a method.
307#[derive(BindTyCtx)]
308#[bind(driver)]
309struct MethodDriver<'a, 'b> {
310    driver: &'a mut SigCheckDriver<'b>,
311    receiver: &'a Ty,
312    method: &'a StrRef,
313}
314
315impl MethodDriver<'_, '_> {
316    fn is_binder(&self) -> bool {
317        matches!(self.method.as_ref(), "with" | "where")
318    }
319
320    fn array_method(&mut self, ty: &Ty, pol: bool) {
321        let method = match self.method.as_ref() {
322            "map" => BuiltinSig::TupleMap(ty),
323            "at" => BuiltinSig::TupleAt(ty),
324            _ => return,
325        };
326        self.driver
327            .checker
328            .check(Sig::Builtin(method), &mut self.driver.ctx, pol);
329    }
330
331    fn arguments_method(&mut self, pol: bool) {
332        let Some(method) = BuiltinSig::arguments_method(self.receiver, self.method.as_ref()) else {
333            return;
334        };
335        self.driver
336            .checker
337            .check(Sig::Builtin(method), &mut self.driver.ctx, pol);
338    }
339}
340
341impl BoundChecker for MethodDriver<'_, '_> {
342    fn collect(&mut self, ty: &Ty, pol: bool) {
343        crate::log_debug_ct!("check method: {ty:?}.{}", self.method.as_ref());
344        match ty {
345            // todo: deduplicate checking early
346            Ty::Value(v) => {
347                match &v.val {
348                    Value::Func(func) => {
349                        if self.is_binder() {
350                            self.driver.checker.check(
351                                Sig::Partialize(&Sig::Value { val: func, at: ty }),
352                                &mut self.driver.ctx,
353                                pol,
354                            );
355                        } else {
356                            // todo: general select operator
357                        }
358                    }
359                    Value::Array(..) => self.array_method(ty, pol),
360                    Value::Args(..) => self.arguments_method(pol),
361                    _ => {}
362                }
363            }
364            Ty::Builtin(BuiltinTy::Element(elem)) => {
365                // todo: distinguish between element and function
366                if self.is_binder() {
367                    let func = (*elem).into();
368                    self.driver.checker.check(
369                        Sig::Partialize(&Sig::Value { val: &func, at: ty }),
370                        &mut self.driver.ctx,
371                        pol,
372                    );
373                } else {
374                    // todo: general select operator
375                }
376            }
377            Ty::Func(sig) => {
378                if self.is_binder() {
379                    self.driver.checker.check(
380                        Sig::Partialize(&Sig::Type(sig)),
381                        &mut self.driver.ctx,
382                        pol,
383                    );
384                } else {
385                    // todo: general select operator
386                }
387            }
388            Ty::With(w) => {
389                self.driver.ctx.args.push(w.with.clone());
390                w.sig.bounds(pol, self);
391                self.driver.ctx.args.pop();
392            }
393            Ty::Tuple(..) => self.array_method(ty, pol),
394            Ty::Array(..) => self.array_method(ty, pol),
395            Ty::Args(..) | Ty::Builtin(BuiltinTy::Args) => self.arguments_method(pol),
396            Ty::If(if_ty) => {
397                self.collect(&if_ty.then, pol);
398                self.collect(&if_ty.else_, pol);
399            }
400            // todo: general select operator
401            _ => {}
402        }
403    }
404}