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 Content {
18 val: &'a typst::foundations::Element,
20 at: &'a Ty,
22 },
23 TypeType {
25 val: &'a typst::foundations::Type,
27 at: &'a Ty,
29 },
30 Type {
32 val: &'a typst::foundations::Type,
34 at: &'a Ty,
36 },
37 Func {
39 val: &'a typst::foundations::Func,
41 at: &'a Ty,
43 },
44 Value {
46 val: &'a Dict,
48 at: &'a Ty,
50 },
51 Module {
53 val: FileId,
55 at: &'a Ty,
57 },
58 ModuleVal {
60 val: &'a Module,
62 at: &'a Ty,
64 },
65}
66
67impl Iface<'_> {
68 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 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 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
106fn 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
114pub trait IfaceChecker: TyCtx {
116 fn check(&mut self, iface: Iface, ctx: &mut IfaceCheckContext, pol: bool) -> Option<()>;
118}
119
120impl Ty {
121 pub fn iface_surface(
123 &self,
124 pol: bool,
125 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
138pub struct IfaceCheckContext {
140 pub args: Vec<Interned<SigTy>>,
142}
143
144#[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 fn array_as_iface(&self) -> bool {
161 true
162 }
163
164 fn dict_as_iface(&self) -> bool {
166 true
167 }
168
169 fn value_as_iface(&self) -> bool {
171 true
172 }
173
174 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 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 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 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::Dict(sig) if self.dict_as_iface() => {
302 self.checker.check(Iface::Dict(sig), &mut self.ctx, pol);
304 }
305 Ty::Tuple(sig) if self.array_as_iface() => {
306 self.checker.check(Iface::Tuple(sig), &mut self.ctx, pol);
308 }
309 Ty::Array(sig) if self.array_as_iface() => {
310 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 }
343}