1use typst::foundations::{Func, Value};
2
3use super::BoundChecker;
4use crate::ty::prelude::*;
5
6#[derive(Debug, Clone, Copy)]
8pub enum Sig<'a> {
9 Builtin(BuiltinSig<'a>),
11 Type(&'a Interned<SigTy>),
13 TypeCons {
15 val: &'a typst::foundations::Type,
17 at: &'a Ty,
19 },
20 ArrayCons(&'a TyRef),
22 TupleCons(&'a Interned<Vec<Ty>>),
24 DictCons(&'a Interned<RecordTy>),
26 Value {
28 val: &'a Func,
30 at: &'a Ty,
32 },
33 Partialize(&'a Sig<'a>),
35 With {
37 sig: &'a Sig<'a>,
39 withs: &'a Vec<Interned<ArgsTy>>,
41 at: &'a Ty,
43 },
44}
45
46pub struct SigShape<'a> {
48 pub sig: Interned<SigTy>,
50 pub withs: Option<&'a Vec<Interned<SigTy>>>,
52}
53
54impl<'a> Sig<'a> {
55 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum SigSurfaceKind {
102 Call,
104 Array,
106 Dict,
108 ArrayOrDict,
110}
111
112pub trait SigChecker: TyCtx {
114 fn check(&mut self, sig: Sig, args: &mut SigCheckContext, pol: bool) -> Option<()>;
116}
117
118impl Ty {
119 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 pub fn sig_repr(&self, pol: bool, ctx: &mut impl TyCtxMut) -> Option<Interned<SigTy>> {
132 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 &mut SigReprDriver(ctx, &mut primary),
157 );
158
159 primary
160 }
161}
162
163pub struct SigCheckContext {
165 pub sig_kind: SigSurfaceKind,
167 pub args: Vec<Interned<SigTy>>,
169 pub at: TyRef,
171}
172
173#[derive(BindTyCtx)]
175#[bind(checker)]
176pub struct SigCheckDriver<'a> {
177 ctx: SigCheckContext,
178 checker: &'a mut dyn SigChecker,
179}
180
181impl SigCheckDriver<'_> {
182 fn func_as_sig(&self) -> bool {
184 matches!(self.ctx.sig_kind, SigSurfaceKind::Call)
185 }
186
187 fn array_as_sig(&self) -> bool {
189 matches!(
190 self.ctx.sig_kind,
191 SigSurfaceKind::Array | SigSurfaceKind::ArrayOrDict
192 )
193 }
194
195 fn dict_as_sig(&self) -> bool {
197 matches!(
198 self.ctx.sig_kind,
199 SigSurfaceKind::Dict | SigSurfaceKind::ArrayOrDict
200 )
201 }
202
203 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 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 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 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.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 Ty::Unary(_) => {}
287 Ty::Binary(_) => {}
288 Ty::If(_) => {}
289 Ty::Param(param) => {
290 self.ty(¶m.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#[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 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 }
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 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 }
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 }
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 _ => {}
402 }
403 }
404}