1#![warn(missing_docs)]
58
59pub mod dyn_tensor;
60pub mod nat;
61pub mod ty_list;
62
63pub use dyn_tensor::{dyn_tensor_of, dyn_tensor_tycon, shape_witness_of, shape_witness_tycon};
64pub use nat::TyNat;
65pub use ty_list::TyList;
66
67use bhc_index::Idx;
68use bhc_intern::Symbol;
69use bhc_span::Span;
70use serde::{Deserialize, Serialize};
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
77pub struct TyId(u32);
78
79impl Idx for TyId {
80 #[allow(clippy::cast_possible_truncation)]
81 fn new(idx: usize) -> Self {
82 Self(idx as u32)
83 }
84
85 fn index(self) -> usize {
86 self.0 as usize
87 }
88}
89
90#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
96pub struct TyVar {
97 pub id: u32,
99 pub kind: Kind,
101}
102
103impl TyVar {
104 #[must_use]
106 pub fn new(id: u32, kind: Kind) -> Self {
107 Self { id, kind }
108 }
109
110 #[must_use]
112 pub fn new_star(id: u32) -> Self {
113 Self::new(id, Kind::Star)
114 }
115}
116
117#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
125pub enum Kind {
126 Star,
128 Arrow(Box<Kind>, Box<Kind>),
130 Constraint,
132 Var(u32),
134
135 Nat,
143
144 List(Box<Kind>),
151}
152
153impl Kind {
154 #[must_use]
156 pub fn star_to_star() -> Self {
157 Self::Arrow(Box::new(Self::Star), Box::new(Self::Star))
158 }
159
160 #[must_use]
162 pub fn is_star(&self) -> bool {
163 matches!(self, Self::Star)
164 }
165
166 #[must_use]
168 pub fn is_nat(&self) -> bool {
169 matches!(self, Self::Nat)
170 }
171
172 #[must_use]
174 pub fn nat_list() -> Self {
175 Self::List(Box::new(Self::Nat))
176 }
177
178 #[must_use]
180 pub fn tensor_kind() -> Self {
181 Self::Arrow(Box::new(Self::nat_list()), Box::new(Self::star_to_star()))
183 }
184
185 #[must_use]
187 pub fn is_list(&self) -> bool {
188 matches!(self, Self::List(_))
189 }
190
191 #[must_use]
193 pub fn list_elem_kind(&self) -> Option<&Kind> {
194 match self {
195 Self::List(k) => Some(k),
196 _ => None,
197 }
198 }
199}
200
201#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
206pub enum Ty {
207 Var(TyVar),
209
210 Con(TyCon),
212
213 Prim(PrimTy),
216
217 App(Box<Ty>, Box<Ty>),
219
220 Fun(Box<Ty>, Box<Ty>),
223
224 Tuple(Vec<Ty>),
226
227 List(Box<Ty>),
230
231 Forall(Vec<TyVar>, Box<Ty>),
233
234 Error,
236
237 Nat(TyNat),
242
243 TyList(TyList),
247}
248
249impl Ty {
250 #[must_use]
252 pub fn unit() -> Self {
253 Self::Tuple(Vec::new())
254 }
255
256 #[must_use]
258 pub fn fun(from: Ty, to: Ty) -> Self {
259 Self::Fun(Box::new(from), Box::new(to))
260 }
261
262 #[must_use]
264 pub fn list(elem: Ty) -> Self {
265 Self::List(Box::new(elem))
266 }
267
268 #[must_use]
270 pub fn int_prim() -> Self {
271 Self::Prim(PrimTy::I64)
272 }
273
274 #[must_use]
276 pub fn double_prim() -> Self {
277 Self::Prim(PrimTy::F64)
278 }
279
280 #[must_use]
282 pub fn float_prim() -> Self {
283 Self::Prim(PrimTy::F32)
284 }
285
286 #[must_use]
288 pub fn is_prim(&self) -> bool {
289 matches!(self, Self::Prim(_))
290 }
291
292 #[must_use]
294 pub fn as_prim(&self) -> Option<PrimTy> {
295 match self {
296 Self::Prim(p) => Some(*p),
297 _ => None,
298 }
299 }
300
301 #[must_use]
303 pub fn is_fun(&self) -> bool {
304 matches!(self, Self::Fun(_, _))
305 }
306
307 #[must_use]
309 pub fn is_error(&self) -> bool {
310 matches!(self, Self::Error)
311 }
312
313 #[must_use]
315 pub fn free_vars(&self) -> Vec<TyVar> {
316 let mut vars = Vec::new();
317 self.collect_free_vars(&mut vars);
318 vars
319 }
320
321 fn collect_free_vars(&self, vars: &mut Vec<TyVar>) {
322 match self {
323 Self::Var(v) => {
324 if !vars.contains(v) {
325 vars.push(v.clone());
326 }
327 }
328 Self::Con(_) | Self::Prim(_) | Self::Error => {}
329 Self::App(f, a) | Self::Fun(f, a) => {
330 f.collect_free_vars(vars);
331 a.collect_free_vars(vars);
332 }
333 Self::Tuple(tys) => {
334 for ty in tys {
335 ty.collect_free_vars(vars);
336 }
337 }
338 Self::List(elem) => elem.collect_free_vars(vars),
339 Self::Forall(bound, body) => {
340 let mut body_vars = Vec::new();
341 body.collect_free_vars(&mut body_vars);
342 for v in body_vars {
343 if !bound.contains(&v) && !vars.contains(&v) {
344 vars.push(v);
345 }
346 }
347 }
348 Self::Nat(n) => {
350 for v in n.free_vars() {
351 if !vars.contains(&v) {
352 vars.push(v);
353 }
354 }
355 }
356 Self::TyList(l) => {
357 for v in l.free_vars() {
358 if !vars.contains(&v) {
359 vars.push(v);
360 }
361 }
362 }
363 }
364 }
365
366 #[must_use]
368 pub fn is_ground(&self) -> bool {
369 match self {
370 Self::Var(_) => false,
371 Self::Con(_) | Self::Prim(_) | Self::Error => true,
372 Self::App(f, a) | Self::Fun(f, a) => f.is_ground() && a.is_ground(),
373 Self::Tuple(tys) => tys.iter().all(Ty::is_ground),
374 Self::List(elem) => elem.is_ground(),
375 Self::Forall(_, body) => body.is_ground(),
376 Self::Nat(n) => n.is_ground(),
377 Self::TyList(l) => l.is_ground(),
378 }
379 }
380
381 #[must_use]
383 pub fn nat_lit(n: u64) -> Self {
384 Self::Nat(TyNat::lit(n))
385 }
386
387 #[must_use]
389 pub fn shape(dims: &[u64]) -> Self {
390 Self::TyList(TyList::shape_from_dims(dims))
391 }
392
393 #[must_use]
395 pub fn is_nat(&self) -> bool {
396 matches!(self, Self::Nat(_))
397 }
398
399 #[must_use]
401 pub fn is_ty_list(&self) -> bool {
402 matches!(self, Self::TyList(_))
403 }
404
405 #[must_use]
407 pub fn as_nat(&self) -> Option<&TyNat> {
408 match self {
409 Self::Nat(n) => Some(n),
410 _ => None,
411 }
412 }
413
414 #[must_use]
416 pub fn as_ty_list(&self) -> Option<&TyList> {
417 match self {
418 Self::TyList(l) => Some(l),
419 _ => None,
420 }
421 }
422}
423
424impl std::fmt::Display for Ty {
425 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426 match self {
427 Self::Var(v) => write!(f, "t{}", v.id),
428 Self::Con(c) => write!(f, "{}", c.name.as_str()),
429 Self::Prim(p) => write!(f, "{p}"),
430 Self::App(fun, arg) => write!(f, "({fun} {arg})"),
431 Self::Fun(from, to) => write!(f, "({from} -> {to})"),
432 Self::Tuple(tys) => {
433 write!(f, "(")?;
434 for (i, ty) in tys.iter().enumerate() {
435 if i > 0 {
436 write!(f, ", ")?;
437 }
438 write!(f, "{ty}")?;
439 }
440 write!(f, ")")
441 }
442 Self::List(elem) => write!(f, "[{elem}]"),
443 Self::Forall(vars, body) => {
444 write!(f, "forall")?;
445 for v in vars {
446 write!(f, " t{}", v.id)?;
447 }
448 write!(f, ". {body}")
449 }
450 Self::Error => write!(f, "<error>"),
451 Self::Nat(n) => write!(f, "{n}"),
452 Self::TyList(l) => write!(f, "{l}"),
453 }
454 }
455}
456
457#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
459pub struct TyCon {
460 pub name: Symbol,
462 pub kind: Kind,
464}
465
466impl TyCon {
467 #[must_use]
469 pub fn new(name: Symbol, kind: Kind) -> Self {
470 Self { name, kind }
471 }
472}
473
474#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
480pub struct Scheme {
481 pub vars: Vec<TyVar>,
483 pub constraints: Vec<Constraint>,
485 pub ty: Ty,
487}
488
489impl Scheme {
490 #[must_use]
492 pub fn mono(ty: Ty) -> Self {
493 Self {
494 vars: Vec::new(),
495 constraints: Vec::new(),
496 ty,
497 }
498 }
499
500 #[must_use]
502 pub fn poly(vars: Vec<TyVar>, ty: Ty) -> Self {
503 Self {
504 vars,
505 constraints: Vec::new(),
506 ty,
507 }
508 }
509
510 #[must_use]
512 pub fn qualified(vars: Vec<TyVar>, constraints: Vec<Constraint>, ty: Ty) -> Self {
513 Self {
514 vars,
515 constraints,
516 ty,
517 }
518 }
519
520 #[must_use]
522 pub fn is_mono(&self) -> bool {
523 self.vars.is_empty()
524 }
525}
526
527#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
529pub struct Constraint {
530 pub class: Symbol,
532 pub args: Vec<Ty>,
534 pub span: Span,
536}
537
538impl Constraint {
539 #[must_use]
541 pub fn new(class: Symbol, ty: Ty, span: Span) -> Self {
542 Self {
543 class,
544 args: vec![ty],
545 span,
546 }
547 }
548
549 #[must_use]
551 pub fn new_multi(class: Symbol, args: Vec<Ty>, span: Span) -> Self {
552 Self { class, args, span }
553 }
554}
555
556#[derive(Clone, Debug, Default, Serialize, Deserialize)]
558pub struct Subst {
559 mapping: rustc_hash::FxHashMap<u32, Ty>,
561}
562
563impl Subst {
564 #[must_use]
566 pub fn new() -> Self {
567 Self::default()
568 }
569
570 pub fn insert(&mut self, var: &TyVar, ty: Ty) {
572 self.mapping.insert(var.id, ty);
573 }
574
575 #[must_use]
577 pub fn get(&self, var: &TyVar) -> Option<&Ty> {
578 self.mapping.get(&var.id)
579 }
580
581 #[must_use]
583 pub fn contains(&self, var: &TyVar) -> bool {
584 self.mapping.contains_key(&var.id)
585 }
586
587 #[must_use]
589 pub fn apply(&self, ty: &Ty) -> Ty {
590 match ty {
591 Ty::Var(v) => self.get(v).cloned().unwrap_or_else(|| ty.clone()),
592 Ty::Con(_) | Ty::Prim(_) => ty.clone(),
593 Ty::App(f, a) => Ty::App(Box::new(self.apply(f)), Box::new(self.apply(a))),
594 Ty::Fun(from, to) => Ty::Fun(Box::new(self.apply(from)), Box::new(self.apply(to))),
595 Ty::Tuple(tys) => Ty::Tuple(tys.iter().map(|t| self.apply(t)).collect()),
596 Ty::List(elem) => Ty::List(Box::new(self.apply(elem))),
597 Ty::Forall(vars, body) => {
598 let mut inner = self.clone();
600 for v in vars {
601 inner.mapping.remove(&v.id);
602 }
603 Ty::Forall(vars.clone(), Box::new(inner.apply(body)))
604 }
605 Ty::Error => Ty::Error,
606 Ty::Nat(n) => Ty::Nat(self.apply_nat(n)),
608 Ty::TyList(l) => Ty::TyList(self.apply_ty_list(l)),
609 }
610 }
611
612 #[must_use]
614 pub fn apply_nat(&self, n: &TyNat) -> TyNat {
615 match n {
616 TyNat::Lit(val) => TyNat::Lit(*val),
617 TyNat::Var(v) => {
618 match self.get(v) {
620 Some(Ty::Nat(replacement)) => replacement.clone(),
621 Some(_) => n.clone(), None => n.clone(),
623 }
624 }
625 TyNat::Add(a, b) => TyNat::add(self.apply_nat(a), self.apply_nat(b)),
626 TyNat::Mul(a, b) => TyNat::mul(self.apply_nat(a), self.apply_nat(b)),
627 }
628 }
629
630 #[must_use]
632 pub fn apply_ty_list(&self, l: &TyList) -> TyList {
633 match l {
634 TyList::Nil => TyList::Nil,
635 TyList::Cons(head, tail) => TyList::cons(self.apply(head), self.apply_ty_list(tail)),
636 TyList::Var(v) => {
637 match self.get(v) {
639 Some(Ty::TyList(replacement)) => replacement.clone(),
640 Some(_) => l.clone(), None => l.clone(),
642 }
643 }
644 TyList::Append(xs, ys) => {
645 TyList::append(self.apply_ty_list(xs), self.apply_ty_list(ys))
646 }
647 }
648 }
649
650 #[must_use]
652 pub fn compose(&self, other: &Self) -> Self {
653 let mut result = Self::new();
654 for (k, v) in &other.mapping {
655 result.mapping.insert(*k, self.apply(v));
656 }
657 for (k, v) in &self.mapping {
658 result.mapping.entry(*k).or_insert_with(|| v.clone());
659 }
660 result
661 }
662
663 #[must_use]
665 pub fn is_empty(&self) -> bool {
666 self.mapping.is_empty()
667 }
668}
669
670#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
680pub enum PrimTy {
681 I32,
683 I64,
685 U32,
687 U64,
689 F32,
691 F64,
693 Char,
695 Addr,
697}
698
699impl PrimTy {
700 #[must_use]
702 pub const fn size_bytes(self) -> usize {
703 match self {
704 Self::I32 | Self::U32 | Self::F32 => 4,
705 Self::I64 | Self::U64 | Self::F64 | Self::Addr => 8,
706 Self::Char => 4, }
708 }
709
710 #[must_use]
712 pub const fn alignment(self) -> usize {
713 self.size_bytes()
714 }
715
716 #[must_use]
718 pub const fn name(self) -> &'static str {
719 match self {
720 Self::I32 => "Int32#",
721 Self::I64 => "Int#",
722 Self::U32 => "Word32#",
723 Self::U64 => "Word#",
724 Self::F32 => "Float#",
725 Self::F64 => "Double#",
726 Self::Char => "Char#",
727 Self::Addr => "Addr#",
728 }
729 }
730
731 #[must_use]
733 pub const fn is_signed_int(self) -> bool {
734 matches!(self, Self::I32 | Self::I64)
735 }
736
737 #[must_use]
739 pub const fn is_unsigned_int(self) -> bool {
740 matches!(self, Self::U32 | Self::U64)
741 }
742
743 #[must_use]
745 pub const fn is_float(self) -> bool {
746 matches!(self, Self::F32 | Self::F64)
747 }
748
749 #[must_use]
751 pub const fn is_numeric(self) -> bool {
752 matches!(
753 self,
754 Self::I32 | Self::I64 | Self::U32 | Self::U64 | Self::F32 | Self::F64
755 )
756 }
757}
758
759impl std::fmt::Display for PrimTy {
760 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
761 write!(f, "{}", self.name())
762 }
763}
764
765#[must_use]
779pub fn types_match(pattern: &Ty, target: &Ty) -> Option<Subst> {
780 let mut subst = Subst::new();
781 if types_match_with_subst(pattern, target, &mut subst) {
782 Some(subst)
783 } else {
784 None
785 }
786}
787
788pub fn types_match_with_subst(pattern: &Ty, target: &Ty, subst: &mut Subst) -> bool {
793 match (pattern, target) {
794 (Ty::Var(v), _) => {
796 if let Some(bound_ty) = subst.get(v) {
797 types_equal(bound_ty, target)
799 } else {
800 subst.insert(v, target.clone());
801 true
802 }
803 }
804
805 (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
807
808 (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
810
811 (Ty::App(f1, a1), Ty::App(f2, a2)) => {
813 types_match_with_subst(f1, f2, subst) && types_match_with_subst(a1, a2, subst)
814 }
815
816 (Ty::Fun(a1, r1), Ty::Fun(a2, r2)) => {
818 types_match_with_subst(a1, a2, subst) && types_match_with_subst(r1, r2, subst)
819 }
820
821 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
823 .iter()
824 .zip(ts2.iter())
825 .all(|(t1, t2)| types_match_with_subst(t1, t2, subst)),
826
827 (Ty::List(e1), Ty::List(e2)) => types_match_with_subst(e1, e2, subst),
829
830 (Ty::Forall(_, body1), Ty::Forall(_, body2)) => types_match_with_subst(body1, body2, subst),
832
833 (Ty::Nat(n1), Ty::Nat(n2)) => n1 == n2,
835
836 (Ty::TyList(l1), Ty::TyList(l2)) => l1 == l2,
838
839 (Ty::Error, _) | (_, Ty::Error) => true,
841
842 _ => false,
844 }
845}
846
847#[must_use]
852pub fn types_match_multi(patterns: &[Ty], targets: &[Ty]) -> Option<Subst> {
853 if patterns.len() != targets.len() {
854 return None;
855 }
856
857 let mut subst = Subst::new();
858 for (pattern, target) in patterns.iter().zip(targets.iter()) {
859 if !types_match_with_subst(pattern, target, &mut subst) {
860 return None;
861 }
862 }
863 Some(subst)
864}
865
866#[must_use]
868pub fn types_equal(t1: &Ty, t2: &Ty) -> bool {
869 match (t1, t2) {
870 (Ty::Var(v1), Ty::Var(v2)) => v1.id == v2.id,
871 (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
872 (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
873 (Ty::App(f1, a1), Ty::App(f2, a2)) => types_equal(f1, f2) && types_equal(a1, a2),
874 (Ty::Fun(a1, r1), Ty::Fun(a2, r2)) => types_equal(a1, a2) && types_equal(r1, r2),
875 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
876 .iter()
877 .zip(ts2.iter())
878 .all(|(t1, t2)| types_equal(t1, t2)),
879 (Ty::List(e1), Ty::List(e2)) => types_equal(e1, e2),
880 (Ty::Error, Ty::Error) => true,
881 _ => false,
882 }
883}
884
885#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
887pub enum TypeError {
888 #[error("type mismatch: expected {expected}, found {found}")]
890 Mismatch {
891 expected: String,
893 found: String,
895 span: Span,
897 },
898
899 #[error("infinite type: {var} occurs in {ty}")]
901 OccursCheck {
902 var: String,
904 ty: String,
906 span: Span,
908 },
909
910 #[error("unbound type variable: {name}")]
912 UnboundVar {
913 name: String,
915 span: Span,
917 },
918
919 #[error("kind mismatch: expected {expected}, found {found}")]
921 KindMismatch {
922 expected: String,
924 found: String,
926 span: Span,
928 },
929
930 #[error("ambiguous type variable: {var}")]
932 Ambiguous {
933 var: String,
935 span: Span,
937 },
938}
939
940#[cfg(test)]
941mod tests {
942 use super::*;
943
944 #[test]
945 fn test_ty_free_vars() {
946 let a = TyVar::new_star(0);
947 let b = TyVar::new_star(1);
948
949 let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(b.clone()));
951 let vars = ty.free_vars();
952 assert_eq!(vars.len(), 2);
953 assert!(vars.contains(&a));
954 assert!(vars.contains(&b));
955 }
956
957 #[test]
958 fn test_subst_apply() {
959 let a = TyVar::new_star(0);
960 let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
962
963 let mut subst = Subst::new();
964 subst.insert(&a, Ty::Con(int_con.clone()));
965
966 let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(a));
967 let result = subst.apply(&ty);
968
969 match result {
970 Ty::Fun(from, to) => {
971 assert!(matches!(*from, Ty::Con(_)));
972 assert!(matches!(*to, Ty::Con(_)));
973 }
974 _ => panic!("expected function type"),
975 }
976 }
977
978 #[test]
979 fn test_scheme_mono() {
980 let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
982 let scheme = Scheme::mono(Ty::Con(int_con));
983 assert!(scheme.is_mono());
984 }
985
986 #[test]
987 fn test_prim_ty_properties() {
988 assert_eq!(PrimTy::I64.size_bytes(), 8);
989 assert_eq!(PrimTy::I32.size_bytes(), 4);
990 assert_eq!(PrimTy::F64.size_bytes(), 8);
991 assert_eq!(PrimTy::F32.size_bytes(), 4);
992
993 assert!(PrimTy::I64.is_signed_int());
994 assert!(PrimTy::I32.is_signed_int());
995 assert!(!PrimTy::U64.is_signed_int());
996
997 assert!(PrimTy::F64.is_float());
998 assert!(PrimTy::F32.is_float());
999 assert!(!PrimTy::I64.is_float());
1000
1001 assert!(PrimTy::I64.is_numeric());
1002 assert!(PrimTy::F64.is_numeric());
1003 assert!(!PrimTy::Char.is_numeric());
1004 assert!(!PrimTy::Addr.is_numeric());
1005 }
1006
1007 #[test]
1008 fn test_prim_ty_names() {
1009 assert_eq!(PrimTy::I64.name(), "Int#");
1010 assert_eq!(PrimTy::F64.name(), "Double#");
1011 assert_eq!(PrimTy::F32.name(), "Float#");
1012 assert_eq!(PrimTy::Char.name(), "Char#");
1013 }
1014
1015 #[test]
1016 fn test_ty_prim_constructors() {
1017 let int = Ty::int_prim();
1018 assert!(int.is_prim());
1019 assert_eq!(int.as_prim(), Some(PrimTy::I64));
1020
1021 let double = Ty::double_prim();
1022 assert!(double.is_prim());
1023 assert_eq!(double.as_prim(), Some(PrimTy::F64));
1024
1025 let unit = Ty::unit();
1027 assert!(!unit.is_prim());
1028 assert_eq!(unit.as_prim(), None);
1029 }
1030
1031 #[test]
1032 fn test_prim_ty_no_free_vars() {
1033 let prim = Ty::int_prim();
1035 assert!(prim.free_vars().is_empty());
1036 }
1037
1038 #[test]
1039 fn test_subst_prim_unchanged() {
1040 let a = TyVar::new_star(0);
1042 let mut subst = Subst::new();
1043 subst.insert(&a, Ty::unit());
1044
1045 let prim = Ty::int_prim();
1046 let result = subst.apply(&prim);
1047 assert_eq!(result, prim);
1048 }
1049
1050 #[test]
1051 fn test_types_match_variable_binding() {
1052 let a = TyVar::new_star(0);
1053 let int_con = TyCon::new(Symbol::intern("Int"), Kind::Star);
1054 let int_ty = Ty::Con(int_con);
1055
1056 let result = types_match(&Ty::Var(a.clone()), &int_ty);
1057 assert!(result.is_some());
1058 let subst = result.unwrap();
1059 assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1060 }
1061
1062 #[test]
1063 fn test_types_match_constructor() {
1064 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1065 let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1066
1067 assert!(types_match(&int_ty, &int_ty).is_some());
1068 assert!(types_match(&int_ty, &bool_ty).is_none());
1069 }
1070
1071 #[test]
1072 fn test_types_match_application() {
1073 let a = TyVar::new_star(0);
1074 let list_con = Ty::Con(TyCon::new(Symbol::intern("[]"), Kind::star_to_star()));
1075 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1076
1077 let pattern = Ty::App(Box::new(list_con.clone()), Box::new(Ty::Var(a.clone())));
1079 let target = Ty::App(Box::new(list_con), Box::new(int_ty.clone()));
1080
1081 let result = types_match(&pattern, &target);
1082 assert!(result.is_some());
1083 let subst = result.unwrap();
1084 assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1085 }
1086
1087 #[test]
1088 fn test_types_match_multi_basic() {
1089 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1090 let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1091
1092 assert!(
1093 types_match_multi(std::slice::from_ref(&int_ty), std::slice::from_ref(&int_ty))
1094 .is_some()
1095 );
1096 assert!(types_match_multi(std::slice::from_ref(&int_ty), &[bool_ty]).is_none());
1097 assert!(types_match_multi(&[], &[]).is_some());
1098 assert!(types_match_multi(std::slice::from_ref(&int_ty), &[]).is_none());
1099 }
1100}