1use typst::foundations::{Dict, Func, Module, Scope, Type};
2use typst::syntax::FileId;
3
4use super::BoundChecker;
5use crate::{syntax::Decl, ty::prelude::*};
6
7#[derive(Debug, Clone, Copy)]
9pub enum Iface<'a> {
10 Array(&'a Interned<Ty>),
12 Tuple(&'a Interned<Vec<Ty>>),
14 Dict(&'a Interned<RecordTy>),
16 Args {
18 at: &'a Ty,
20 },
21 Content {
23 val: &'a typst::foundations::Element,
25 at: &'a Ty,
27 },
28 TypeType {
30 val: &'a typst::foundations::Type,
32 at: &'a Ty,
34 },
35 Type {
37 val: &'a typst::foundations::Type,
39 at: &'a Ty,
41 },
42 Func {
44 val: &'a typst::foundations::Func,
46 at: &'a Ty,
48 },
49 Value {
51 val: &'a Dict,
53 at: &'a Ty,
55 },
56 Module {
58 val: FileId,
60 at: &'a Ty,
62 },
63 ModuleVal {
65 val: &'a Module,
67 at: &'a Ty,
69 },
70}
71
72impl Iface<'_> {
73 pub fn to_type(self) -> Ty {
75 match self {
76 Iface::Array(ty) => Ty::Array(ty.clone()),
77 Iface::Tuple(tys) => Ty::Tuple(tys.clone()),
78 Iface::Dict(dict) => Ty::Dict(dict.clone()),
79 Iface::Args { at }
80 | Iface::Content { at, .. }
81 | Iface::TypeType { at, .. }
82 | Iface::Type { at, .. }
83 | Iface::Func { at, .. }
84 | Iface::Value { at, .. }
85 | Iface::Module { at, .. }
86 | Iface::ModuleVal { at, .. } => at.clone(),
87 }
88 }
89
90 pub fn select(self, ctx: &mut impl TyCtxMut, key: &StrRef) -> Option<Ty> {
92 crate::log_debug_ct!("iface shape: {self:?}");
93
94 match self {
95 Iface::Array(..) | Iface::Tuple(..) => {
96 select_scope(Some(Type::of::<typst::foundations::Array>().scope()), key)
97 }
98 Iface::Dict(dict) => dict.field_by_name(key).cloned(),
99 Iface::Args { at } => {
100 if BuiltinSig::arguments_method(at, key).is_some() {
101 return None;
102 }
103 select_scope(Some(Type::of::<typst::foundations::Args>().scope()), key)
104 }
105 Iface::Content { val, .. } => select_scope(Some(val.scope()), key),
106 Iface::TypeType { val, .. } | Iface::Type { val, .. } => {
108 select_scope(Some(val.scope()), key)
109 }
110 Iface::Func { val, .. } => select_scope(val.scope(), key),
111 Iface::Value { val, at: _ } => ctx.type_of_dict(val).field_by_name(key).cloned(),
112 Iface::Module { val, at: _ } => ctx.check_module_item(val, key),
113 Iface::ModuleVal { val, at: _ } => ctx.type_of_module(val).field_by_name(key).cloned(),
114 }
115 }
116}
117
118fn select_scope(scope: Option<&Scope>, key: &str) -> Option<Ty> {
120 let scope = scope?;
121 let sub = scope.get(key)?;
122 let sub_span = sub.span();
123 Some(Ty::Value(InsTy::new_at(sub.read().clone(), sub_span)))
124}
125
126pub trait IfaceChecker: TyCtx {
128 fn check(&mut self, iface: Iface, ctx: &mut IfaceCheckContext, pol: bool) -> Option<()>;
130}
131
132impl Ty {
133 pub fn iface_surface(
135 &self,
136 pol: bool,
137 checker: &mut impl IfaceChecker,
139 ) {
140 let context = IfaceCheckContext { args: Vec::new() };
141 let mut worker = IfaceCheckDriver {
142 ctx: context,
143 checker,
144 };
145
146 worker.ty(self, pol);
147 }
148}
149
150pub struct IfaceCheckContext {
152 pub args: Vec<Interned<SigTy>>,
154}
155
156#[derive(BindTyCtx)]
158#[bind(checker)]
159pub struct IfaceCheckDriver<'a> {
160 ctx: IfaceCheckContext,
161 checker: &'a mut dyn IfaceChecker,
162}
163
164impl BoundChecker for IfaceCheckDriver<'_> {
165 fn collect(&mut self, ty: &Ty, pol: bool) {
166 self.ty(ty, pol);
167 }
168}
169
170impl IfaceCheckDriver<'_> {
171 fn array_as_iface(&self) -> bool {
173 true
174 }
175
176 fn dict_as_iface(&self) -> bool {
178 true
179 }
180
181 fn value_as_iface(&self) -> bool {
183 true
184 }
185
186 fn ty(&mut self, at: &Ty, pol: bool) {
188 crate::log_debug_ct!("check iface ty: {at:?}");
189
190 match at {
191 Ty::Builtin(BuiltinTy::Stroke) if self.dict_as_iface() => {
192 self.checker
193 .check(Iface::Dict(&FLOW_STROKE_DICT), &mut self.ctx, pol);
194 }
195 Ty::Builtin(BuiltinTy::Margin) if self.dict_as_iface() => {
196 self.checker
197 .check(Iface::Dict(&FLOW_MARGIN_DICT), &mut self.ctx, pol);
198 }
199 Ty::Builtin(BuiltinTy::Inset) if self.dict_as_iface() => {
200 self.checker
201 .check(Iface::Dict(&FLOW_INSET_DICT), &mut self.ctx, pol);
202 }
203 Ty::Builtin(BuiltinTy::Outset) if self.dict_as_iface() => {
204 self.checker
205 .check(Iface::Dict(&FLOW_OUTSET_DICT), &mut self.ctx, pol);
206 }
207 Ty::Builtin(BuiltinTy::Radius) if self.dict_as_iface() => {
208 self.checker
209 .check(Iface::Dict(&FLOW_RADIUS_DICT), &mut self.ctx, pol);
210 }
211 Ty::Builtin(BuiltinTy::TextFont) if self.dict_as_iface() => {
212 self.checker
213 .check(Iface::Dict(&FLOW_TEXT_FONT_DICT), &mut self.ctx, pol);
214 }
215 Ty::Builtin(BuiltinTy::Args) => {
216 self.checker.check(Iface::Args { at }, &mut self.ctx, pol);
217 }
218 Ty::Value(ins_ty) => {
219 if self.value_as_iface() {
221 match &ins_ty.val {
222 Value::Module(val) => {
223 self.checker
224 .check(Iface::ModuleVal { val, at }, &mut self.ctx, pol);
225 }
226 Value::Dict(dict) => {
227 self.checker
228 .check(Iface::Value { val: dict, at }, &mut self.ctx, pol);
229 }
230 Value::Type(ty) => {
231 self.checker
232 .check(Iface::TypeType { val: ty, at }, &mut self.ctx, pol);
233 }
234 Value::Func(func) => {
235 self.checker
236 .check(Iface::Func { val: func, at }, &mut self.ctx, pol);
237 }
238 Value::Args(..) => {
239 self.checker.check(Iface::Args { at }, &mut self.ctx, pol);
240 }
241 Value::None
242 | Value::Auto
243 | Value::Bool(_)
244 | Value::Int(_)
245 | Value::Float(_)
246 | Value::Length(..)
247 | Value::Angle(..)
248 | Value::Ratio(..)
249 | Value::Relative(..)
250 | Value::Fraction(..)
251 | Value::Color(..)
252 | Value::Gradient(..)
253 | Value::Tiling(..)
254 | Value::Symbol(..)
255 | Value::Version(..)
256 | Value::Str(..)
257 | Value::Bytes(..)
258 | Value::Label(..)
259 | Value::Datetime(..)
260 | Value::Decimal(..)
261 | Value::Duration(..)
262 | Value::Content(..)
263 | Value::Styles(..)
264 | Value::Array(..)
265 | Value::Dyn(..) => {
266 self.checker.check(
267 Iface::Type {
268 val: &ins_ty.val.ty(),
269 at,
270 },
271 &mut self.ctx,
272 pol,
273 );
274 }
275 }
276 }
277 }
278 Ty::Builtin(BuiltinTy::Content(Some(elem))) if self.value_as_iface() => {
280 self.checker
281 .check(Iface::Content { val: elem, at }, &mut self.ctx, pol);
282 }
283 Ty::Builtin(BuiltinTy::Content(..)) if self.value_as_iface() => {
284 let ty = Type::of::<typst::foundations::Content>();
285 self.checker
286 .check(Iface::Type { val: &ty, at }, &mut self.ctx, pol);
287 }
288 Ty::Builtin(BuiltinTy::Type(ty)) if self.value_as_iface() => {
289 self.checker
291 .check(Iface::Type { val: ty, at }, &mut self.ctx, pol);
292 }
293 Ty::Builtin(BuiltinTy::Element(elem)) if self.value_as_iface() => {
294 self.checker.check(
295 Iface::Func {
296 val: &Func::from(*elem),
297 at,
298 },
299 &mut self.ctx,
300 pol,
301 );
302 }
303 Ty::Builtin(BuiltinTy::Module(module)) => {
304 if let Decl::Module(m) = module.as_ref() {
305 self.checker
306 .check(Iface::Module { val: m.fid, at }, &mut self.ctx, pol);
307 }
308 }
309 Ty::Dict(sig) if self.dict_as_iface() => {
319 self.checker.check(Iface::Dict(sig), &mut self.ctx, pol);
321 }
322 Ty::Tuple(sig) if self.array_as_iface() => {
323 self.checker.check(Iface::Tuple(sig), &mut self.ctx, pol);
325 }
326 Ty::Array(sig) if self.array_as_iface() => {
327 self.checker.check(Iface::Array(sig), &mut self.ctx, pol);
329 }
330 Ty::Args(..) => {
331 self.checker.check(Iface::Args { at }, &mut self.ctx, pol);
332 }
333 Ty::Dict(..) => {
334 self.checker.check(
335 Iface::Type {
336 val: &Type::of::<typst::foundations::Dict>(),
337 at,
338 },
339 &mut self.ctx,
340 pol,
341 );
342 }
343 Ty::Tuple(..) | Ty::Array(..) => {
344 self.checker.check(
345 Iface::Type {
346 val: &Type::of::<typst::foundations::Array>(),
347 at,
348 },
349 &mut self.ctx,
350 pol,
351 );
352 }
353 Ty::Var(..) => at.bounds(pol, self),
354 _ if at.has_bounds() => at.bounds(pol, self),
355 _ => {}
356 }
357 }
363}