tinymist_analysis/ty/
iface.rs

1use typst::foundations::{Dict, Func, Module, Scope, Type};
2use typst::syntax::FileId;
3
4use super::BoundChecker;
5use crate::{syntax::Decl, ty::prelude::*};
6
7/// A type that represents the interface of a type.
8#[derive(Debug, Clone, Copy)]
9pub enum Iface<'a> {
10    /// An array type.
11    Array(&'a Interned<Ty>),
12    /// A tuple type.
13    Tuple(&'a Interned<Vec<Ty>>),
14    /// A dictionary type.
15    Dict(&'a Interned<RecordTy>),
16    /// A content type.
17    Content {
18        /// The element type.
19        val: &'a typst::foundations::Element,
20        /// The original type.
21        at: &'a Ty,
22    },
23    /// A type type.
24    TypeType {
25        /// The type type.
26        val: &'a typst::foundations::Type,
27        /// The original type.
28        at: &'a Ty,
29    },
30    /// A type.
31    Type {
32        /// The type.
33        val: &'a typst::foundations::Type,
34        /// The original type.
35        at: &'a Ty,
36    },
37    /// A function type.
38    Func {
39        /// The function.
40        val: &'a typst::foundations::Func,
41        /// The original type.
42        at: &'a Ty,
43    },
44    /// A value type.
45    Value {
46        /// The value.
47        val: &'a Dict,
48        /// The original type.
49        at: &'a Ty,
50    },
51    /// A module type.
52    Module {
53        /// The module.
54        val: FileId,
55        /// The original type.
56        at: &'a Ty,
57    },
58    /// A module value type.
59    ModuleVal {
60        /// The module value.
61        val: &'a Module,
62        /// The original type.
63        at: &'a Ty,
64    },
65}
66
67impl Iface<'_> {
68    /// Converts the interface to a type.
69    pub fn to_type(self) -> Ty {
70        match self {
71            Iface::Array(ty) => Ty::Array(ty.clone()),
72            Iface::Tuple(tys) => Ty::Tuple(tys.clone()),
73            Iface::Dict(dict) => Ty::Dict(dict.clone()),
74            Iface::Content { at, .. }
75            | Iface::TypeType { at, .. }
76            | Iface::Type { at, .. }
77            | Iface::Func { at, .. }
78            | Iface::Value { at, .. }
79            | Iface::Module { at, .. }
80            | Iface::ModuleVal { at, .. } => at.clone(),
81        }
82    }
83
84    /// Selects the given key from the interface.
85    pub fn select(self, ctx: &mut impl TyCtxMut, key: &StrRef) -> Option<Ty> {
86        crate::log_debug_ct!("iface shape: {self:?}");
87
88        match self {
89            Iface::Array(..) | Iface::Tuple(..) => {
90                select_scope(Some(Type::of::<typst::foundations::Array>().scope()), key)
91            }
92            Iface::Dict(dict) => dict.field_by_name(key).cloned(),
93            Iface::Content { val, .. } => select_scope(Some(val.scope()), key),
94            // todo: distinguish TypeType and Type
95            Iface::TypeType { val, .. } | Iface::Type { val, .. } => {
96                select_scope(Some(val.scope()), key)
97            }
98            Iface::Func { val, .. } => select_scope(val.scope(), key),
99            Iface::Value { val, at: _ } => ctx.type_of_dict(val).field_by_name(key).cloned(),
100            Iface::Module { val, at: _ } => ctx.check_module_item(val, key),
101            Iface::ModuleVal { val, at: _ } => ctx.type_of_module(val).field_by_name(key).cloned(),
102        }
103    }
104}
105
106/// Selects the given key from the given scope.
107fn select_scope(scope: Option<&Scope>, key: &str) -> Option<Ty> {
108    let scope = scope?;
109    let sub = scope.get(key)?;
110    let sub_span = sub.span();
111    Some(Ty::Value(InsTy::new_at(sub.read().clone(), sub_span)))
112}
113
114/// A trait to check the interface of a type.
115pub trait IfaceChecker: TyCtx {
116    /// Checks the interface of the given type.
117    fn check(&mut self, iface: Iface, ctx: &mut IfaceCheckContext, pol: bool) -> Option<()>;
118}
119
120impl Ty {
121    /// Iterates over the signatures of the given type.
122    pub fn iface_surface(
123        &self,
124        pol: bool,
125        // iface_kind: IfaceSurfaceKind,
126        checker: &mut impl IfaceChecker,
127    ) {
128        let context = IfaceCheckContext { args: Vec::new() };
129        let mut worker = IfaceCheckDriver {
130            ctx: context,
131            checker,
132        };
133
134        worker.ty(self, pol);
135    }
136}
137
138/// A context to check the interface of a type.
139pub struct IfaceCheckContext {
140    /// The arguments of the function.
141    pub args: Vec<Interned<SigTy>>,
142}
143
144/// A driver to check the interface of a type.
145#[derive(BindTyCtx)]
146#[bind(checker)]
147pub struct IfaceCheckDriver<'a> {
148    ctx: IfaceCheckContext,
149    checker: &'a mut dyn IfaceChecker,
150}
151
152impl BoundChecker for IfaceCheckDriver<'_> {
153    fn collect(&mut self, ty: &Ty, pol: bool) {
154        self.ty(ty, pol);
155    }
156}
157
158impl IfaceCheckDriver<'_> {
159    /// Determines whether to check the array as an interface.
160    fn array_as_iface(&self) -> bool {
161        true
162    }
163
164    /// Determines whether to check the dictionary as an interface.
165    fn dict_as_iface(&self) -> bool {
166        true
167    }
168
169    /// Determines whether to check the value as an interface.
170    fn value_as_iface(&self) -> bool {
171        true
172    }
173
174    /// Checks the interface of the given type.
175    fn ty(&mut self, at: &Ty, pol: bool) {
176        crate::log_debug_ct!("check iface ty: {at:?}");
177
178        match at {
179            Ty::Builtin(BuiltinTy::Stroke) if self.dict_as_iface() => {
180                self.checker
181                    .check(Iface::Dict(&FLOW_STROKE_DICT), &mut self.ctx, pol);
182            }
183            Ty::Builtin(BuiltinTy::Margin) if self.dict_as_iface() => {
184                self.checker
185                    .check(Iface::Dict(&FLOW_MARGIN_DICT), &mut self.ctx, pol);
186            }
187            Ty::Builtin(BuiltinTy::Inset) if self.dict_as_iface() => {
188                self.checker
189                    .check(Iface::Dict(&FLOW_INSET_DICT), &mut self.ctx, pol);
190            }
191            Ty::Builtin(BuiltinTy::Outset) if self.dict_as_iface() => {
192                self.checker
193                    .check(Iface::Dict(&FLOW_OUTSET_DICT), &mut self.ctx, pol);
194            }
195            Ty::Builtin(BuiltinTy::Radius) if self.dict_as_iface() => {
196                self.checker
197                    .check(Iface::Dict(&FLOW_RADIUS_DICT), &mut self.ctx, pol);
198            }
199            Ty::Builtin(BuiltinTy::TextFont) if self.dict_as_iface() => {
200                self.checker
201                    .check(Iface::Dict(&FLOW_TEXT_FONT_DICT), &mut self.ctx, pol);
202            }
203            Ty::Value(ins_ty) => {
204                // todo: deduplicate checking early
205                if self.value_as_iface() {
206                    match &ins_ty.val {
207                        Value::Module(val) => {
208                            self.checker
209                                .check(Iface::ModuleVal { val, at }, &mut self.ctx, pol);
210                        }
211                        Value::Dict(dict) => {
212                            self.checker
213                                .check(Iface::Value { val: dict, at }, &mut self.ctx, pol);
214                        }
215                        Value::Type(ty) => {
216                            self.checker
217                                .check(Iface::TypeType { val: ty, at }, &mut self.ctx, pol);
218                        }
219                        Value::Func(func) => {
220                            self.checker
221                                .check(Iface::Func { val: func, at }, &mut self.ctx, pol);
222                        }
223                        Value::None
224                        | Value::Auto
225                        | Value::Bool(_)
226                        | Value::Int(_)
227                        | Value::Float(_)
228                        | Value::Length(..)
229                        | Value::Angle(..)
230                        | Value::Ratio(..)
231                        | Value::Relative(..)
232                        | Value::Fraction(..)
233                        | Value::Color(..)
234                        | Value::Gradient(..)
235                        | Value::Tiling(..)
236                        | Value::Symbol(..)
237                        | Value::Version(..)
238                        | Value::Str(..)
239                        | Value::Bytes(..)
240                        | Value::Label(..)
241                        | Value::Datetime(..)
242                        | Value::Decimal(..)
243                        | Value::Duration(..)
244                        | Value::Content(..)
245                        | Value::Styles(..)
246                        | Value::Array(..)
247                        | Value::Args(..)
248                        | Value::Dyn(..) => {
249                            self.checker.check(
250                                Iface::Type {
251                                    val: &ins_ty.val.ty(),
252                                    at,
253                                },
254                                &mut self.ctx,
255                                pol,
256                            );
257                        }
258                    }
259                }
260            }
261            // todo: more builtin types to check
262            Ty::Builtin(BuiltinTy::Content(Some(elem))) if self.value_as_iface() => {
263                self.checker
264                    .check(Iface::Content { val: elem, at }, &mut self.ctx, pol);
265            }
266            Ty::Builtin(BuiltinTy::Content(..)) if self.value_as_iface() => {
267                let ty = Type::of::<typst::foundations::Content>();
268                self.checker
269                    .check(Iface::Type { val: &ty, at }, &mut self.ctx, pol);
270            }
271            Ty::Builtin(BuiltinTy::Type(ty)) if self.value_as_iface() => {
272                // todo: distinguish between element and function
273                self.checker
274                    .check(Iface::Type { val: ty, at }, &mut self.ctx, pol);
275            }
276            Ty::Builtin(BuiltinTy::Element(elem)) if self.value_as_iface() => {
277                self.checker.check(
278                    Iface::Func {
279                        val: &Func::from(*elem),
280                        at,
281                    },
282                    &mut self.ctx,
283                    pol,
284                );
285            }
286            Ty::Builtin(BuiltinTy::Module(module)) => {
287                if let Decl::Module(m) = module.as_ref() {
288                    self.checker
289                        .check(Iface::Module { val: m.fid, at }, &mut self.ctx, pol);
290                }
291            }
292            // Ty::Func(..) if self.value_as_iface() => {
293            //     self.checker.check(Iface::Type(sig), &mut self.ctx, pol);
294            // }
295            // Ty::Array(sig) if self.array_as_sig() => {
296            //     // let sig = FlowSignature::array_cons(*sig.clone(), true);
297            //     self.checker.check(Iface::ArrayCons(sig), &mut self.ctx, pol);
298            // }
299            // // todo: tuple
300            // Ty::Tuple(_) => {}
301            Ty::Dict(sig) if self.dict_as_iface() => {
302                // self.check_dict_signature(sig, pol, self.checker);
303                self.checker.check(Iface::Dict(sig), &mut self.ctx, pol);
304            }
305            Ty::Tuple(sig) if self.array_as_iface() => {
306                // self.check_dict_signature(sig, pol, self.checker);
307                self.checker.check(Iface::Tuple(sig), &mut self.ctx, pol);
308            }
309            Ty::Array(sig) if self.array_as_iface() => {
310                // self.check_dict_signature(sig, pol, self.checker);
311                self.checker.check(Iface::Array(sig), &mut self.ctx, pol);
312            }
313            Ty::Dict(..) => {
314                self.checker.check(
315                    Iface::Type {
316                        val: &Type::of::<typst::foundations::Dict>(),
317                        at,
318                    },
319                    &mut self.ctx,
320                    pol,
321                );
322            }
323            Ty::Tuple(..) | Ty::Array(..) => {
324                self.checker.check(
325                    Iface::Type {
326                        val: &Type::of::<typst::foundations::Array>(),
327                        at,
328                    },
329                    &mut self.ctx,
330                    pol,
331                );
332            }
333            Ty::Var(..) => at.bounds(pol, self),
334            _ if at.has_bounds() => at.bounds(pol, self),
335            _ => {}
336        }
337        // Ty::Select(sel) => sel.ty.bounds(pol, &mut MethodDriver(self,
338        // &sel.select)), // todo: calculate these operators
339        // Ty::Unary(_) => {}
340        // Ty::Binary(_) => {}
341        // Ty::If(_) => {}
342    }
343}