1use core::fmt;
6use std::{
7 hash::{Hash, Hasher},
8 sync::{Arc, OnceLock},
9};
10
11use ecow::EcoString;
12use parking_lot::{Mutex, RwLock};
13use rustc_hash::{FxHashMap, FxHashSet};
14use serde::{Deserialize, Serialize};
15use typst::{
16 foundations::{Content, Element, ParamInfo, Type, Value},
17 syntax::{FileId, Span, SyntaxKind, SyntaxNode, ast},
18};
19
20use super::{BoundPred, BuiltinTy, PackageId};
21use crate::{
22 adt::{interner::impl_internable, snapshot_map},
23 docs::UntypedDefDocs,
24 syntax::{DeclExpr, UnaryOp},
25};
26
27pub(crate) use super::{TyCtx, TyCtxMut};
28pub(crate) use crate::adt::interner::Interned;
29pub use tinymist_derive::BindTyCtx;
30
31pub(crate) type TyRef = Interned<Ty>;
33pub(crate) type StrRef = Interned<str>;
35
36#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
38pub enum Ty {
39 Any,
43 Boolean(Option<bool>),
46 Builtin(BuiltinTy),
48 Value(Interned<InsTy>),
50 Param(Interned<ParamTy>),
52
53 Union(Interned<Vec<Ty>>),
57 Let(Interned<TypeBounds>),
60 Var(Interned<TypeVar>),
62
63 Dict(Interned<RecordTy>),
66 Array(TyRef),
68 Tuple(Interned<Vec<Ty>>),
71 Func(Interned<SigTy>),
73 Args(Interned<ArgsTy>),
75 Pattern(Interned<PatternTy>),
77
78 With(Interned<SigWithTy>),
81 Select(Interned<SelectTy>),
83 Unary(Interned<TypeUnary>),
85 Binary(Interned<TypeBinary>),
87 If(Interned<IfTy>),
89}
90
91impl fmt::Debug for Ty {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 Ty::Any => f.write_str("Any"),
95 Ty::Builtin(ty) => write!(f, "{ty:?}"),
96 Ty::Args(args) => write!(f, "&({args:?})"),
97 Ty::Func(func) => write!(f, "{func:?}"),
98 Ty::Pattern(pat) => write!(f, "{pat:?}"),
99 Ty::Dict(record) => write!(f, "{record:?}"),
100 Ty::Array(arr) => write!(f, "Array<{arr:?}>"),
101 Ty::Tuple(elems) => {
102 f.write_str("(")?;
103 for t in elems.iter() {
104 write!(f, "{t:?}, ")?;
105 }
106 f.write_str(")")
107 }
108 Ty::With(with) => write!(f, "({:?}).with(..{:?})", with.sig, with.with),
109 Ty::Select(sel) => write!(f, "{sel:?}"),
110 Ty::Union(types) => {
111 f.write_str("(")?;
112 if let Some((first, u)) = types.split_first() {
113 write!(f, "{first:?}")?;
114 for u in u {
115 write!(f, " | {u:?}")?;
116 }
117 }
118 f.write_str(")")
119 }
120 Ty::Let(bounds) => write!(f, "({bounds:?})"),
121 Ty::Param(param) => write!(f, "{:?}: {:?}", param.name, param.ty),
122 Ty::Var(var) => var.fmt(f),
123 Ty::Unary(unary) => write!(f, "{unary:?}"),
124 Ty::Binary(binary) => write!(f, "{binary:?}"),
125 Ty::If(if_expr) => write!(f, "{if_expr:?}"),
126 Ty::Value(ins_ty) => write!(f, "{:?}", ins_ty.val),
127 Ty::Boolean(truthiness) => {
128 if let Some(truthiness) = truthiness {
129 write!(f, "{truthiness}")
130 } else {
131 f.write_str("Boolean")
132 }
133 }
134 }
135 }
136}
137
138impl Ty {
139 pub fn is_dict(&self) -> bool {
141 matches!(self, Ty::Dict(..))
142 }
143
144 pub fn union(lhs: Option<Ty>, rhs: Option<Ty>) -> Option<Ty> {
146 Some(match (lhs, rhs) {
147 (Some(lhs), Some(rhs)) => Ty::from_types([lhs, rhs].into_iter()),
148 (Some(ty), None) | (None, Some(ty)) => ty,
149 (None, None) => return None,
150 })
151 }
152
153 pub fn from_types(iter: impl ExactSizeIterator<Item = Ty>) -> Self {
155 if iter.len() == 0 {
156 Ty::Any
157 } else if iter.len() == 1 {
158 let mut iter = iter;
159 iter.next().unwrap()
160 } else {
161 Self::iter_union(iter)
162 }
163 }
164
165 pub fn iter_union(iter: impl IntoIterator<Item = Ty>) -> Self {
167 let mut v: Vec<Ty> = iter.into_iter().collect();
168 v.sort();
169 Ty::Union(Interned::new(v))
170 }
171
172 pub const fn undef() -> Self {
175 Ty::Builtin(BuiltinTy::Undef)
176 }
177
178 pub fn name(&self) -> Interned<str> {
180 match self {
181 Ty::Var(v) => v.name.clone(),
182 Ty::Builtin(BuiltinTy::Module(m)) => m.name().clone(),
183 ty => ty
184 .value()
185 .map(|_| Interned::new_str(&self.name()))
186 .unwrap_or_default(),
187 }
188 }
189
190 pub fn span(&self) -> Span {
192 fn seq(u: &[Ty]) -> Option<Span> {
193 u.iter().find_map(|ty| {
194 let sub = ty.span();
195 if sub.is_detached() {
196 return None;
197 }
198 Some(sub)
199 })
200 }
201
202 match self {
203 Ty::Var(v) => v.def.span(),
204 Ty::Let(u) => seq(&u.ubs)
205 .or_else(|| seq(&u.lbs))
206 .unwrap_or_else(Span::detached),
207 Ty::Union(u) => seq(u).unwrap_or_else(Span::detached),
208 _ => Span::detached(),
209 }
210 }
211
212 pub fn value(&self) -> Option<Value> {
214 match self {
215 Ty::Value(v) => Some(v.val.clone()),
216 Ty::Builtin(BuiltinTy::Element(v)) => Some(Value::Func((*v).into())),
217 Ty::Builtin(BuiltinTy::Type(ty)) => Some(Value::Type(*ty)),
218 _ => None,
219 }
220 }
221
222 pub fn element(&self) -> Option<Element> {
224 match self {
225 Ty::Value(ins_ty) => match &ins_ty.val {
226 Value::Func(func) => func.element(),
227 _ => None,
228 },
229 Ty::Builtin(BuiltinTy::Element(v)) => Some(*v),
230 _ => None,
231 }
232 }
233
234 pub fn satisfy<T: TyCtx>(&self, ctx: &T, f: impl FnMut(&Ty, bool)) {
236 self.bounds(true, &mut BoundPred::new(ctx, f));
237 }
238
239 pub fn is_content<T: TyCtx>(&self, ctx: &T) -> bool {
241 let mut res = false;
242 self.satisfy(ctx, |ty: &Ty, _pol| {
243 res = res || {
244 match ty {
245 Ty::Value(v) => is_content_builtin_type(&v.val.ty()),
246 Ty::Builtin(BuiltinTy::Content(..)) => true,
247 Ty::Builtin(BuiltinTy::Type(v)) => is_content_builtin_type(v),
248 _ => false,
249 }
250 }
251 });
252 res
253 }
254
255 pub fn is_str<T: TyCtx>(&self, ctx: &T) -> bool {
257 let mut res = false;
258 self.satisfy(ctx, |ty: &Ty, _pol| {
259 res = res || {
260 match ty {
261 Ty::Value(v) => is_str_builtin_type(&v.val.ty()),
262 Ty::Builtin(BuiltinTy::Type(v)) => is_str_builtin_type(v),
263 _ => false,
264 }
265 }
266 });
267 res
268 }
269
270 pub fn is_type<T: TyCtx>(&self, ctx: &T) -> bool {
272 let mut res = false;
273 self.satisfy(ctx, |ty: &Ty, _pol| {
274 res = res || {
275 match ty {
276 Ty::Value(v) => is_type_builtin_type(&v.val.ty()),
277 Ty::Builtin(BuiltinTy::Type(ty)) => is_type_builtin_type(ty),
278 Ty::Builtin(BuiltinTy::TypeType(..)) => true,
279 _ => false,
280 }
281 }
282 });
283 res
284 }
285}
286
287fn is_content_builtin_type(ty: &Type) -> bool {
289 *ty == Type::of::<Content>() || *ty == Type::of::<typst::foundations::Symbol>()
290}
291
292fn is_str_builtin_type(ty: &Type) -> bool {
294 *ty == Type::of::<typst::foundations::Str>()
295}
296
297fn is_type_builtin_type(ty: &Type) -> bool {
299 *ty == Type::of::<Type>()
300}
301
302pub enum TypeSigParam<'a> {
304 Pos(&'a Ty),
306 Named(&'a StrRef, &'a Ty),
308 Rest(&'a Ty),
310}
311
312impl fmt::Debug for TypeSigParam<'_> {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 match self {
315 TypeSigParam::Pos(ty) => write!(f, "{ty:?}"),
316 TypeSigParam::Named(name, ty) => write!(f, "{name:?}: {ty:?}"),
317 TypeSigParam::Rest(ty) => write!(f, "...: {ty:?}"),
319 }
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
326pub struct TypeSource {
327 pub name_node: SyntaxNode,
329 pub name_repr: OnceLock<StrRef>,
331 pub doc: StrRef,
333}
334
335impl Hash for TypeSource {
336 fn hash<H: Hasher>(&self, state: &mut H) {
337 self.name_node.hash(state);
338 self.doc.hash(state);
339 }
340}
341
342impl TypeSource {
343 pub fn name(&self) -> StrRef {
345 self.name_repr
346 .get_or_init(|| {
347 let name = self.name_node.text();
348 if !name.is_empty() {
349 return name.into();
350 }
351 let name = self.name_node.clone().into_text();
352 name.into()
353 })
354 .clone()
355 }
356}
357
358#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
360pub struct NameBone {
361 pub names: Box<[StrRef]>,
363}
364
365impl NameBone {
366 pub fn empty() -> Interned<Self> {
368 Interned::new(Self {
369 names: Box::new([]),
370 })
371 }
372}
373
374impl NameBone {
375 pub fn find(&self, name: &StrRef) -> Option<usize> {
377 self.names.binary_search_by(|probe| probe.cmp(name)).ok()
378 }
379}
380
381impl NameBone {
382 pub fn intersect_enumerate<'a>(
384 &'a self,
385 rhs: &'a NameBone,
386 ) -> impl Iterator<Item = (usize, usize)> + 'a {
387 let mut lhs_iter = self.names.iter().enumerate();
388 let mut rhs_iter = rhs.names.iter().enumerate();
389
390 let mut lhs = lhs_iter.next();
391 let mut rhs = rhs_iter.next();
392
393 std::iter::from_fn(move || {
394 'name_scanning: loop {
395 if let (Some((idx, lhs_key)), Some((j, rhs_key))) = (lhs, rhs) {
396 match lhs_key.cmp(rhs_key) {
397 std::cmp::Ordering::Less => {
398 lhs = lhs_iter.next();
399 continue 'name_scanning;
400 }
401 std::cmp::Ordering::Greater => {
402 rhs = rhs_iter.next();
403 continue 'name_scanning;
404 }
405 std::cmp::Ordering::Equal => {
406 lhs = lhs_iter.next();
407 rhs = rhs_iter.next();
408 return Some((idx, j));
409 }
410 }
411 }
412 return None;
413 }
414 })
415 }
416}
417
418#[derive(Clone, Default)]
420pub struct DynTypeBounds {
421 pub lbs: rpds::HashTrieSetSync<Ty>,
423 pub ubs: rpds::HashTrieSetSync<Ty>,
425}
426
427impl From<TypeBounds> for DynTypeBounds {
428 fn from(bounds: TypeBounds) -> Self {
429 Self {
430 lbs: bounds.lbs.into_iter().collect(),
431 ubs: bounds.ubs.into_iter().collect(),
432 }
433 }
434}
435
436impl DynTypeBounds {
437 pub fn freeze(&self) -> TypeBounds {
439 let mut lbs: Vec<_> = self.lbs.iter().cloned().collect();
441 lbs.sort();
442 let mut ubs: Vec<_> = self.ubs.iter().cloned().collect();
443 ubs.sort();
444 TypeBounds { lbs, ubs }
445 }
446}
447
448#[derive(Hash, Clone, PartialEq, Eq, Default, PartialOrd, Ord)]
452pub struct TypeBounds {
453 pub lbs: Vec<Ty>,
455 pub ubs: Vec<Ty>,
457}
458
459impl fmt::Debug for TypeBounds {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 if !self.lbs.is_empty() {
464 write!(f, " ⪰ {:?}", self.lbs[0])?;
465 for lb in &self.lbs[1..] {
466 write!(f, " | {lb:?}")?;
467 }
468 }
469 if !self.ubs.is_empty() {
470 write!(f, " ⪯ {:?}", self.ubs[0])?;
471 for ub in &self.ubs[1..] {
472 write!(f, " & {ub:?}")?;
473 }
474 }
475 Ok(())
476 }
477}
478
479pub trait TypeInterface {
482 fn bone(&self) -> &Interned<NameBone>;
485 fn interface(&self) -> impl Iterator<Item = (&StrRef, &Ty)>;
487 fn field_by_bone_offset(&self, idx: usize) -> Option<&Ty>;
489 fn field_by_name(&self, name: &StrRef) -> Option<&Ty> {
491 self.field_by_bone_offset(self.bone().find(name)?)
492 }
493}
494
495pub trait TypeInterfaceExt: TypeInterface {
497 fn common_iface_fields<'a>(
499 &'a self,
500 rhs: &'a Self,
501 ) -> impl Iterator<Item = (&'a StrRef, &'a Ty, &'a Ty)> {
502 let lhs_names = self.bone();
503 let rhs_names = rhs.bone();
504
505 lhs_names
506 .intersect_enumerate(rhs_names)
507 .filter_map(move |(i, j)| {
508 let lhs = self.field_by_bone_offset(i)?;
509 let rhs = rhs.field_by_bone_offset(j)?;
510 Some((&lhs_names.names[i], lhs, rhs))
511 })
512 }
513}
514
515impl<T: TypeInterface> TypeInterfaceExt for T {}
516
517#[derive(Debug, Hash, Clone, PartialEq)]
519pub struct InsTy {
520 pub val: Value,
522 pub syntax: Option<Interned<TypeSource>>,
524}
525
526impl Eq for InsTy {}
529
530impl PartialOrd for Interned<InsTy> {
533 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
534 Some(self.cmp(other))
535 }
536}
537
538impl Ord for Interned<InsTy> {
541 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
542 cmp_value(&self.val, &other.val)
543 }
544}
545
546fn cmp_value(x: &Value, y: &Value) -> std::cmp::Ordering {
547 match x.partial_cmp(y) {
548 Some(order) => return order,
549 None => {
550 let x_dis = val_discriminant(x);
551 let y_dis = val_discriminant(y);
552 if x_dis != y_dis {
553 return x_dis.cmp(&y_dis);
554 }
555 }
556 }
557
558 match (&x, &y) {
559 (Value::Str(x), Value::Str(y)) => x.cmp(y),
560 (Value::Bool(x), Value::Bool(y)) => x.cmp(y),
561 (Value::Int(x), Value::Int(y)) => x.cmp(y),
562 (Value::Decimal(x), Value::Decimal(y)) => x.cmp(y),
563 (Value::Angle(x), Value::Angle(y)) => x.cmp(y),
564 (Value::Ratio(x), Value::Ratio(y)) => x.cmp(y),
565 (Value::Fraction(x), Value::Fraction(y)) => x.cmp(y),
566 (Value::Version(x), Value::Version(y)) => x.cmp(y),
567 (Value::Bytes(x), Value::Bytes(y)) => x.cmp(y),
568 (Value::Duration(x), Value::Duration(y)) => x.cmp(y),
569 (Value::Type(x), Value::Type(y)) => x.cmp(y),
570 (Value::None, Value::None) | (Value::Auto, Value::Auto) => std::cmp::Ordering::Equal,
571 (Value::Array(x), Value::Array(y)) => cmp_by(x.iter(), y.iter(), cmp_value),
572 (Value::Dict(x), Value::Dict(y)) => cmp_by(x.iter(), y.iter(), |(xk, xv), (yk, yv)| {
573 xk.cmp(yk).then_with(|| cmp_value(xv, yv))
574 }),
575 (Value::Label(x), Value::Label(y)) => x.resolve().cmp(&y.resolve()),
576 (Value::Float(x), Value::Float(y)) => x.to_bits().cmp(&y.to_bits()),
577 (Value::Length(x), Value::Length(y)) => x.abs.cmp(&y.abs).then_with(|| x.em.cmp(&y.em)),
578 (Value::Relative(x), Value::Relative(y)) => x.rel.cmp(&y.rel).then_with(|| {
579 x.abs
580 .abs
581 .cmp(&y.abs.abs)
582 .then_with(|| x.abs.em.cmp(&y.abs.em))
583 }),
584 (Value::Func(x), Value::Func(y)) => {
585 if !x.span().is_detached() && !y.span().is_detached() {
586 return x.span().into_raw().cmp(&y.span().into_raw());
587 }
588
589 use typst::foundations::func::Repr;
590 match (x.inner(), y.inner()) {
591 (Repr::Element(x), Repr::Element(y)) => x.cmp(y),
592 _ => ptr_cmp(x, y),
593 }
594 }
595 (Value::Args(x), Value::Args(y)) => {
596 if !x.span.is_detached() && !y.span.is_detached() {
597 return x.span.into_raw().cmp(&y.span.into_raw());
598 }
599
600 ptr_cmp(x, y)
601 }
602 (Value::Module(x), Value::Module(y)) => match (x.file_id(), y.file_id()) {
603 (Some(x), Some(y)) => x.cmp(&y),
604 (Some(..), None) => std::cmp::Ordering::Less,
605 (None, Some(..)) => std::cmp::Ordering::Greater,
606 (None, None) => ptr_cmp(x, y),
607 },
608 (Value::Datetime(x), Value::Datetime(y)) => {
609 x.partial_cmp(y).unwrap_or_else(|| ptr_cmp(x, y))
610 }
611 (Value::Color(x), Value::Color(y)) => ptr_cmp(x, y),
612 (Value::Gradient(x), Value::Gradient(y)) => ptr_cmp(x, y),
613 (Value::Tiling(x), Value::Tiling(y)) => ptr_cmp(x, y),
614 (Value::Symbol(x), Value::Symbol(y)) => ptr_cmp(x, y),
615 (Value::Content(x), Value::Content(y)) => ptr_cmp(x, y),
616 (Value::Styles(x), Value::Styles(y)) => ptr_cmp(x, y),
617 (Value::Dyn(x), Value::Dyn(y)) => ptr_cmp(x, y),
618 _ => ptr_cmp(x, y),
619 }
620}
621
622fn cmp_by<T>(
623 mut x_iter: impl Iterator<Item = T>,
624 mut y_iter: impl Iterator<Item = T>,
625 mut cmp: impl FnMut(T, T) -> std::cmp::Ordering,
626) -> std::cmp::Ordering {
627 use std::cmp::Ordering;
628 loop {
629 match (x_iter.next(), y_iter.next()) {
630 (Some(x_item), Some(y_item)) => match cmp(x_item, y_item) {
631 Ordering::Equal => continue,
632 other => return other,
633 },
634 (Some(_), None) => return Ordering::Greater,
635 (None, Some(_)) => return Ordering::Less,
636 (None, None) => return Ordering::Equal,
637 }
638 }
639}
640
641fn val_discriminant(val: &Value) -> TypstValueEnum {
642 match val {
643 Value::Str(..) => TypstValueEnum::Str,
644 Value::None => TypstValueEnum::None,
645 Value::Auto => TypstValueEnum::Auto,
646 Value::Array(..) => TypstValueEnum::Array,
647 Value::Args(..) => TypstValueEnum::Args,
648 Value::Dict(..) => TypstValueEnum::Dict,
649 Value::Module(..) => TypstValueEnum::Module,
650 Value::Func(..) => TypstValueEnum::Func,
651 Value::Label(..) => TypstValueEnum::Label,
652 Value::Bool(..) => TypstValueEnum::Bool,
653 Value::Int(..) => TypstValueEnum::Int,
654 Value::Float(..) => TypstValueEnum::Float,
655 Value::Decimal(..) => TypstValueEnum::Decimal,
656 Value::Length(..) => TypstValueEnum::Length,
657 Value::Angle(..) => TypstValueEnum::Angle,
658 Value::Ratio(..) => TypstValueEnum::Ratio,
659 Value::Relative(..) => TypstValueEnum::Relative,
660 Value::Fraction(..) => TypstValueEnum::Fraction,
661 Value::Color(..) => TypstValueEnum::Color,
662 Value::Gradient(..) => TypstValueEnum::Gradient,
663 Value::Tiling(..) => TypstValueEnum::Tiling,
664 Value::Symbol(..) => TypstValueEnum::Symbol,
665 Value::Version(..) => TypstValueEnum::Version,
666 Value::Bytes(..) => TypstValueEnum::Bytes,
667 Value::Datetime(..) => TypstValueEnum::Datetime,
668 Value::Duration(..) => TypstValueEnum::Duration,
669 Value::Content(..) => TypstValueEnum::Content,
670 Value::Styles(..) => TypstValueEnum::Styles,
671 Value::Type(..) => TypstValueEnum::Type,
672 Value::Dyn(..) => TypstValueEnum::Dyn,
673 }
674}
675
676fn ptr_cmp<T: PartialEq>(x: &T, y: &T) -> std::cmp::Ordering {
677 if x == y {
678 std::cmp::Ordering::Equal
679 } else {
680 let x = std::ptr::from_ref(x);
681 let y = std::ptr::from_ref(y);
682 x.cmp(&y)
683 }
684}
685
686#[derive(PartialEq, Eq, PartialOrd, Ord)]
687enum TypstValueEnum {
688 Str,
689 None,
690 Auto,
691 Array,
692 Args,
693 Dict,
694 Module,
695 Func,
696 Label,
697 Bool,
698 Int,
699 Float,
700 Decimal,
701 Length,
702 Angle,
703 Ratio,
704 Relative,
705 Fraction,
706 Color,
707 Gradient,
708 Tiling,
709 Symbol,
710 Version,
711 Bytes,
712 Datetime,
713 Duration,
714 Content,
715 Styles,
716 Type,
717 Dyn,
718}
719
720impl InsTy {
721 pub fn new(val: Value) -> Interned<Self> {
723 Self { val, syntax: None }.into()
724 }
725
726 pub fn new_at(val: Value, span: Span) -> Interned<Self> {
728 let mut name = SyntaxNode::leaf(SyntaxKind::Ident, "");
729 name.synthesize(span);
730 Interned::new(Self {
731 val,
732 syntax: Some(Interned::new(TypeSource {
733 name_node: name,
734 name_repr: OnceLock::new(),
735 doc: "".into(),
736 })),
737 })
738 }
739
740 pub fn new_doc(val: Value, doc: impl Into<StrRef>) -> Interned<Self> {
742 Interned::new(Self {
743 val,
744 syntax: Some(Interned::new(TypeSource {
745 name_node: SyntaxNode::default(),
746 name_repr: OnceLock::new(),
747 doc: doc.into(),
748 })),
749 })
750 }
751
752 pub fn span(&self) -> Span {
754 self.syntax
755 .as_ref()
756 .map(|source| source.name_node.span())
757 .or_else(|| {
758 Some(match &self.val {
759 Value::Func(func) => func.span(),
760 Value::Args(args) => args.span,
761 Value::Content(content) => content.span(),
762 _ => return None,
763 })
764 })
765 .unwrap_or_else(Span::detached)
766 }
767}
768
769#[derive(
771 Debug, Clone, Copy, Hash, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord,
772)]
773pub struct ParamAttrs {
774 pub positional: bool,
776 pub named: bool,
781 pub variadic: bool,
783 pub settable: bool,
785}
786
787impl ParamAttrs {
788 pub fn positional() -> ParamAttrs {
790 ParamAttrs {
791 positional: true,
792 named: false,
793 variadic: false,
794 settable: false,
795 }
796 }
797
798 pub fn named() -> ParamAttrs {
800 ParamAttrs {
801 positional: false,
802 named: true,
803 variadic: false,
804 settable: false,
805 }
806 }
807
808 pub fn variadic() -> ParamAttrs {
810 ParamAttrs {
811 positional: true,
812 named: false,
813 variadic: true,
814 settable: false,
815 }
816 }
817}
818
819impl From<&ParamInfo> for ParamAttrs {
820 fn from(param: &ParamInfo) -> Self {
821 ParamAttrs {
822 positional: param.positional,
823 named: param.named,
824 variadic: param.variadic,
825 settable: param.settable,
826 }
827 }
828}
829
830#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
832pub struct ParamTy {
833 pub name: StrRef,
835 pub docs: Option<EcoString>,
837 pub default: Option<EcoString>,
839 pub ty: Ty,
841 pub attrs: ParamAttrs,
843}
844
845impl ParamTy {
846 pub fn new_untyped(name: StrRef, attrs: ParamAttrs) -> Interned<Self> {
848 Self::new(Ty::Any, name, attrs)
849 }
850
851 pub fn new(ty: Ty, name: StrRef, attrs: ParamAttrs) -> Interned<Self> {
853 Interned::new(Self {
854 name,
855 ty,
856 docs: None,
857 default: None,
858 attrs,
859 })
860 }
861}
862
863#[derive(Hash, Clone, PartialEq, Eq)]
865pub struct TypeVar {
866 pub name: StrRef,
868 pub def: DeclExpr,
870}
871
872impl Ord for TypeVar {
873 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
874 self.def.cmp(&other.def)
876 }
877}
878
879impl TypeVar {
880 pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
883 self.def.strict_cmp(&other.def)
884 }
885}
886
887impl PartialOrd for TypeVar {
888 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
889 Some(self.cmp(other))
890 }
891}
892
893impl fmt::Debug for TypeVar {
894 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
895 write!(f, "@{}", self.name)
896 }
897}
898
899impl TypeVar {
900 pub fn new(name: StrRef, def: DeclExpr) -> Interned<Self> {
902 Interned::new(Self { name, def })
903 }
904
905 pub fn name(&self) -> StrRef {
907 self.name.clone()
908 }
909}
910
911#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
913pub struct RecordTy {
914 pub names: Interned<NameBone>,
916 pub types: Interned<Vec<Ty>>,
918}
919
920impl RecordTy {
921 pub fn shape_fields(mut fields: Vec<(StrRef, Ty)>) -> (NameBone, Vec<Ty>) {
923 fields.sort_by(|a, b| a.0.cmp(&b.0));
924 let names = NameBone {
925 names: fields.iter().map(|(name, _)| name.clone()).collect(),
926 };
927 let types = fields.into_iter().map(|(_, ty)| ty).collect::<Vec<_>>();
928
929 (names, types)
930 }
931
932 pub fn new(fields: Vec<(StrRef, Ty)>) -> Interned<Self> {
934 let (names, types) = Self::shape_fields(fields);
935 Interned::new(Self {
936 types: Interned::new(types),
937 names: Interned::new(names),
938 })
939 }
940}
941
942impl TypeInterface for RecordTy {
943 fn bone(&self) -> &Interned<NameBone> {
944 &self.names
945 }
946
947 fn field_by_bone_offset(&self, idx: usize) -> Option<&Ty> {
948 self.types.get(idx)
949 }
950
951 fn interface(&self) -> impl Iterator<Item = (&StrRef, &Ty)> {
952 self.names.names.iter().zip(self.types.iter())
953 }
954}
955
956impl fmt::Debug for RecordTy {
957 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
958 f.write_str("{")?;
959 interpersed(
960 f,
961 self.interface()
962 .map(|(name, ty)| TypeSigParam::Named(name, ty)),
963 )?;
964 f.write_str("}")
965 }
966}
967
968#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
970pub struct SigTy {
971 pub inputs: Interned<Vec<Ty>>,
973 pub body: Option<Ty>,
975 pub names: Interned<NameBone>,
977 pub name_started: u32,
979 pub spread_left: bool,
981 pub spread_right: bool,
983}
984
985impl SigTy {
986 pub fn any() -> Interned<SigTy> {
988 let rest = Ty::Array(Interned::new(Ty::Any));
989 Interned::new(Self {
990 inputs: Interned::new(vec![rest]),
991 body: Some(Ty::Any),
992 names: NameBone::empty(),
993 name_started: 0,
994 spread_left: false,
995 spread_right: true,
996 })
997 }
998
999 #[comemo::memoize]
1001 pub fn array_cons(elem: Ty, anyify: bool) -> Interned<SigTy> {
1002 let rest = Ty::Array(Interned::new(elem.clone()));
1003 let ret = if anyify { Ty::Any } else { rest.clone() };
1004 Interned::new(Self {
1005 inputs: Interned::new(vec![rest]),
1006 body: Some(ret),
1007 names: NameBone::empty(),
1008 name_started: 0,
1009 spread_left: false,
1010 spread_right: true,
1011 })
1012 }
1013
1014 #[comemo::memoize]
1016 pub fn unary(inp: Ty, ret: Ty) -> Interned<SigTy> {
1017 Interned::new(Self {
1018 inputs: Interned::new(vec![inp]),
1019 body: Some(ret),
1020 names: NameBone::empty(),
1021 name_started: 1,
1022 spread_left: false,
1023 spread_right: false,
1024 })
1025 }
1026
1027 #[comemo::memoize]
1029 pub fn tuple_cons(elems: Interned<Vec<Ty>>, anyify: bool) -> Interned<SigTy> {
1030 let ret = if anyify {
1031 Ty::Any
1032 } else {
1033 Ty::Tuple(elems.clone())
1034 };
1035 let name_started = elems.len() as u32;
1036 Interned::new(Self {
1037 inputs: elems,
1038 body: Some(ret),
1039 names: NameBone::empty(),
1040 name_started,
1041 spread_left: false,
1042 spread_right: false,
1043 })
1044 }
1045
1046 #[comemo::memoize]
1048 pub fn dict_cons(named: &Interned<RecordTy>, anyify: bool) -> Interned<SigTy> {
1049 let ret = if anyify {
1050 Ty::Any
1051 } else {
1052 Ty::Dict(named.clone())
1053 };
1054
1055 Interned::new(Self {
1056 inputs: named.types.clone(),
1057 body: Some(ret),
1058 names: named.names.clone(),
1059 name_started: 0,
1060 spread_left: false,
1061 spread_right: false,
1062 })
1063 }
1064
1065 pub fn with_body(mut self, res_ty: Ty) -> Self {
1067 self.body = Some(res_ty);
1068 self
1069 }
1070
1071 pub fn new(
1073 pos: impl ExactSizeIterator<Item = Ty>,
1074 named: impl IntoIterator<Item = (StrRef, Ty)>,
1075 rest_left: Option<Ty>,
1076 rest_right: Option<Ty>,
1077 ret_ty: Option<Ty>,
1078 ) -> Self {
1079 let named = named.into_iter().collect::<Vec<_>>();
1080 let (names, mut named_types) = RecordTy::shape_fields(named);
1081 let spread_left = rest_left.is_some();
1082 let spread_right = rest_right.is_some();
1083
1084 let name_started = if spread_right { 1 } else { 0 } + named_types.len();
1085 let mut types = Vec::with_capacity(
1086 pos.len() + named_types.len() + spread_left as usize + spread_right as usize,
1087 );
1088 types.extend(pos);
1089 types.append(&mut named_types);
1090 types.extend(rest_left);
1091 types.extend(rest_right);
1092
1093 let name_started = (types.len() - name_started) as u32;
1094
1095 Self {
1096 inputs: Interned::new(types),
1097 body: ret_ty,
1098 names: Interned::new(names),
1099 name_started,
1100 spread_left,
1101 spread_right,
1102 }
1103 }
1104}
1105
1106impl Default for SigTy {
1107 fn default() -> Self {
1108 Self {
1109 inputs: Interned::new(Vec::new()),
1110 body: None,
1111 names: NameBone::empty(),
1112 name_started: 0,
1113 spread_left: false,
1114 spread_right: false,
1115 }
1116 }
1117}
1118
1119impl TypeInterface for SigTy {
1120 fn bone(&self) -> &Interned<NameBone> {
1121 &self.names
1122 }
1123
1124 fn interface(&self) -> impl Iterator<Item = (&StrRef, &Ty)> {
1125 let names = self.names.names.iter();
1126 let types = self.inputs.iter().skip(self.name_started as usize);
1127 names.zip(types)
1128 }
1129
1130 fn field_by_bone_offset(&self, offset: usize) -> Option<&Ty> {
1131 self.inputs.get(offset + self.name_started as usize)
1132 }
1133}
1134
1135impl SigTy {
1136 pub fn inputs(&self) -> impl Iterator<Item = &Ty> {
1138 self.inputs.iter()
1139 }
1140
1141 pub fn positional_params(&self) -> impl ExactSizeIterator<Item = &Ty> {
1143 self.inputs.iter().take(self.name_started as usize)
1144 }
1145
1146 pub fn pos(&self, idx: usize) -> Option<&Ty> {
1148 (idx < self.name_started as usize)
1149 .then_some(())
1150 .and_then(|_| self.inputs.get(idx))
1151 }
1152
1153 pub fn pos_or_rest(&self, idx: usize) -> Option<Ty> {
1155 let nth = self.pos(idx).cloned();
1156 nth.or_else(|| {
1157 let rest_idx = || idx.saturating_sub(self.positional_params().len());
1158
1159 let rest_ty = self.rest_param()?;
1160 match rest_ty {
1161 Ty::Array(ty) => Some(ty.as_ref().clone()),
1162 Ty::Tuple(tys) => tys.get(rest_idx()).cloned(),
1163 _ => None,
1164 }
1165 })
1166 }
1167
1168 pub fn named_params(&self) -> impl ExactSizeIterator<Item = (&StrRef, &Ty)> {
1170 let named_names = self.names.names.iter();
1171 let named_types = self.inputs.iter().skip(self.name_started as usize);
1172
1173 named_names.zip(named_types)
1174 }
1175
1176 pub fn named(&self, name: &StrRef) -> Option<&Ty> {
1178 let idx = self.names.find(name)?;
1179 self.inputs.get(idx + self.name_started as usize)
1180 }
1181
1182 pub fn rest_param(&self) -> Option<&Ty> {
1184 if self.spread_right {
1185 self.inputs.last()
1186 } else {
1187 None
1188 }
1189 }
1190
1191 pub fn matches<'a>(
1193 &'a self,
1194 args: &'a SigTy,
1195 with: Option<&'a Vec<Interned<SigTy>>>,
1196 ) -> impl Iterator<Item = (&'a Ty, &'a Ty)> + 'a {
1197 let with_len = with
1198 .map(|w| w.iter().map(|w| w.positional_params().len()).sum::<usize>())
1199 .unwrap_or(0);
1200
1201 let sig_pos = self.positional_params();
1202 let arg_pos = args.positional_params();
1203
1204 let sig_rest = self.rest_param();
1205 let arg_rest = args.rest_param();
1206
1207 let max_len = sig_pos.len().max(with_len + arg_pos.len())
1208 + if sig_rest.is_some() && arg_rest.is_some() {
1209 1
1210 } else {
1211 0
1212 };
1213
1214 let arg_pos = with
1215 .into_iter()
1216 .flat_map(|w| w.iter().rev().map(|w| w.positional_params()))
1217 .flatten()
1218 .chain(arg_pos);
1219
1220 let sig_stream = sig_pos.chain(sig_rest.into_iter().cycle()).take(max_len);
1221 let arg_stream = arg_pos.chain(arg_rest.into_iter().cycle()).take(max_len);
1222
1223 let pos = sig_stream.zip(arg_stream);
1224 let common_ifaces = with
1225 .map(|args_all| args_all.iter().rev())
1226 .into_iter()
1227 .flatten()
1228 .flat_map(|args| self.common_iface_fields(args))
1229 .chain(self.common_iface_fields(args));
1230 let named = common_ifaces.map(|(_, l, r)| (l, r));
1231
1232 pos.chain(named)
1233 }
1234}
1235
1236impl fmt::Debug for SigTy {
1237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1238 f.write_str("(")?;
1239 let pos = self.positional_params().map(TypeSigParam::Pos);
1240 let named = self
1241 .named_params()
1242 .map(|(name, ty)| TypeSigParam::Named(name, ty));
1243 let rest = self.rest_param().map(TypeSigParam::Rest);
1244 interpersed(f, pos.chain(named).chain(rest))?;
1245 f.write_str(") => ")?;
1246 if let Some(ret) = &self.body {
1247 ret.fmt(f)?;
1248 } else {
1249 f.write_str("any")?;
1250 }
1251 Ok(())
1252 }
1253}
1254
1255pub type ArgsTy = SigTy;
1257
1258pub type PatternTy = SigTy;
1260
1261#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1263pub struct SigWithTy {
1264 pub sig: TyRef,
1266 pub with: Interned<ArgsTy>,
1268}
1269
1270impl SigWithTy {
1271 pub fn new(sig: TyRef, with: Interned<ArgsTy>) -> Interned<Self> {
1273 Interned::new(Self { sig, with })
1274 }
1275}
1276
1277impl fmt::Debug for SigWithTy {
1278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1279 write!(f, "{:?}.with({:?})", self.sig, self.with)
1280 }
1281}
1282
1283#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1285pub struct SelectTy {
1286 pub ty: TyRef,
1288 pub select: StrRef,
1290}
1291
1292impl SelectTy {
1293 pub fn new(ty: TyRef, select: StrRef) -> Interned<Self> {
1295 Interned::new(Self { ty, select })
1296 }
1297}
1298
1299impl fmt::Debug for SelectTy {
1300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1301 write!(f, "{:?}.{}", RefDebug(&self.ty), self.select)
1302 }
1303}
1304
1305#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1307pub struct TypeUnary {
1308 pub lhs: Ty,
1310 pub op: UnaryOp,
1312}
1313
1314impl PartialOrd for TypeUnary {
1315 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1316 Some(self.cmp(other))
1317 }
1318}
1319
1320impl Ord for TypeUnary {
1321 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1322 let op_as_int = self.op as u8;
1323 let other_op_as_int = other.op as u8;
1324 op_as_int
1325 .cmp(&other_op_as_int)
1326 .then_with(|| self.lhs.cmp(&other.lhs))
1327 }
1328}
1329
1330impl TypeUnary {
1331 pub fn new(op: UnaryOp, lhs: Ty) -> Interned<Self> {
1333 Interned::new(Self { lhs, op })
1334 }
1335
1336 pub fn operands(&self) -> [&Ty; 1] {
1338 [&self.lhs]
1339 }
1340}
1341
1342pub type BinaryOp = ast::BinOp;
1344
1345#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1347pub struct TypeBinary {
1348 pub operands: (Ty, Ty),
1350 pub op: BinaryOp,
1352}
1353
1354impl PartialOrd for TypeBinary {
1355 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1356 Some(self.cmp(other))
1357 }
1358}
1359
1360impl Ord for TypeBinary {
1361 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1362 let op_as_int = self.op as u8;
1363 let other_op_as_int = other.op as u8;
1364 op_as_int
1365 .cmp(&other_op_as_int)
1366 .then_with(|| self.operands.cmp(&other.operands))
1367 }
1368}
1369
1370impl TypeBinary {
1371 pub fn new(op: BinaryOp, lhs: Ty, rhs: Ty) -> Interned<Self> {
1373 Interned::new(Self {
1374 operands: (lhs, rhs),
1375 op,
1376 })
1377 }
1378
1379 pub fn operands(&self) -> [&Ty; 2] {
1381 [&self.operands.0, &self.operands.1]
1382 }
1383}
1384
1385#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1388pub struct IfTy {
1389 pub cond: TyRef,
1391 pub then: TyRef,
1393 pub else_: TyRef,
1395}
1396
1397impl IfTy {
1398 pub fn new(cond: TyRef, then: TyRef, else_: TyRef) -> Interned<Self> {
1400 Interned::new(Self { cond, then, else_ })
1401 }
1402}
1403
1404#[derive(Default)]
1406pub struct TypeInfo {
1407 pub valid: bool,
1409 pub fid: Option<FileId>,
1411 pub revision: usize,
1413 pub exports: FxHashMap<StrRef, Ty>,
1415 pub vars: FxHashMap<DeclExpr, TypeVarBounds>,
1417 pub var_docs: FxHashMap<DeclExpr, Arc<UntypedDefDocs>>,
1419 pub local_binds: snapshot_map::SnapshotMap<DeclExpr, Ty>,
1421 pub mapping: FxHashMap<Span, FxHashSet<Ty>>,
1423 pub(super) cano_cache: Mutex<TypeCanoStore>,
1425}
1426
1427impl Hash for TypeInfo {
1428 fn hash<H: Hasher>(&self, state: &mut H) {
1429 self.valid.hash(state);
1430 self.fid.hash(state);
1431 self.revision.hash(state);
1432 }
1433}
1434
1435impl TyCtx for TypeInfo {
1436 fn global_bounds(&self, var: &Interned<TypeVar>, _pol: bool) -> Option<DynTypeBounds> {
1437 let v = self.vars.get(&var.def)?;
1438 Some(v.bounds.bounds().read().clone())
1439 }
1440
1441 fn local_bind_of(&self, var: &Interned<TypeVar>) -> Option<Ty> {
1442 self.local_binds.get(&var.def).cloned()
1443 }
1444}
1445
1446impl TypeInfo {
1447 pub fn type_of_span(&self, site: Span) -> Option<Ty> {
1449 self.mapping
1450 .get(&site)
1451 .cloned()
1452 .map(|types| Ty::from_types(types.into_iter()))
1453 }
1454
1455 pub fn witness_at_least(&mut self, site: Span, ty: Ty) {
1458 Self::witness_(site, ty, &mut self.mapping);
1459 }
1460 pub fn witness_at_most(&mut self, site: Span, ty: Ty) {
1462 Self::witness_(site, ty, &mut self.mapping);
1463 }
1464
1465 pub fn witness_(site: Span, ty: Ty, mapping: &mut FxHashMap<Span, FxHashSet<Ty>>) {
1467 if site.is_detached() {
1468 return;
1469 }
1470
1471 mapping.entry(site).or_default().insert(ty);
1473 }
1474
1475 pub fn to_bounds(&self, def: Ty) -> DynTypeBounds {
1477 let mut store = DynTypeBounds::default();
1478 match def {
1479 Ty::Var(v) => {
1480 let w = self.vars.get(&v.def).unwrap();
1481 match &w.bounds {
1482 FlowVarKind::Strong(bounds) | FlowVarKind::Weak(bounds) => {
1483 let w = bounds.read();
1484 for bound in w.lbs.iter() {
1485 store.lbs.insert_mut(bound.clone());
1486 }
1487 for bound in w.ubs.iter() {
1488 store.ubs.insert_mut(bound.clone());
1489 }
1490 }
1491 }
1492 }
1493 Ty::Let(bounds) => {
1494 for bound in bounds.lbs.iter() {
1495 store.lbs.insert_mut(bound.clone());
1496 }
1497 for bound in bounds.ubs.iter() {
1498 store.ubs.insert_mut(bound.clone());
1499 }
1500 }
1501 _ => {
1502 store.ubs.insert_mut(def);
1503 }
1504 }
1505
1506 store
1507 }
1508}
1509
1510impl TyCtxMut for TypeInfo {
1511 type Snap = ena::undo_log::Snapshot;
1512
1513 fn start_scope(&mut self) -> Self::Snap {
1514 self.local_binds.snapshot()
1515 }
1516
1517 fn end_scope(&mut self, snap: Self::Snap) {
1518 self.local_binds.rollback_to(snap);
1519 }
1520
1521 fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty) {
1522 self.local_binds.insert(var.def.clone(), ty);
1523 }
1524
1525 fn type_of_func(&mut self, _func: &typst::foundations::Func) -> Option<Interned<SigTy>> {
1526 None
1527 }
1528
1529 fn type_of_value(&mut self, _val: &Value) -> Ty {
1530 Ty::Any
1531 }
1532
1533 fn check_module_item(&mut self, _module: FileId, _key: &StrRef) -> Option<Ty> {
1534 None
1535 }
1536}
1537
1538#[derive(Clone)]
1540pub struct TypeVarBounds {
1541 pub var: Interned<TypeVar>,
1543 pub bounds: FlowVarKind,
1545}
1546
1547impl fmt::Debug for TypeVarBounds {
1548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1549 write!(f, "{:?}", self.var)
1550 }
1551}
1552
1553impl TypeVarBounds {
1554 pub fn new(var: TypeVar, init: DynTypeBounds) -> Self {
1556 Self {
1557 var: Interned::new(var),
1558 bounds: FlowVarKind::Strong(Arc::new(RwLock::new(init.clone()))),
1559 }
1560 }
1561
1562 pub fn name(&self) -> &StrRef {
1564 &self.var.name
1565 }
1566
1567 pub fn as_type(&self) -> Ty {
1569 Ty::Var(self.var.clone())
1570 }
1571
1572 pub fn weaken(&mut self) {
1574 match &self.bounds {
1575 FlowVarKind::Strong(w) => {
1576 self.bounds = FlowVarKind::Weak(w.clone());
1577 }
1578 FlowVarKind::Weak(_) => {}
1579 }
1580 }
1581}
1582
1583#[derive(Clone)]
1585pub enum FlowVarKind {
1586 Strong(Arc<RwLock<DynTypeBounds>>),
1588 Weak(Arc<RwLock<DynTypeBounds>>),
1591}
1592
1593impl FlowVarKind {
1594 pub fn bounds(&self) -> &RwLock<DynTypeBounds> {
1596 match self {
1597 FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => w,
1598 }
1599 }
1600}
1601
1602#[derive(Default)]
1604pub(super) struct TypeCanoStore {
1605 pub cano_cache: FxHashMap<(Ty, bool), Ty>,
1607 pub cano_local_cache: FxHashMap<(DeclExpr, bool), Ty>,
1609 pub negatives: FxHashSet<DeclExpr>,
1611 pub positives: FxHashSet<DeclExpr>,
1613}
1614
1615impl_internable!(Ty,);
1616impl_internable!(InsTy,);
1617impl_internable!(ParamTy,);
1618impl_internable!(TypeSource,);
1619impl_internable!(TypeVar,);
1620impl_internable!(SigWithTy,);
1621impl_internable!(SigTy,);
1622impl_internable!(RecordTy,);
1623impl_internable!(SelectTy,);
1624impl_internable!(TypeUnary,);
1625impl_internable!(TypeBinary,);
1626impl_internable!(IfTy,);
1627impl_internable!(Vec<Ty>,);
1628impl_internable!(TypeBounds,);
1629impl_internable!(NameBone,);
1630impl_internable!(PackageId,);
1631impl_internable!((Ty, Ty),);
1632
1633struct RefDebug<'a>(&'a Ty);
1634
1635impl fmt::Debug for RefDebug<'_> {
1636 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1637 match self.0 {
1638 Ty::Var(v) => write!(f, "@v{:?}", v.name()),
1639 _ => write!(f, "{:?}", self.0),
1640 }
1641 }
1642}
1643
1644fn interpersed<T: fmt::Debug>(
1645 f: &mut fmt::Formatter<'_>,
1646 iter: impl Iterator<Item = T>,
1647) -> fmt::Result {
1648 let mut first = true;
1649 for arg in iter {
1650 if first {
1651 first = false;
1652 } else {
1653 f.write_str(", ")?;
1654 }
1655 arg.fmt(f)?;
1656 }
1657 Ok(())
1658}
1659
1660#[cfg(test)]
1661mod tests {
1662 use insta::{assert_debug_snapshot, assert_snapshot};
1663
1664 use crate::ty::tests::*;
1665
1666 #[test]
1667 fn test_ty_size() {
1668 use super::*;
1669 assert!(size_of::<Ty>() <= size_of::<usize>() * 2);
1670 }
1671
1672 #[test]
1673 fn test_ty() {
1674 use super::*;
1675 let ty = Ty::Builtin(BuiltinTy::Clause);
1676 let ty_ref = TyRef::new(ty.clone());
1677 assert_debug_snapshot!(ty_ref, @"Clause");
1678 }
1679
1680 #[test]
1681 fn test_sig_matches() {
1682 use super::*;
1683
1684 fn matches(
1685 sig: Interned<SigTy>,
1686 args: Interned<SigTy>,
1687 with: Option<Vec<Interned<ArgsTy>>>,
1688 ) -> String {
1689 let res = sig.matches(&args, with.as_ref()).collect::<Vec<_>>();
1690 format!("{res:?}")
1691 }
1692
1693 assert_snapshot!(matches(literal_sig!(p1), literal_sig!(q1), None), @"[(@p1, @q1)]");
1694 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(q1), None), @"[(@p1, @q1)]");
1695 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(q1, q2), None), @"[(@p1, @q1), (@p2, @q2)]");
1696 assert_snapshot!(matches(literal_sig!(p1), literal_sig!(q1, q2), None), @"[(@p1, @q1)]");
1697
1698 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1), None), @"[(@p1, @q1)]");
1699 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1, q2), None), @"[(@p1, @q1), (@r1, @q2)]");
1700 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1, q2, q3), None), @"[(@p1, @q1), (@r1, @q2), (@r1, @q3)]");
1701 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(q1, q2), None), @"[(@r1, @q1), (@r1, @q2)]");
1702
1703 assert_snapshot!(matches(literal_sig!(p1), literal_sig!(q1, ...s2), None), @"[(@p1, @q1)]");
1704 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(q1, ...s2), None), @"[(@p1, @q1), (@p2, @s2)]");
1705 assert_snapshot!(matches(literal_sig!(p1, p2, p3), literal_sig!(q1, ...s2), None), @"[(@p1, @q1), (@p2, @s2), (@p3, @s2)]");
1706 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(...s2), None), @"[(@p1, @s2), (@p2, @s2)]");
1707
1708 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1, ...s2), None), @"[(@p1, @q1), (@r1, @s2)]");
1709 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(q1, ...s2), None), @"[(@r1, @q1), (@r1, @s2)]");
1710 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(...s2), None), @"[(@p1, @s2), (@r1, @s2)]");
1711 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(...s2), None), @"[(@r1, @s2)]");
1712
1713 assert_snapshot!(matches(literal_sig!(p0, p1, ...r1), literal_sig!(q1, ...s2), None), @"[(@p0, @q1), (@p1, @s2), (@r1, @s2)]");
1714 assert_snapshot!(matches(literal_sig!(p0, p1, ...r1), literal_sig!(...s2), None), @"[(@p0, @s2), (@p1, @s2), (@r1, @s2)]");
1715
1716 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q0, q1, ...s2), None), @"[(@p1, @q0), (@r1, @q1), (@r1, @s2)]");
1717 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(q0, q1, ...s2), None), @"[(@r1, @q0), (@r1, @q1), (@r1, @s2)]");
1718
1719 assert_snapshot!(matches(literal_sig!(p1 !u1: w1), literal_sig!(q1 !u1: w2), None), @"[(@p1, @q1), (@w1, @w2)]");
1720 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, ...r1), literal_sig!(q1 !u1: w2), None), @"[(@p1, @q1), (@w1, @w2)]");
1721 assert_snapshot!(matches(literal_sig!(p1 !u1: w1), literal_sig!(q1 !u1: w2, ...s2), None), @"[(@p1, @q1), (@w1, @w2)]");
1722 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, ...r1), literal_sig!(q1 !u1: w2, ...s2), None), @"[(@p1, @q1), (@r1, @s2), (@w1, @w2)]");
1723
1724 assert_snapshot!(matches(literal_sig!(), literal_sig!(!u1: w2), None), @"[]");
1725 assert_snapshot!(matches(literal_sig!(!u1: w1), literal_sig!(), None), @"[]");
1726 assert_snapshot!(matches(literal_sig!(!u1: w1), literal_sig!(!u1: w2), None), @"[(@w1, @w2)]");
1727 assert_snapshot!(matches(literal_sig!(!u1: w1), literal_sig!(!u2: w2), None), @"[]");
1728 assert_snapshot!(matches(literal_sig!(!u2: w1), literal_sig!(!u1: w2), None), @"[]");
1729 assert_snapshot!(matches(literal_sig!(!u1: w1, !u2: w3), literal_sig!(!u1: w2, !u2: w4), None), @"[(@w1, @w2), (@w3, @w4)]");
1730 assert_snapshot!(matches(literal_sig!(!u1: w1, !u2: w3), literal_sig!(!u2: w2, !u1: w4), None), @"[(@w1, @w4), (@w3, @w2)]");
1731 assert_snapshot!(matches(literal_sig!(!u2: w1), literal_sig!(!u1: w2, !u2: w4), None), @"[(@w1, @w4)]");
1732 assert_snapshot!(matches(literal_sig!(!u1: w1, !u2: w2), literal_sig!(!u2: w4), None), @"[(@w2, @w4)]");
1733
1734 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, !u2: w2), literal_sig!(q1), Some(vec![
1735 literal_sig!(!u2: w6),
1736 ])), @"[(@p1, @q1), (@w2, @w6)]");
1737 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, !u2: w2), literal_sig!(q1 !u2: w4), Some(vec![
1738 literal_sig!(!u2: w5),
1739 ])), @"[(@p1, @q1), (@w2, @w5), (@w2, @w4)]");
1740 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, !u2: w2), literal_sig!(q1 ), Some(vec![
1741 literal_sig!(!u2: w7),
1742 literal_sig!(!u2: w8),
1743 ])), @"[(@p1, @q1), (@w2, @w8), (@w2, @w7)]");
1744 assert_snapshot!(matches(literal_sig!(p1, p2, p3), literal_sig!(q1), Some(vec![
1745 literal_sig!(q2),
1746 literal_sig!(q3),
1747 ])), @"[(@p1, @q3), (@p2, @q2), (@p3, @q1)]");
1748 }
1749}