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 InsTy {
531 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
532 Some(self.cmp(other))
533 }
534}
535
536impl Ord for InsTy {
537 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
538 self.val
539 .partial_cmp(&other.val)
540 .unwrap_or(std::cmp::Ordering::Equal)
541 }
542}
543
544impl InsTy {
545 pub fn new(val: Value) -> Interned<Self> {
547 Self { val, syntax: None }.into()
548 }
549
550 pub fn new_at(val: Value, span: Span) -> Interned<Self> {
552 let mut name = SyntaxNode::leaf(SyntaxKind::Ident, "");
553 name.synthesize(span);
554 Interned::new(Self {
555 val,
556 syntax: Some(Interned::new(TypeSource {
557 name_node: name,
558 name_repr: OnceLock::new(),
559 doc: "".into(),
560 })),
561 })
562 }
563
564 pub fn new_doc(val: Value, doc: impl Into<StrRef>) -> Interned<Self> {
566 Interned::new(Self {
567 val,
568 syntax: Some(Interned::new(TypeSource {
569 name_node: SyntaxNode::default(),
570 name_repr: OnceLock::new(),
571 doc: doc.into(),
572 })),
573 })
574 }
575
576 pub fn span(&self) -> Span {
578 self.syntax
579 .as_ref()
580 .map(|source| source.name_node.span())
581 .or_else(|| {
582 Some(match &self.val {
583 Value::Func(func) => func.span(),
584 Value::Args(args) => args.span,
585 Value::Content(content) => content.span(),
586 _ => return None,
587 })
588 })
589 .unwrap_or_else(Span::detached)
590 }
591}
592
593#[derive(
595 Debug, Clone, Copy, Hash, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord,
596)]
597pub struct ParamAttrs {
598 pub positional: bool,
600 pub named: bool,
605 pub variadic: bool,
607 pub settable: bool,
609}
610
611impl ParamAttrs {
612 pub fn positional() -> ParamAttrs {
614 ParamAttrs {
615 positional: true,
616 named: false,
617 variadic: false,
618 settable: false,
619 }
620 }
621
622 pub fn named() -> ParamAttrs {
624 ParamAttrs {
625 positional: false,
626 named: true,
627 variadic: false,
628 settable: false,
629 }
630 }
631
632 pub fn variadic() -> ParamAttrs {
634 ParamAttrs {
635 positional: true,
636 named: false,
637 variadic: true,
638 settable: false,
639 }
640 }
641}
642
643impl From<&ParamInfo> for ParamAttrs {
644 fn from(param: &ParamInfo) -> Self {
645 ParamAttrs {
646 positional: param.positional,
647 named: param.named,
648 variadic: param.variadic,
649 settable: param.settable,
650 }
651 }
652}
653
654#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
656pub struct ParamTy {
657 pub name: StrRef,
659 pub docs: Option<EcoString>,
661 pub default: Option<EcoString>,
663 pub ty: Ty,
665 pub attrs: ParamAttrs,
667}
668
669impl ParamTy {
670 pub fn new_untyped(name: StrRef, attrs: ParamAttrs) -> Interned<Self> {
672 Self::new(Ty::Any, name, attrs)
673 }
674
675 pub fn new(ty: Ty, name: StrRef, attrs: ParamAttrs) -> Interned<Self> {
677 Interned::new(Self {
678 name,
679 ty,
680 docs: None,
681 default: None,
682 attrs,
683 })
684 }
685}
686
687#[derive(Hash, Clone, PartialEq, Eq)]
689pub struct TypeVar {
690 pub name: StrRef,
692 pub def: DeclExpr,
694}
695
696impl Ord for TypeVar {
697 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
698 self.def.cmp(&other.def)
700 }
701}
702
703impl TypeVar {
704 pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
707 self.def.strict_cmp(&other.def)
708 }
709}
710
711impl PartialOrd for TypeVar {
712 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
713 Some(self.cmp(other))
714 }
715}
716
717impl fmt::Debug for TypeVar {
718 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
719 write!(f, "@{}", self.name)
720 }
721}
722
723impl TypeVar {
724 pub fn new(name: StrRef, def: DeclExpr) -> Interned<Self> {
726 Interned::new(Self { name, def })
727 }
728
729 pub fn name(&self) -> StrRef {
731 self.name.clone()
732 }
733}
734
735#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
737pub struct RecordTy {
738 pub names: Interned<NameBone>,
740 pub types: Interned<Vec<Ty>>,
742}
743
744impl RecordTy {
745 pub fn shape_fields(mut fields: Vec<(StrRef, Ty)>) -> (NameBone, Vec<Ty>) {
747 fields.sort_by(|a, b| a.0.cmp(&b.0));
748 let names = NameBone {
749 names: fields.iter().map(|(name, _)| name.clone()).collect(),
750 };
751 let types = fields.into_iter().map(|(_, ty)| ty).collect::<Vec<_>>();
752
753 (names, types)
754 }
755
756 pub fn new(fields: Vec<(StrRef, Ty)>) -> Interned<Self> {
758 let (names, types) = Self::shape_fields(fields);
759 Interned::new(Self {
760 types: Interned::new(types),
761 names: Interned::new(names),
762 })
763 }
764}
765
766impl TypeInterface for RecordTy {
767 fn bone(&self) -> &Interned<NameBone> {
768 &self.names
769 }
770
771 fn field_by_bone_offset(&self, idx: usize) -> Option<&Ty> {
772 self.types.get(idx)
773 }
774
775 fn interface(&self) -> impl Iterator<Item = (&StrRef, &Ty)> {
776 self.names.names.iter().zip(self.types.iter())
777 }
778}
779
780impl fmt::Debug for RecordTy {
781 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
782 f.write_str("{")?;
783 interpersed(
784 f,
785 self.interface()
786 .map(|(name, ty)| TypeSigParam::Named(name, ty)),
787 )?;
788 f.write_str("}")
789 }
790}
791
792#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
794pub struct SigTy {
795 pub inputs: Interned<Vec<Ty>>,
797 pub body: Option<Ty>,
799 pub names: Interned<NameBone>,
801 pub name_started: u32,
803 pub spread_left: bool,
805 pub spread_right: bool,
807}
808
809impl SigTy {
810 pub fn any() -> Interned<SigTy> {
812 let rest = Ty::Array(Interned::new(Ty::Any));
813 Interned::new(Self {
814 inputs: Interned::new(vec![rest]),
815 body: Some(Ty::Any),
816 names: NameBone::empty(),
817 name_started: 0,
818 spread_left: false,
819 spread_right: true,
820 })
821 }
822
823 #[comemo::memoize]
825 pub fn array_cons(elem: Ty, anyify: bool) -> Interned<SigTy> {
826 let rest = Ty::Array(Interned::new(elem.clone()));
827 let ret = if anyify { Ty::Any } else { rest.clone() };
828 Interned::new(Self {
829 inputs: Interned::new(vec![rest]),
830 body: Some(ret),
831 names: NameBone::empty(),
832 name_started: 0,
833 spread_left: false,
834 spread_right: true,
835 })
836 }
837
838 #[comemo::memoize]
840 pub fn unary(inp: Ty, ret: Ty) -> Interned<SigTy> {
841 Interned::new(Self {
842 inputs: Interned::new(vec![inp]),
843 body: Some(ret),
844 names: NameBone::empty(),
845 name_started: 1,
846 spread_left: false,
847 spread_right: false,
848 })
849 }
850
851 #[comemo::memoize]
853 pub fn tuple_cons(elems: Interned<Vec<Ty>>, anyify: bool) -> Interned<SigTy> {
854 let ret = if anyify {
855 Ty::Any
856 } else {
857 Ty::Tuple(elems.clone())
858 };
859 let name_started = elems.len() as u32;
860 Interned::new(Self {
861 inputs: elems,
862 body: Some(ret),
863 names: NameBone::empty(),
864 name_started,
865 spread_left: false,
866 spread_right: false,
867 })
868 }
869
870 #[comemo::memoize]
872 pub fn dict_cons(named: &Interned<RecordTy>, anyify: bool) -> Interned<SigTy> {
873 let ret = if anyify {
874 Ty::Any
875 } else {
876 Ty::Dict(named.clone())
877 };
878
879 Interned::new(Self {
880 inputs: named.types.clone(),
881 body: Some(ret),
882 names: named.names.clone(),
883 name_started: 0,
884 spread_left: false,
885 spread_right: false,
886 })
887 }
888
889 pub fn with_body(mut self, res_ty: Ty) -> Self {
891 self.body = Some(res_ty);
892 self
893 }
894
895 pub fn new(
897 pos: impl ExactSizeIterator<Item = Ty>,
898 named: impl IntoIterator<Item = (StrRef, Ty)>,
899 rest_left: Option<Ty>,
900 rest_right: Option<Ty>,
901 ret_ty: Option<Ty>,
902 ) -> Self {
903 let named = named.into_iter().collect::<Vec<_>>();
904 let (names, mut named_types) = RecordTy::shape_fields(named);
905 let spread_left = rest_left.is_some();
906 let spread_right = rest_right.is_some();
907
908 let name_started = if spread_right { 1 } else { 0 } + named_types.len();
909 let mut types = Vec::with_capacity(
910 pos.len() + named_types.len() + spread_left as usize + spread_right as usize,
911 );
912 types.extend(pos);
913 types.append(&mut named_types);
914 types.extend(rest_left);
915 types.extend(rest_right);
916
917 let name_started = (types.len() - name_started) as u32;
918
919 Self {
920 inputs: Interned::new(types),
921 body: ret_ty,
922 names: Interned::new(names),
923 name_started,
924 spread_left,
925 spread_right,
926 }
927 }
928}
929
930impl Default for SigTy {
931 fn default() -> Self {
932 Self {
933 inputs: Interned::new(Vec::new()),
934 body: None,
935 names: NameBone::empty(),
936 name_started: 0,
937 spread_left: false,
938 spread_right: false,
939 }
940 }
941}
942
943impl TypeInterface for SigTy {
944 fn bone(&self) -> &Interned<NameBone> {
945 &self.names
946 }
947
948 fn interface(&self) -> impl Iterator<Item = (&StrRef, &Ty)> {
949 let names = self.names.names.iter();
950 let types = self.inputs.iter().skip(self.name_started as usize);
951 names.zip(types)
952 }
953
954 fn field_by_bone_offset(&self, offset: usize) -> Option<&Ty> {
955 self.inputs.get(offset + self.name_started as usize)
956 }
957}
958
959impl SigTy {
960 pub fn inputs(&self) -> impl Iterator<Item = &Ty> {
962 self.inputs.iter()
963 }
964
965 pub fn positional_params(&self) -> impl ExactSizeIterator<Item = &Ty> {
967 self.inputs.iter().take(self.name_started as usize)
968 }
969
970 pub fn pos(&self, idx: usize) -> Option<&Ty> {
972 (idx < self.name_started as usize)
973 .then_some(())
974 .and_then(|_| self.inputs.get(idx))
975 }
976
977 pub fn pos_or_rest(&self, idx: usize) -> Option<Ty> {
979 let nth = self.pos(idx).cloned();
980 nth.or_else(|| {
981 let rest_idx = || idx.saturating_sub(self.positional_params().len());
982
983 let rest_ty = self.rest_param()?;
984 match rest_ty {
985 Ty::Array(ty) => Some(ty.as_ref().clone()),
986 Ty::Tuple(tys) => tys.get(rest_idx()).cloned(),
987 _ => None,
988 }
989 })
990 }
991
992 pub fn named_params(&self) -> impl ExactSizeIterator<Item = (&StrRef, &Ty)> {
994 let named_names = self.names.names.iter();
995 let named_types = self.inputs.iter().skip(self.name_started as usize);
996
997 named_names.zip(named_types)
998 }
999
1000 pub fn named(&self, name: &StrRef) -> Option<&Ty> {
1002 let idx = self.names.find(name)?;
1003 self.inputs.get(idx + self.name_started as usize)
1004 }
1005
1006 pub fn rest_param(&self) -> Option<&Ty> {
1008 if self.spread_right {
1009 self.inputs.last()
1010 } else {
1011 None
1012 }
1013 }
1014
1015 pub fn matches<'a>(
1017 &'a self,
1018 args: &'a SigTy,
1019 with: Option<&'a Vec<Interned<SigTy>>>,
1020 ) -> impl Iterator<Item = (&'a Ty, &'a Ty)> + 'a {
1021 let with_len = with
1022 .map(|w| w.iter().map(|w| w.positional_params().len()).sum::<usize>())
1023 .unwrap_or(0);
1024
1025 let sig_pos = self.positional_params();
1026 let arg_pos = args.positional_params();
1027
1028 let sig_rest = self.rest_param();
1029 let arg_rest = args.rest_param();
1030
1031 let max_len = sig_pos.len().max(with_len + arg_pos.len())
1032 + if sig_rest.is_some() && arg_rest.is_some() {
1033 1
1034 } else {
1035 0
1036 };
1037
1038 let arg_pos = with
1039 .into_iter()
1040 .flat_map(|w| w.iter().rev().map(|w| w.positional_params()))
1041 .flatten()
1042 .chain(arg_pos);
1043
1044 let sig_stream = sig_pos.chain(sig_rest.into_iter().cycle()).take(max_len);
1045 let arg_stream = arg_pos.chain(arg_rest.into_iter().cycle()).take(max_len);
1046
1047 let pos = sig_stream.zip(arg_stream);
1048 let common_ifaces = with
1049 .map(|args_all| args_all.iter().rev())
1050 .into_iter()
1051 .flatten()
1052 .flat_map(|args| self.common_iface_fields(args))
1053 .chain(self.common_iface_fields(args));
1054 let named = common_ifaces.map(|(_, l, r)| (l, r));
1055
1056 pos.chain(named)
1057 }
1058}
1059
1060impl fmt::Debug for SigTy {
1061 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1062 f.write_str("(")?;
1063 let pos = self.positional_params().map(TypeSigParam::Pos);
1064 let named = self
1065 .named_params()
1066 .map(|(name, ty)| TypeSigParam::Named(name, ty));
1067 let rest = self.rest_param().map(TypeSigParam::Rest);
1068 interpersed(f, pos.chain(named).chain(rest))?;
1069 f.write_str(") => ")?;
1070 if let Some(ret) = &self.body {
1071 ret.fmt(f)?;
1072 } else {
1073 f.write_str("any")?;
1074 }
1075 Ok(())
1076 }
1077}
1078
1079pub type ArgsTy = SigTy;
1081
1082pub type PatternTy = SigTy;
1084
1085#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1087pub struct SigWithTy {
1088 pub sig: TyRef,
1090 pub with: Interned<ArgsTy>,
1092}
1093
1094impl SigWithTy {
1095 pub fn new(sig: TyRef, with: Interned<ArgsTy>) -> Interned<Self> {
1097 Interned::new(Self { sig, with })
1098 }
1099}
1100
1101impl fmt::Debug for SigWithTy {
1102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1103 write!(f, "{:?}.with({:?})", self.sig, self.with)
1104 }
1105}
1106
1107#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1109pub struct SelectTy {
1110 pub ty: TyRef,
1112 pub select: StrRef,
1114}
1115
1116impl SelectTy {
1117 pub fn new(ty: TyRef, select: StrRef) -> Interned<Self> {
1119 Interned::new(Self { ty, select })
1120 }
1121}
1122
1123impl fmt::Debug for SelectTy {
1124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1125 write!(f, "{:?}.{}", RefDebug(&self.ty), self.select)
1126 }
1127}
1128
1129#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1131pub struct TypeUnary {
1132 pub lhs: Ty,
1134 pub op: UnaryOp,
1136}
1137
1138impl PartialOrd for TypeUnary {
1139 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1140 Some(self.cmp(other))
1141 }
1142}
1143
1144impl Ord for TypeUnary {
1145 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1146 let op_as_int = self.op as u8;
1147 let other_op_as_int = other.op as u8;
1148 op_as_int
1149 .cmp(&other_op_as_int)
1150 .then_with(|| self.lhs.cmp(&other.lhs))
1151 }
1152}
1153
1154impl TypeUnary {
1155 pub fn new(op: UnaryOp, lhs: Ty) -> Interned<Self> {
1157 Interned::new(Self { lhs, op })
1158 }
1159
1160 pub fn operands(&self) -> [&Ty; 1] {
1162 [&self.lhs]
1163 }
1164}
1165
1166pub type BinaryOp = ast::BinOp;
1168
1169#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1171pub struct TypeBinary {
1172 pub operands: (Ty, Ty),
1174 pub op: BinaryOp,
1176}
1177
1178impl PartialOrd for TypeBinary {
1179 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1180 Some(self.cmp(other))
1181 }
1182}
1183
1184impl Ord for TypeBinary {
1185 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1186 let op_as_int = self.op as u8;
1187 let other_op_as_int = other.op as u8;
1188 op_as_int
1189 .cmp(&other_op_as_int)
1190 .then_with(|| self.operands.cmp(&other.operands))
1191 }
1192}
1193
1194impl TypeBinary {
1195 pub fn new(op: BinaryOp, lhs: Ty, rhs: Ty) -> Interned<Self> {
1197 Interned::new(Self {
1198 operands: (lhs, rhs),
1199 op,
1200 })
1201 }
1202
1203 pub fn operands(&self) -> [&Ty; 2] {
1205 [&self.operands.0, &self.operands.1]
1206 }
1207}
1208
1209#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1212pub struct IfTy {
1213 pub cond: TyRef,
1215 pub then: TyRef,
1217 pub else_: TyRef,
1219}
1220
1221impl IfTy {
1222 pub fn new(cond: TyRef, then: TyRef, else_: TyRef) -> Interned<Self> {
1224 Interned::new(Self { cond, then, else_ })
1225 }
1226}
1227
1228#[derive(Default)]
1230pub struct TypeInfo {
1231 pub valid: bool,
1233 pub fid: Option<FileId>,
1235 pub revision: usize,
1237 pub exports: FxHashMap<StrRef, Ty>,
1239 pub vars: FxHashMap<DeclExpr, TypeVarBounds>,
1241 pub var_docs: FxHashMap<DeclExpr, Arc<UntypedDefDocs>>,
1243 pub local_binds: snapshot_map::SnapshotMap<DeclExpr, Ty>,
1245 pub mapping: FxHashMap<Span, FxHashSet<Ty>>,
1247 pub(super) cano_cache: Mutex<TypeCanoStore>,
1249}
1250
1251impl Hash for TypeInfo {
1252 fn hash<H: Hasher>(&self, state: &mut H) {
1253 self.valid.hash(state);
1254 self.fid.hash(state);
1255 self.revision.hash(state);
1256 }
1257}
1258
1259impl TyCtx for TypeInfo {
1260 fn global_bounds(&self, var: &Interned<TypeVar>, _pol: bool) -> Option<DynTypeBounds> {
1261 let v = self.vars.get(&var.def)?;
1262 Some(v.bounds.bounds().read().clone())
1263 }
1264
1265 fn local_bind_of(&self, var: &Interned<TypeVar>) -> Option<Ty> {
1266 self.local_binds.get(&var.def).cloned()
1267 }
1268}
1269
1270impl TypeInfo {
1271 pub fn type_of_span(&self, site: Span) -> Option<Ty> {
1273 self.mapping
1274 .get(&site)
1275 .cloned()
1276 .map(|types| Ty::from_types(types.into_iter()))
1277 }
1278
1279 pub fn witness_at_least(&mut self, site: Span, ty: Ty) {
1282 Self::witness_(site, ty, &mut self.mapping);
1283 }
1284 pub fn witness_at_most(&mut self, site: Span, ty: Ty) {
1286 Self::witness_(site, ty, &mut self.mapping);
1287 }
1288
1289 pub fn witness_(site: Span, ty: Ty, mapping: &mut FxHashMap<Span, FxHashSet<Ty>>) {
1291 if site.is_detached() {
1292 return;
1293 }
1294
1295 mapping.entry(site).or_default().insert(ty);
1297 }
1298
1299 pub fn to_bounds(&self, def: Ty) -> DynTypeBounds {
1301 let mut store = DynTypeBounds::default();
1302 match def {
1303 Ty::Var(v) => {
1304 let w = self.vars.get(&v.def).unwrap();
1305 match &w.bounds {
1306 FlowVarKind::Strong(bounds) | FlowVarKind::Weak(bounds) => {
1307 let w = bounds.read();
1308 for bound in w.lbs.iter() {
1309 store.lbs.insert_mut(bound.clone());
1310 }
1311 for bound in w.ubs.iter() {
1312 store.ubs.insert_mut(bound.clone());
1313 }
1314 }
1315 }
1316 }
1317 Ty::Let(bounds) => {
1318 for bound in bounds.lbs.iter() {
1319 store.lbs.insert_mut(bound.clone());
1320 }
1321 for bound in bounds.ubs.iter() {
1322 store.ubs.insert_mut(bound.clone());
1323 }
1324 }
1325 _ => {
1326 store.ubs.insert_mut(def);
1327 }
1328 }
1329
1330 store
1331 }
1332}
1333
1334impl TyCtxMut for TypeInfo {
1335 type Snap = ena::undo_log::Snapshot;
1336
1337 fn start_scope(&mut self) -> Self::Snap {
1338 self.local_binds.snapshot()
1339 }
1340
1341 fn end_scope(&mut self, snap: Self::Snap) {
1342 self.local_binds.rollback_to(snap);
1343 }
1344
1345 fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty) {
1346 self.local_binds.insert(var.def.clone(), ty);
1347 }
1348
1349 fn type_of_func(&mut self, _func: &typst::foundations::Func) -> Option<Interned<SigTy>> {
1350 None
1351 }
1352
1353 fn type_of_value(&mut self, _val: &Value) -> Ty {
1354 Ty::Any
1355 }
1356
1357 fn check_module_item(&mut self, _module: FileId, _key: &StrRef) -> Option<Ty> {
1358 None
1359 }
1360}
1361
1362#[derive(Clone)]
1364pub struct TypeVarBounds {
1365 pub var: Interned<TypeVar>,
1367 pub bounds: FlowVarKind,
1369}
1370
1371impl fmt::Debug for TypeVarBounds {
1372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1373 write!(f, "{:?}", self.var)
1374 }
1375}
1376
1377impl TypeVarBounds {
1378 pub fn new(var: TypeVar, init: DynTypeBounds) -> Self {
1380 Self {
1381 var: Interned::new(var),
1382 bounds: FlowVarKind::Strong(Arc::new(RwLock::new(init.clone()))),
1383 }
1384 }
1385
1386 pub fn name(&self) -> &StrRef {
1388 &self.var.name
1389 }
1390
1391 pub fn as_type(&self) -> Ty {
1393 Ty::Var(self.var.clone())
1394 }
1395
1396 pub fn weaken(&mut self) {
1398 match &self.bounds {
1399 FlowVarKind::Strong(w) => {
1400 self.bounds = FlowVarKind::Weak(w.clone());
1401 }
1402 FlowVarKind::Weak(_) => {}
1403 }
1404 }
1405}
1406
1407#[derive(Clone)]
1409pub enum FlowVarKind {
1410 Strong(Arc<RwLock<DynTypeBounds>>),
1412 Weak(Arc<RwLock<DynTypeBounds>>),
1415}
1416
1417impl FlowVarKind {
1418 pub fn bounds(&self) -> &RwLock<DynTypeBounds> {
1420 match self {
1421 FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => w,
1422 }
1423 }
1424}
1425
1426#[derive(Default)]
1428pub(super) struct TypeCanoStore {
1429 pub cano_cache: FxHashMap<(Ty, bool), Ty>,
1431 pub cano_local_cache: FxHashMap<(DeclExpr, bool), Ty>,
1433 pub negatives: FxHashSet<DeclExpr>,
1435 pub positives: FxHashSet<DeclExpr>,
1437}
1438
1439impl_internable!(Ty,);
1440impl_internable!(InsTy,);
1441impl_internable!(ParamTy,);
1442impl_internable!(TypeSource,);
1443impl_internable!(TypeVar,);
1444impl_internable!(SigWithTy,);
1445impl_internable!(SigTy,);
1446impl_internable!(RecordTy,);
1447impl_internable!(SelectTy,);
1448impl_internable!(TypeUnary,);
1449impl_internable!(TypeBinary,);
1450impl_internable!(IfTy,);
1451impl_internable!(Vec<Ty>,);
1452impl_internable!(TypeBounds,);
1453impl_internable!(NameBone,);
1454impl_internable!(PackageId,);
1455impl_internable!((Ty, Ty),);
1456
1457struct RefDebug<'a>(&'a Ty);
1458
1459impl fmt::Debug for RefDebug<'_> {
1460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1461 match self.0 {
1462 Ty::Var(v) => write!(f, "@v{:?}", v.name()),
1463 _ => write!(f, "{:?}", self.0),
1464 }
1465 }
1466}
1467
1468fn interpersed<T: fmt::Debug>(
1469 f: &mut fmt::Formatter<'_>,
1470 iter: impl Iterator<Item = T>,
1471) -> fmt::Result {
1472 let mut first = true;
1473 for arg in iter {
1474 if first {
1475 first = false;
1476 } else {
1477 f.write_str(", ")?;
1478 }
1479 arg.fmt(f)?;
1480 }
1481 Ok(())
1482}
1483
1484#[cfg(test)]
1485mod tests {
1486 use insta::{assert_debug_snapshot, assert_snapshot};
1487
1488 use crate::ty::tests::*;
1489
1490 #[test]
1491 fn test_ty_size() {
1492 use super::*;
1493 assert!(size_of::<Ty>() <= size_of::<usize>() * 2);
1494 }
1495
1496 #[test]
1497 fn test_ty() {
1498 use super::*;
1499 let ty = Ty::Builtin(BuiltinTy::Clause);
1500 let ty_ref = TyRef::new(ty.clone());
1501 assert_debug_snapshot!(ty_ref, @"Clause");
1502 }
1503
1504 #[test]
1505 fn test_sig_matches() {
1506 use super::*;
1507
1508 fn matches(
1509 sig: Interned<SigTy>,
1510 args: Interned<SigTy>,
1511 with: Option<Vec<Interned<ArgsTy>>>,
1512 ) -> String {
1513 let res = sig.matches(&args, with.as_ref()).collect::<Vec<_>>();
1514 format!("{res:?}")
1515 }
1516
1517 assert_snapshot!(matches(literal_sig!(p1), literal_sig!(q1), None), @"[(@p1, @q1)]");
1518 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(q1), None), @"[(@p1, @q1)]");
1519 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(q1, q2), None), @"[(@p1, @q1), (@p2, @q2)]");
1520 assert_snapshot!(matches(literal_sig!(p1), literal_sig!(q1, q2), None), @"[(@p1, @q1)]");
1521
1522 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1), None), @"[(@p1, @q1)]");
1523 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1, q2), None), @"[(@p1, @q1), (@r1, @q2)]");
1524 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1, q2, q3), None), @"[(@p1, @q1), (@r1, @q2), (@r1, @q3)]");
1525 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(q1, q2), None), @"[(@r1, @q1), (@r1, @q2)]");
1526
1527 assert_snapshot!(matches(literal_sig!(p1), literal_sig!(q1, ...s2), None), @"[(@p1, @q1)]");
1528 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(q1, ...s2), None), @"[(@p1, @q1), (@p2, @s2)]");
1529 assert_snapshot!(matches(literal_sig!(p1, p2, p3), literal_sig!(q1, ...s2), None), @"[(@p1, @q1), (@p2, @s2), (@p3, @s2)]");
1530 assert_snapshot!(matches(literal_sig!(p1, p2), literal_sig!(...s2), None), @"[(@p1, @s2), (@p2, @s2)]");
1531
1532 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q1, ...s2), None), @"[(@p1, @q1), (@r1, @s2)]");
1533 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(q1, ...s2), None), @"[(@r1, @q1), (@r1, @s2)]");
1534 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(...s2), None), @"[(@p1, @s2), (@r1, @s2)]");
1535 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(...s2), None), @"[(@r1, @s2)]");
1536
1537 assert_snapshot!(matches(literal_sig!(p0, p1, ...r1), literal_sig!(q1, ...s2), None), @"[(@p0, @q1), (@p1, @s2), (@r1, @s2)]");
1538 assert_snapshot!(matches(literal_sig!(p0, p1, ...r1), literal_sig!(...s2), None), @"[(@p0, @s2), (@p1, @s2), (@r1, @s2)]");
1539
1540 assert_snapshot!(matches(literal_sig!(p1, ...r1), literal_sig!(q0, q1, ...s2), None), @"[(@p1, @q0), (@r1, @q1), (@r1, @s2)]");
1541 assert_snapshot!(matches(literal_sig!(...r1), literal_sig!(q0, q1, ...s2), None), @"[(@r1, @q0), (@r1, @q1), (@r1, @s2)]");
1542
1543 assert_snapshot!(matches(literal_sig!(p1 !u1: w1), literal_sig!(q1 !u1: w2), None), @"[(@p1, @q1), (@w1, @w2)]");
1544 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, ...r1), literal_sig!(q1 !u1: w2), None), @"[(@p1, @q1), (@w1, @w2)]");
1545 assert_snapshot!(matches(literal_sig!(p1 !u1: w1), literal_sig!(q1 !u1: w2, ...s2), None), @"[(@p1, @q1), (@w1, @w2)]");
1546 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, ...r1), literal_sig!(q1 !u1: w2, ...s2), None), @"[(@p1, @q1), (@r1, @s2), (@w1, @w2)]");
1547
1548 assert_snapshot!(matches(literal_sig!(), literal_sig!(!u1: w2), None), @"[]");
1549 assert_snapshot!(matches(literal_sig!(!u1: w1), literal_sig!(), None), @"[]");
1550 assert_snapshot!(matches(literal_sig!(!u1: w1), literal_sig!(!u1: w2), None), @"[(@w1, @w2)]");
1551 assert_snapshot!(matches(literal_sig!(!u1: w1), literal_sig!(!u2: w2), None), @"[]");
1552 assert_snapshot!(matches(literal_sig!(!u2: w1), literal_sig!(!u1: w2), None), @"[]");
1553 assert_snapshot!(matches(literal_sig!(!u1: w1, !u2: w3), literal_sig!(!u1: w2, !u2: w4), None), @"[(@w1, @w2), (@w3, @w4)]");
1554 assert_snapshot!(matches(literal_sig!(!u1: w1, !u2: w3), literal_sig!(!u2: w2, !u1: w4), None), @"[(@w1, @w4), (@w3, @w2)]");
1555 assert_snapshot!(matches(literal_sig!(!u2: w1), literal_sig!(!u1: w2, !u2: w4), None), @"[(@w1, @w4)]");
1556 assert_snapshot!(matches(literal_sig!(!u1: w1, !u2: w2), literal_sig!(!u2: w4), None), @"[(@w2, @w4)]");
1557
1558 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, !u2: w2), literal_sig!(q1), Some(vec![
1559 literal_sig!(!u2: w6),
1560 ])), @"[(@p1, @q1), (@w2, @w6)]");
1561 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, !u2: w2), literal_sig!(q1 !u2: w4), Some(vec![
1562 literal_sig!(!u2: w5),
1563 ])), @"[(@p1, @q1), (@w2, @w5), (@w2, @w4)]");
1564 assert_snapshot!(matches(literal_sig!(p1 !u1: w1, !u2: w2), literal_sig!(q1 ), Some(vec![
1565 literal_sig!(!u2: w7),
1566 literal_sig!(!u2: w8),
1567 ])), @"[(@p1, @q1), (@w2, @w8), (@w2, @w7)]");
1568 assert_snapshot!(matches(literal_sig!(p1, p2, p3), literal_sig!(q1), Some(vec![
1569 literal_sig!(q2),
1570 literal_sig!(q3),
1571 ])), @"[(@p1, @q3), (@p2, @q2), (@p3, @q1)]");
1572 }
1573}