tinymist_analysis/ty/
def.rs

1//! Name Convention:
2//! - `TypeXXX`: abstracted types or clauses
3//! - `XXTy`: concrete types
4
5use 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
31/// A reference to the interned type.
32pub(crate) type TyRef = Interned<Ty>;
33/// A reference to the interned string.
34pub(crate) type StrRef = Interned<str>;
35
36/// All possible types in tinymist.
37#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
38pub enum Ty {
39    // Simple Types
40    /// A top type, whose negation is bottom type.
41    /// `t := top, t^- := bottom`
42    Any,
43    /// A boolean type, can be `false`, `true`, or both (boolean type).
44    /// `t := false | true`
45    Boolean(Option<bool>),
46    /// All possible types in typst.
47    Builtin(BuiltinTy),
48    /// A possible typst instance of some type.
49    Value(Interned<InsTy>),
50    /// A parameter type
51    Param(Interned<ParamTy>),
52
53    // Combination Types
54    /// A union type, whose negation is intersection type.
55    /// `t := t1 | t2 | ... | tn, t^- := t1 & t2 & ... & tn`
56    Union(Interned<Vec<Ty>>),
57    /// A frozen type variable.
58    /// `t :> t1 | t2 | ... | tn <: f1 & f2 & ... & fn`
59    Let(Interned<TypeBounds>),
60    /// An opening type variable owing bounds.
61    Var(Interned<TypeVar>),
62
63    // Composite Types
64    /// A typst dictionary type.
65    Dict(Interned<RecordTy>),
66    /// An array type.
67    Array(TyRef),
68    /// A tuple type.
69    /// Note: may contains spread types.
70    Tuple(Interned<Vec<Ty>>),
71    /// A function type.
72    Func(Interned<SigTy>),
73    /// An argument type.
74    Args(Interned<ArgsTy>),
75    /// A pattern type.
76    Pattern(Interned<PatternTy>),
77
78    // Type operations
79    /// A partially applied function type.
80    With(Interned<SigWithTy>),
81    /// Select a field from a type.
82    Select(Interned<SelectTy>),
83    /// A unary operation.
84    Unary(Interned<TypeUnary>),
85    /// A binary operation.
86    Binary(Interned<TypeBinary>),
87    /// A conditional type.
88    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    /// Whether the type is a dictionary type.
140    pub fn is_dict(&self) -> bool {
141        matches!(self, Ty::Dict(..))
142    }
143
144    /// Creates a union type from two types.
145    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    /// Creates a union type from an iterator of types.
154    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    /// Creates a union type from an iterator of types.     
166    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    /// Creates an undefined type (which will emit an error).
173    /// A that type is annotated if the syntax structure causes an type error.
174    pub const fn undef() -> Self {
175        Ty::Builtin(BuiltinTy::Undef)
176    }
177
178    /// Gets the name of the type.
179    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    /// Gets the span of the type.
191    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    /// Gets the value repr of the type.
213    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    /// Gets the element type.
223    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    /// Checks a type against a context.
235    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    /// Checks if the type is a content type.
240    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    /// Checks if the type is a string type.
256    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    /// Checks if the type is a type type.
271    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
287/// Checks if the type is a content builtin type.
288fn is_content_builtin_type(ty: &Type) -> bool {
289    *ty == Type::of::<Content>() || *ty == Type::of::<typst::foundations::Symbol>()
290}
291
292/// Checks if the type is a string builtin type.
293fn is_str_builtin_type(ty: &Type) -> bool {
294    *ty == Type::of::<typst::foundations::Str>()
295}
296
297/// Checks if the type is a type builtin type.
298fn is_type_builtin_type(ty: &Type) -> bool {
299    *ty == Type::of::<Type>()
300}
301
302/// A function parameter type.
303pub enum TypeSigParam<'a> {
304    /// A positional parameter: `a`
305    Pos(&'a Ty),
306    /// A named parameter: `b: c`
307    Named(&'a StrRef, &'a Ty),
308    /// A rest parameter (spread right): `..d`
309    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            // todo: the rest is not three dots
318            TypeSigParam::Rest(ty) => write!(f, "...: {ty:?}"),
319        }
320    }
321}
322
323/// The syntax source (definition) of a type node.
324/// todo: whether we should store them in the type node
325#[derive(Debug, Clone, PartialEq, Eq)]
326pub struct TypeSource {
327    /// A name node with span.
328    pub name_node: SyntaxNode,
329    /// A lazy evaluated name.
330    pub name_repr: OnceLock<StrRef>,
331    /// The attached documentation.
332    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    /// Gets the name of the type node.
344    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/// An ordered list of names.
359#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
360pub struct NameBone {
361    /// The names in the bone.
362    pub names: Box<[StrRef]>,
363}
364
365impl NameBone {
366    /// Creates an empty bone.
367    pub fn empty() -> Interned<Self> {
368        Interned::new(Self {
369            names: Box::new([]),
370        })
371    }
372}
373
374impl NameBone {
375    /// Finds the index of the name in the bone.
376    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    /// Intersects the names of two bones.
383    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/// The state of a type variable (bounds of some type in program).
419#[derive(Clone, Default)]
420pub struct DynTypeBounds {
421    /// The lower bounds
422    pub lbs: rpds::HashTrieSetSync<Ty>,
423    /// The upper bounds
424    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    /// Gets the frozen bounds.
438    pub fn freeze(&self) -> TypeBounds {
439        // sorted
440        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/// A frozen type variable (bounds of some type in program).
449/// `t :> t1 | ... | tn <: f1 & ... & fn`
450/// `  lbs------------- ubs-------------`
451#[derive(Hash, Clone, PartialEq, Eq, Default, PartialOrd, Ord)]
452pub struct TypeBounds {
453    /// The lower bounds.
454    pub lbs: Vec<Ty>,
455    /// The upper bounds.
456    pub ubs: Vec<Ty>,
457}
458
459impl fmt::Debug for TypeBounds {
460    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461        // write!(f, "{}", self.name)
462        // also where
463        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
479/// A common type kinds for those types that has fields (abstracted record
480/// type).
481pub trait TypeInterface {
482    /// Gets the bone of a record.
483    /// See [`NameBone`] for more details.
484    fn bone(&self) -> &Interned<NameBone>;
485    /// Iterates over the fields of a record.
486    fn interface(&self) -> impl Iterator<Item = (&StrRef, &Ty)>;
487    /// Gets the field by bone offset.
488    fn field_by_bone_offset(&self, idx: usize) -> Option<&Ty>;
489    /// Gets the field by name.
490    fn field_by_name(&self, name: &StrRef) -> Option<&Ty> {
491        self.field_by_bone_offset(self.bone().find(name)?)
492    }
493}
494
495/// Extension common methods for [`TypeInterface`].
496pub trait TypeInterfaceExt: TypeInterface {
497    /// Convenience method to get the common fields of two records.
498    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/// An instance of a typst type.
518#[derive(Debug, Hash, Clone, PartialEq)]
519pub struct InsTy {
520    /// The value of the instance.
521    pub val: Value,
522    /// The syntax source of the instance.
523    pub syntax: Option<Interned<TypeSource>>,
524}
525
526/// There are some case that val is not Eq, but we make it Eq for simplicity
527/// For example, a float instance which is NaN.
528impl 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    /// Creates an instance.
546    pub fn new(val: Value) -> Interned<Self> {
547        Self { val, syntax: None }.into()
548    }
549
550    /// Creates an instance with a sapn.
551    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    /// Creates an instance with a documentation string.
565    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    /// Gets the span of the instance.
577    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/// Describes a function parameter attribute.
594#[derive(
595    Debug, Clone, Copy, Hash, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord,
596)]
597pub struct ParamAttrs {
598    /// Whether the parameter is positional.
599    pub positional: bool,
600    /// Whether the parameter is named.
601    ///
602    /// Can be true even if `positional` is true if the parameter can be given
603    /// in both variants.
604    pub named: bool,
605    /// Whether the parameter can be given any number of times.
606    pub variadic: bool,
607    /// Whether the parameter is settable with a set rule.
608    pub settable: bool,
609}
610
611impl ParamAttrs {
612    /// Creates a positional parameter attribute.
613    pub fn positional() -> ParamAttrs {
614        ParamAttrs {
615            positional: true,
616            named: false,
617            variadic: false,
618            settable: false,
619        }
620    }
621
622    /// Creates a named parameter attribute.
623    pub fn named() -> ParamAttrs {
624        ParamAttrs {
625            positional: false,
626            named: true,
627            variadic: false,
628            settable: false,
629        }
630    }
631
632    /// Creates a variadic parameter attribute.
633    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/// Describes a parameter type.
655#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
656pub struct ParamTy {
657    /// The name of the parameter.
658    pub name: StrRef,
659    /// The docstring of the parameter.
660    pub docs: Option<EcoString>,
661    /// The default value of the variable.
662    pub default: Option<EcoString>,
663    /// The type of the parameter.
664    pub ty: Ty,
665    /// The attributes of the parameter.
666    pub attrs: ParamAttrs,
667}
668
669impl ParamTy {
670    /// Creates an untyped field type.
671    pub fn new_untyped(name: StrRef, attrs: ParamAttrs) -> Interned<Self> {
672        Self::new(Ty::Any, name, attrs)
673    }
674
675    /// Creates a typed field type.
676    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/// A type variable.
688#[derive(Hash, Clone, PartialEq, Eq)]
689pub struct TypeVar {
690    /// The name of the type variable.
691    pub name: StrRef,
692    /// The definition id of the type variable.
693    pub def: DeclExpr,
694}
695
696impl Ord for TypeVar {
697    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
698        // todo: buggy
699        self.def.cmp(&other.def)
700    }
701}
702
703impl TypeVar {
704    /// Low-performance comparison but it is free from the concurrency issue.
705    /// This is only used for making stable test snapshots.
706    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    /// Creates a type variable.
725    pub fn new(name: StrRef, def: DeclExpr) -> Interned<Self> {
726        Interned::new(Self { name, def })
727    }
728
729    /// Gets the name of the type variable.
730    pub fn name(&self) -> StrRef {
731        self.name.clone()
732    }
733}
734
735/// A record type.
736#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
737pub struct RecordTy {
738    /// The names of the fields.
739    pub names: Interned<NameBone>,
740    /// The types of the fields.
741    pub types: Interned<Vec<Ty>>,
742}
743
744impl RecordTy {
745    /// Shapes the fields of a record.
746    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    /// Creates a record type.
757    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/// A typst function type.
793#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
794pub struct SigTy {
795    /// The input types of the function.
796    pub inputs: Interned<Vec<Ty>>,
797    /// The return (body) type of the function.
798    pub body: Option<Ty>,
799    /// The name bone of the named parameters.
800    pub names: Interned<NameBone>,
801    /// The index of the first named parameter.
802    pub name_started: u32,
803    /// Whether the function has a spread left parameter.
804    pub spread_left: bool,
805    /// Whether the function has a spread right parameter.
806    pub spread_right: bool,
807}
808
809impl SigTy {
810    /// Creates an function that accepts any arguments: `(a, b: c, ..d)`
811    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    /// Creates an array constructor: `(a)`
824    #[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    /// Creates a unary constructor: `(a) => b`
839    #[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    /// Creates a tuple constructor: `(a, b, c)`
852    #[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    /// Creates a dictionary constructor: `(a: b, c: d)`
871    #[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    /// Sets the return type of the function.
890    pub fn with_body(mut self, res_ty: Ty) -> Self {
891        self.body = Some(res_ty);
892        self
893    }
894
895    /// Creates a function type.
896    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    /// Gets the input types of the function.
961    pub fn inputs(&self) -> impl Iterator<Item = &Ty> {
962        self.inputs.iter()
963    }
964
965    /// Gets the positional parameters of the function.
966    pub fn positional_params(&self) -> impl ExactSizeIterator<Item = &Ty> {
967        self.inputs.iter().take(self.name_started as usize)
968    }
969
970    /// Gets the parameter at the given index.
971    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    /// Gets the parameter or the rest parameter at the given index.
978    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    /// Gets the named parameters of the function.
993    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    /// Gets the named parameter by given name.
1001    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    /// Gets the rest parameter of the function.
1007    pub fn rest_param(&self) -> Option<&Ty> {
1008        if self.spread_right {
1009            self.inputs.last()
1010        } else {
1011            None
1012        }
1013    }
1014
1015    /// Matches the function type with the given arguments.
1016    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
1079/// A function argument type.
1080pub type ArgsTy = SigTy;
1081
1082/// A pattern type.
1083pub type PatternTy = SigTy;
1084
1085/// A type with partially applied arguments.
1086#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1087pub struct SigWithTy {
1088    /// The signature of the function.
1089    pub sig: TyRef,
1090    /// The arguments applied to the function.
1091    pub with: Interned<ArgsTy>,
1092}
1093
1094impl SigWithTy {
1095    /// Creates a type with applied arguments.
1096    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/// A field selection type.
1108#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1109pub struct SelectTy {
1110    /// The type to select from.
1111    pub ty: TyRef,
1112    /// The field to select
1113    pub select: StrRef,
1114}
1115
1116impl SelectTy {
1117    /// Creates a field selection type.
1118    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/// A unary operation type.
1130#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1131pub struct TypeUnary {
1132    /// The operand of the unary operation.
1133    pub lhs: Ty,
1134    /// The kind of the unary operation
1135    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    /// Creates a unary operation type.
1156    pub fn new(op: UnaryOp, lhs: Ty) -> Interned<Self> {
1157        Interned::new(Self { lhs, op })
1158    }
1159
1160    /// Gets the operands of the unary operation.
1161    pub fn operands(&self) -> [&Ty; 1] {
1162        [&self.lhs]
1163    }
1164}
1165
1166/// The kind of binary operation.
1167pub type BinaryOp = ast::BinOp;
1168
1169/// A binary operation type.
1170#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1171pub struct TypeBinary {
1172    /// The operands of the binary operation.
1173    pub operands: (Ty, Ty),
1174    /// The kind of the binary operation.
1175    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    /// Creates a binary operation type.
1196    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    /// Gets the operands of the binary operation.
1204    pub fn operands(&self) -> [&Ty; 2] {
1205        [&self.operands.0, &self.operands.1]
1206    }
1207}
1208
1209/// A conditional type.
1210/// `if t1 then t2 else t3`
1211#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
1212pub struct IfTy {
1213    /// The condition.
1214    pub cond: TyRef,
1215    /// The type when the condition is true.
1216    pub then: TyRef,
1217    /// The type when the condition is false.
1218    pub else_: TyRef,
1219}
1220
1221impl IfTy {
1222    /// Creates a conditional type.
1223    pub fn new(cond: TyRef, then: TyRef, else_: TyRef) -> Interned<Self> {
1224        Interned::new(Self { cond, then, else_ })
1225    }
1226}
1227
1228/// The type information on a group of syntax structures (typing).
1229#[derive(Default)]
1230pub struct TypeInfo {
1231    /// Whether the typing is valid.
1232    pub valid: bool,
1233    /// The belonging file id.
1234    pub fid: Option<FileId>,
1235    /// The used revision.
1236    pub revision: usize,
1237    /// The exported types.
1238    pub exports: FxHashMap<StrRef, Ty>,
1239    /// The typing on definitions.
1240    pub vars: FxHashMap<DeclExpr, TypeVarBounds>,
1241    /// The checked documentation of definitions.
1242    pub var_docs: FxHashMap<DeclExpr, Arc<UntypedDefDocs>>,
1243    /// The local binding of the type variable.
1244    pub local_binds: snapshot_map::SnapshotMap<DeclExpr, Ty>,
1245    /// The typing on syntax structures.
1246    pub mapping: FxHashMap<Span, FxHashSet<Ty>>,
1247    /// The cache to canonicalize types.
1248    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    /// Gets the type of a syntax structure.
1272    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    // todo: distinguish at least, at most
1280    /// Witnesses a lower-bound type on a syntax structure.
1281    pub fn witness_at_least(&mut self, site: Span, ty: Ty) {
1282        Self::witness_(site, ty, &mut self.mapping);
1283    }
1284    /// Witnesses a upper-bound type on a syntax structure.
1285    pub fn witness_at_most(&mut self, site: Span, ty: Ty) {
1286        Self::witness_(site, ty, &mut self.mapping);
1287    }
1288
1289    /// Witnesses a type.
1290    pub fn witness_(site: Span, ty: Ty, mapping: &mut FxHashMap<Span, FxHashSet<Ty>>) {
1291        if site.is_detached() {
1292            return;
1293        }
1294
1295        // todo: intersect/union
1296        mapping.entry(site).or_default().insert(ty);
1297    }
1298
1299    /// Converts a type to a type with bounds.
1300    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/// A type variable bounds.
1363#[derive(Clone)]
1364pub struct TypeVarBounds {
1365    /// The type variable representation.
1366    pub var: Interned<TypeVar>,
1367    /// The bounds of the type variable.
1368    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    /// Creates a type variable bounds.
1379    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    /// Gets the name of the type variable.
1387    pub fn name(&self) -> &StrRef {
1388        &self.var.name
1389    }
1390
1391    /// Gets self as a type.
1392    pub fn as_type(&self) -> Ty {
1393        Ty::Var(self.var.clone())
1394    }
1395
1396    /// Slightly closes the type variable.
1397    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/// A type variable bounds.
1408#[derive(Clone)]
1409pub enum FlowVarKind {
1410    /// A type variable that receives both types and values (type instances).
1411    Strong(Arc<RwLock<DynTypeBounds>>),
1412    /// A type variable that receives only types.
1413    /// The received values will be lifted to types.
1414    Weak(Arc<RwLock<DynTypeBounds>>),
1415}
1416
1417impl FlowVarKind {
1418    /// Gets the bounds of the type variable.
1419    pub fn bounds(&self) -> &RwLock<DynTypeBounds> {
1420        match self {
1421            FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => w,
1422        }
1423    }
1424}
1425
1426/// A cache to canonicalize types.
1427#[derive(Default)]
1428pub(super) struct TypeCanoStore {
1429    /// Maps a type to its canonical form.
1430    pub cano_cache: FxHashMap<(Ty, bool), Ty>,
1431    /// Maps a local type to its canonical form.
1432    pub cano_local_cache: FxHashMap<(DeclExpr, bool), Ty>,
1433    /// The negative bounds of a type variable.
1434    pub negatives: FxHashSet<DeclExpr>,
1435    /// The positive bounds of a type variable.
1436    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}