1#![warn(missing_docs)]
58#![warn(clippy::all)]
59#![warn(clippy::pedantic)]
60
61pub mod dyn_tensor;
62pub mod nat;
63pub mod ty_list;
64
65pub use dyn_tensor::{dyn_tensor_of, dyn_tensor_tycon, shape_witness_of, shape_witness_tycon};
66pub use nat::TyNat;
67pub use ty_list::TyList;
68
69use bhc_index::Idx;
70use bhc_intern::Symbol;
71use bhc_span::Span;
72use serde::{Deserialize, Serialize};
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
79pub struct TyId(u32);
80
81impl Idx for TyId {
82 #[allow(clippy::cast_possible_truncation)]
83 fn new(idx: usize) -> Self {
84 Self(idx as u32)
85 }
86
87 fn index(self) -> usize {
88 self.0 as usize
89 }
90}
91
92#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
98pub struct TyVar {
99 pub id: u32,
101 pub kind: Kind,
103}
104
105impl TyVar {
106 #[must_use]
108 pub fn new(id: u32, kind: Kind) -> Self {
109 Self { id, kind }
110 }
111
112 #[must_use]
114 pub fn new_star(id: u32) -> Self {
115 Self::new(id, Kind::Star)
116 }
117}
118
119#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
127pub enum Kind {
128 Star,
130 Arrow(Box<Kind>, Box<Kind>),
132 Constraint,
134 Var(u32),
136
137 Nat,
145
146 List(Box<Kind>),
153}
154
155impl Kind {
156 #[must_use]
158 pub fn star_to_star() -> Self {
159 Self::Arrow(Box::new(Self::Star), Box::new(Self::Star))
160 }
161
162 #[must_use]
164 pub fn is_star(&self) -> bool {
165 matches!(self, Self::Star)
166 }
167
168 #[must_use]
170 pub fn is_nat(&self) -> bool {
171 matches!(self, Self::Nat)
172 }
173
174 #[must_use]
176 pub fn nat_list() -> Self {
177 Self::List(Box::new(Self::Nat))
178 }
179
180 #[must_use]
182 pub fn tensor_kind() -> Self {
183 Self::Arrow(Box::new(Self::nat_list()), Box::new(Self::star_to_star()))
185 }
186
187 #[must_use]
189 pub fn is_list(&self) -> bool {
190 matches!(self, Self::List(_))
191 }
192
193 #[must_use]
195 pub fn list_elem_kind(&self) -> Option<&Kind> {
196 match self {
197 Self::List(k) => Some(k),
198 _ => None,
199 }
200 }
201}
202
203#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
208pub enum Ty {
209 Var(TyVar),
211
212 Con(TyCon),
214
215 Prim(PrimTy),
218
219 App(Box<Ty>, Box<Ty>),
221
222 Fun(Box<Ty>, Box<Ty>),
225
226 Tuple(Vec<Ty>),
228
229 List(Box<Ty>),
232
233 Forall(Vec<TyVar>, Box<Ty>),
235
236 Error,
238
239 Nat(TyNat),
244
245 TyList(TyList),
249}
250
251impl Ty {
252 #[must_use]
254 pub fn unit() -> Self {
255 Self::Tuple(Vec::new())
256 }
257
258 #[must_use]
260 pub fn fun(from: Ty, to: Ty) -> Self {
261 Self::Fun(Box::new(from), Box::new(to))
262 }
263
264 #[must_use]
266 pub fn list(elem: Ty) -> Self {
267 Self::List(Box::new(elem))
268 }
269
270 #[must_use]
272 pub fn int_prim() -> Self {
273 Self::Prim(PrimTy::I64)
274 }
275
276 #[must_use]
278 pub fn double_prim() -> Self {
279 Self::Prim(PrimTy::F64)
280 }
281
282 #[must_use]
284 pub fn float_prim() -> Self {
285 Self::Prim(PrimTy::F32)
286 }
287
288 #[must_use]
290 pub fn is_prim(&self) -> bool {
291 matches!(self, Self::Prim(_))
292 }
293
294 #[must_use]
296 pub fn as_prim(&self) -> Option<PrimTy> {
297 match self {
298 Self::Prim(p) => Some(*p),
299 _ => None,
300 }
301 }
302
303 #[must_use]
305 pub fn is_fun(&self) -> bool {
306 matches!(self, Self::Fun(_, _))
307 }
308
309 #[must_use]
311 pub fn is_error(&self) -> bool {
312 matches!(self, Self::Error)
313 }
314
315 #[must_use]
317 pub fn free_vars(&self) -> Vec<TyVar> {
318 let mut vars = Vec::new();
319 self.collect_free_vars(&mut vars);
320 vars
321 }
322
323 fn collect_free_vars(&self, vars: &mut Vec<TyVar>) {
324 match self {
325 Self::Var(v) => {
326 if !vars.contains(v) {
327 vars.push(v.clone());
328 }
329 }
330 Self::Con(_) | Self::Prim(_) | Self::Error => {}
331 Self::App(f, a) | Self::Fun(f, a) => {
332 f.collect_free_vars(vars);
333 a.collect_free_vars(vars);
334 }
335 Self::Tuple(tys) => {
336 for ty in tys {
337 ty.collect_free_vars(vars);
338 }
339 }
340 Self::List(elem) => elem.collect_free_vars(vars),
341 Self::Forall(bound, body) => {
342 let mut body_vars = Vec::new();
343 body.collect_free_vars(&mut body_vars);
344 for v in body_vars {
345 if !bound.contains(&v) && !vars.contains(&v) {
346 vars.push(v);
347 }
348 }
349 }
350 Self::Nat(n) => {
352 for v in n.free_vars() {
353 if !vars.contains(&v) {
354 vars.push(v);
355 }
356 }
357 }
358 Self::TyList(l) => {
359 for v in l.free_vars() {
360 if !vars.contains(&v) {
361 vars.push(v);
362 }
363 }
364 }
365 }
366 }
367
368 #[must_use]
370 pub fn is_ground(&self) -> bool {
371 match self {
372 Self::Var(_) => false,
373 Self::Con(_) | Self::Prim(_) | Self::Error => true,
374 Self::App(f, a) | Self::Fun(f, a) => f.is_ground() && a.is_ground(),
375 Self::Tuple(tys) => tys.iter().all(Ty::is_ground),
376 Self::List(elem) => elem.is_ground(),
377 Self::Forall(_, body) => body.is_ground(),
378 Self::Nat(n) => n.is_ground(),
379 Self::TyList(l) => l.is_ground(),
380 }
381 }
382
383 #[must_use]
385 pub fn nat_lit(n: u64) -> Self {
386 Self::Nat(TyNat::lit(n))
387 }
388
389 #[must_use]
391 pub fn shape(dims: &[u64]) -> Self {
392 Self::TyList(TyList::shape_from_dims(dims))
393 }
394
395 #[must_use]
397 pub fn is_nat(&self) -> bool {
398 matches!(self, Self::Nat(_))
399 }
400
401 #[must_use]
403 pub fn is_ty_list(&self) -> bool {
404 matches!(self, Self::TyList(_))
405 }
406
407 #[must_use]
409 pub fn as_nat(&self) -> Option<&TyNat> {
410 match self {
411 Self::Nat(n) => Some(n),
412 _ => None,
413 }
414 }
415
416 #[must_use]
418 pub fn as_ty_list(&self) -> Option<&TyList> {
419 match self {
420 Self::TyList(l) => Some(l),
421 _ => None,
422 }
423 }
424}
425
426impl std::fmt::Display for Ty {
427 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428 match self {
429 Self::Var(v) => write!(f, "t{}", v.id),
430 Self::Con(c) => write!(f, "{}", c.name.as_str()),
431 Self::Prim(p) => write!(f, "{p}"),
432 Self::App(fun, arg) => write!(f, "({fun} {arg})"),
433 Self::Fun(from, to) => write!(f, "({from} -> {to})"),
434 Self::Tuple(tys) => {
435 write!(f, "(")?;
436 for (i, ty) in tys.iter().enumerate() {
437 if i > 0 {
438 write!(f, ", ")?;
439 }
440 write!(f, "{ty}")?;
441 }
442 write!(f, ")")
443 }
444 Self::List(elem) => write!(f, "[{elem}]"),
445 Self::Forall(vars, body) => {
446 write!(f, "forall")?;
447 for v in vars {
448 write!(f, " t{}", v.id)?;
449 }
450 write!(f, ". {body}")
451 }
452 Self::Error => write!(f, "<error>"),
453 Self::Nat(n) => write!(f, "{n}"),
454 Self::TyList(l) => write!(f, "{l}"),
455 }
456 }
457}
458
459#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
461pub struct TyCon {
462 pub name: Symbol,
464 pub kind: Kind,
466}
467
468impl TyCon {
469 #[must_use]
471 pub fn new(name: Symbol, kind: Kind) -> Self {
472 Self { name, kind }
473 }
474}
475
476#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
482pub struct Scheme {
483 pub vars: Vec<TyVar>,
485 pub constraints: Vec<Constraint>,
487 pub ty: Ty,
489}
490
491impl Scheme {
492 #[must_use]
494 pub fn mono(ty: Ty) -> Self {
495 Self {
496 vars: Vec::new(),
497 constraints: Vec::new(),
498 ty,
499 }
500 }
501
502 #[must_use]
504 pub fn poly(vars: Vec<TyVar>, ty: Ty) -> Self {
505 Self {
506 vars,
507 constraints: Vec::new(),
508 ty,
509 }
510 }
511
512 #[must_use]
514 pub fn qualified(vars: Vec<TyVar>, constraints: Vec<Constraint>, ty: Ty) -> Self {
515 Self {
516 vars,
517 constraints,
518 ty,
519 }
520 }
521
522 #[must_use]
524 pub fn is_mono(&self) -> bool {
525 self.vars.is_empty()
526 }
527}
528
529#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
531pub struct Constraint {
532 pub class: Symbol,
534 pub args: Vec<Ty>,
536 pub span: Span,
538}
539
540impl Constraint {
541 #[must_use]
543 pub fn new(class: Symbol, ty: Ty, span: Span) -> Self {
544 Self {
545 class,
546 args: vec![ty],
547 span,
548 }
549 }
550
551 #[must_use]
553 pub fn new_multi(class: Symbol, args: Vec<Ty>, span: Span) -> Self {
554 Self { class, args, span }
555 }
556}
557
558#[derive(Clone, Debug, Default, Serialize, Deserialize)]
560pub struct Subst {
561 mapping: rustc_hash::FxHashMap<u32, Ty>,
563}
564
565impl Subst {
566 #[must_use]
568 pub fn new() -> Self {
569 Self::default()
570 }
571
572 pub fn insert(&mut self, var: &TyVar, ty: Ty) {
574 self.mapping.insert(var.id, ty);
575 }
576
577 #[must_use]
579 pub fn get(&self, var: &TyVar) -> Option<&Ty> {
580 self.mapping.get(&var.id)
581 }
582
583 #[must_use]
585 pub fn contains(&self, var: &TyVar) -> bool {
586 self.mapping.contains_key(&var.id)
587 }
588
589 #[must_use]
591 pub fn apply(&self, ty: &Ty) -> Ty {
592 match ty {
593 Ty::Var(v) => self.get(v).cloned().unwrap_or_else(|| ty.clone()),
594 Ty::Con(_) | Ty::Prim(_) => ty.clone(),
595 Ty::App(f, a) => Ty::App(Box::new(self.apply(f)), Box::new(self.apply(a))),
596 Ty::Fun(from, to) => Ty::Fun(Box::new(self.apply(from)), Box::new(self.apply(to))),
597 Ty::Tuple(tys) => Ty::Tuple(tys.iter().map(|t| self.apply(t)).collect()),
598 Ty::List(elem) => Ty::List(Box::new(self.apply(elem))),
599 Ty::Forall(vars, body) => {
600 let mut inner = self.clone();
602 for v in vars {
603 inner.mapping.remove(&v.id);
604 }
605 Ty::Forall(vars.clone(), Box::new(inner.apply(body)))
606 }
607 Ty::Error => Ty::Error,
608 Ty::Nat(n) => Ty::Nat(self.apply_nat(n)),
610 Ty::TyList(l) => Ty::TyList(self.apply_ty_list(l)),
611 }
612 }
613
614 #[must_use]
616 pub fn apply_nat(&self, n: &TyNat) -> TyNat {
617 match n {
618 TyNat::Lit(val) => TyNat::Lit(*val),
619 TyNat::Var(v) => {
620 match self.get(v) {
622 Some(Ty::Nat(replacement)) => replacement.clone(),
623 Some(_) => n.clone(), None => n.clone(),
625 }
626 }
627 TyNat::Add(a, b) => TyNat::add(self.apply_nat(a), self.apply_nat(b)),
628 TyNat::Mul(a, b) => TyNat::mul(self.apply_nat(a), self.apply_nat(b)),
629 }
630 }
631
632 #[must_use]
634 pub fn apply_ty_list(&self, l: &TyList) -> TyList {
635 match l {
636 TyList::Nil => TyList::Nil,
637 TyList::Cons(head, tail) => TyList::cons(self.apply(head), self.apply_ty_list(tail)),
638 TyList::Var(v) => {
639 match self.get(v) {
641 Some(Ty::TyList(replacement)) => replacement.clone(),
642 Some(_) => l.clone(), None => l.clone(),
644 }
645 }
646 TyList::Append(xs, ys) => {
647 TyList::append(self.apply_ty_list(xs), self.apply_ty_list(ys))
648 }
649 }
650 }
651
652 #[must_use]
654 pub fn compose(&self, other: &Self) -> Self {
655 let mut result = Self::new();
656 for (k, v) in &other.mapping {
657 result.mapping.insert(*k, self.apply(v));
658 }
659 for (k, v) in &self.mapping {
660 result.mapping.entry(*k).or_insert_with(|| v.clone());
661 }
662 result
663 }
664
665 #[must_use]
667 pub fn is_empty(&self) -> bool {
668 self.mapping.is_empty()
669 }
670}
671
672#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
682pub enum PrimTy {
683 I32,
685 I64,
687 U32,
689 U64,
691 F32,
693 F64,
695 Char,
697 Addr,
699}
700
701impl PrimTy {
702 #[must_use]
704 pub const fn size_bytes(self) -> usize {
705 match self {
706 Self::I32 | Self::U32 | Self::F32 => 4,
707 Self::I64 | Self::U64 | Self::F64 | Self::Addr => 8,
708 Self::Char => 4, }
710 }
711
712 #[must_use]
714 pub const fn alignment(self) -> usize {
715 self.size_bytes()
716 }
717
718 #[must_use]
720 pub const fn name(self) -> &'static str {
721 match self {
722 Self::I32 => "Int32#",
723 Self::I64 => "Int#",
724 Self::U32 => "Word32#",
725 Self::U64 => "Word#",
726 Self::F32 => "Float#",
727 Self::F64 => "Double#",
728 Self::Char => "Char#",
729 Self::Addr => "Addr#",
730 }
731 }
732
733 #[must_use]
735 pub const fn is_signed_int(self) -> bool {
736 matches!(self, Self::I32 | Self::I64)
737 }
738
739 #[must_use]
741 pub const fn is_unsigned_int(self) -> bool {
742 matches!(self, Self::U32 | Self::U64)
743 }
744
745 #[must_use]
747 pub const fn is_float(self) -> bool {
748 matches!(self, Self::F32 | Self::F64)
749 }
750
751 #[must_use]
753 pub const fn is_numeric(self) -> bool {
754 matches!(
755 self,
756 Self::I32 | Self::I64 | Self::U32 | Self::U64 | Self::F32 | Self::F64
757 )
758 }
759}
760
761impl std::fmt::Display for PrimTy {
762 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
763 write!(f, "{}", self.name())
764 }
765}
766
767#[must_use]
781pub fn types_match(pattern: &Ty, target: &Ty) -> Option<Subst> {
782 let mut subst = Subst::new();
783 if types_match_with_subst(pattern, target, &mut subst) {
784 Some(subst)
785 } else {
786 None
787 }
788}
789
790pub fn types_match_with_subst(pattern: &Ty, target: &Ty, subst: &mut Subst) -> bool {
795 match (pattern, target) {
796 (Ty::Var(v), _) => {
798 if let Some(bound_ty) = subst.get(v) {
799 types_equal(bound_ty, target)
801 } else {
802 subst.insert(v, target.clone());
803 true
804 }
805 }
806
807 (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
809
810 (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
812
813 (Ty::App(f1, a1), Ty::App(f2, a2)) => {
815 types_match_with_subst(f1, f2, subst) && types_match_with_subst(a1, a2, subst)
816 }
817
818 (Ty::Fun(a1, r1), Ty::Fun(a2, r2)) => {
820 types_match_with_subst(a1, a2, subst) && types_match_with_subst(r1, r2, subst)
821 }
822
823 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
825 .iter()
826 .zip(ts2.iter())
827 .all(|(t1, t2)| types_match_with_subst(t1, t2, subst)),
828
829 (Ty::List(e1), Ty::List(e2)) => types_match_with_subst(e1, e2, subst),
831
832 (Ty::Forall(_, body1), Ty::Forall(_, body2)) => types_match_with_subst(body1, body2, subst),
834
835 (Ty::Nat(n1), Ty::Nat(n2)) => n1 == n2,
837
838 (Ty::TyList(l1), Ty::TyList(l2)) => l1 == l2,
840
841 (Ty::Error, _) | (_, Ty::Error) => true,
843
844 _ => false,
846 }
847}
848
849#[must_use]
854pub fn types_match_multi(patterns: &[Ty], targets: &[Ty]) -> Option<Subst> {
855 if patterns.len() != targets.len() {
856 return None;
857 }
858
859 let mut subst = Subst::new();
860 for (pattern, target) in patterns.iter().zip(targets.iter()) {
861 if !types_match_with_subst(pattern, target, &mut subst) {
862 return None;
863 }
864 }
865 Some(subst)
866}
867
868#[must_use]
870pub fn types_equal(t1: &Ty, t2: &Ty) -> bool {
871 match (t1, t2) {
872 (Ty::Var(v1), Ty::Var(v2)) => v1.id == v2.id,
873 (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
874 (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
875 (Ty::App(f1, a1), Ty::App(f2, a2)) => types_equal(f1, f2) && types_equal(a1, a2),
876 (Ty::Fun(a1, r1), Ty::Fun(a2, r2)) => types_equal(a1, a2) && types_equal(r1, r2),
877 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
878 .iter()
879 .zip(ts2.iter())
880 .all(|(t1, t2)| types_equal(t1, t2)),
881 (Ty::List(e1), Ty::List(e2)) => types_equal(e1, e2),
882 (Ty::Error, Ty::Error) => true,
883 _ => false,
884 }
885}
886
887#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
889pub enum TypeError {
890 #[error("type mismatch: expected {expected}, found {found}")]
892 Mismatch {
893 expected: String,
895 found: String,
897 span: Span,
899 },
900
901 #[error("infinite type: {var} occurs in {ty}")]
903 OccursCheck {
904 var: String,
906 ty: String,
908 span: Span,
910 },
911
912 #[error("unbound type variable: {name}")]
914 UnboundVar {
915 name: String,
917 span: Span,
919 },
920
921 #[error("kind mismatch: expected {expected}, found {found}")]
923 KindMismatch {
924 expected: String,
926 found: String,
928 span: Span,
930 },
931
932 #[error("ambiguous type variable: {var}")]
934 Ambiguous {
935 var: String,
937 span: Span,
939 },
940}
941
942#[cfg(test)]
943mod tests {
944 use super::*;
945
946 #[test]
947 fn test_ty_free_vars() {
948 let a = TyVar::new_star(0);
949 let b = TyVar::new_star(1);
950
951 let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(b.clone()));
953 let vars = ty.free_vars();
954 assert_eq!(vars.len(), 2);
955 assert!(vars.contains(&a));
956 assert!(vars.contains(&b));
957 }
958
959 #[test]
960 fn test_subst_apply() {
961 let a = TyVar::new_star(0);
962 let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
964
965 let mut subst = Subst::new();
966 subst.insert(&a, Ty::Con(int_con.clone()));
967
968 let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(a));
969 let result = subst.apply(&ty);
970
971 match result {
972 Ty::Fun(from, to) => {
973 assert!(matches!(*from, Ty::Con(_)));
974 assert!(matches!(*to, Ty::Con(_)));
975 }
976 _ => panic!("expected function type"),
977 }
978 }
979
980 #[test]
981 fn test_scheme_mono() {
982 let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
984 let scheme = Scheme::mono(Ty::Con(int_con));
985 assert!(scheme.is_mono());
986 }
987
988 #[test]
989 fn test_prim_ty_properties() {
990 assert_eq!(PrimTy::I64.size_bytes(), 8);
991 assert_eq!(PrimTy::I32.size_bytes(), 4);
992 assert_eq!(PrimTy::F64.size_bytes(), 8);
993 assert_eq!(PrimTy::F32.size_bytes(), 4);
994
995 assert!(PrimTy::I64.is_signed_int());
996 assert!(PrimTy::I32.is_signed_int());
997 assert!(!PrimTy::U64.is_signed_int());
998
999 assert!(PrimTy::F64.is_float());
1000 assert!(PrimTy::F32.is_float());
1001 assert!(!PrimTy::I64.is_float());
1002
1003 assert!(PrimTy::I64.is_numeric());
1004 assert!(PrimTy::F64.is_numeric());
1005 assert!(!PrimTy::Char.is_numeric());
1006 assert!(!PrimTy::Addr.is_numeric());
1007 }
1008
1009 #[test]
1010 fn test_prim_ty_names() {
1011 assert_eq!(PrimTy::I64.name(), "Int#");
1012 assert_eq!(PrimTy::F64.name(), "Double#");
1013 assert_eq!(PrimTy::F32.name(), "Float#");
1014 assert_eq!(PrimTy::Char.name(), "Char#");
1015 }
1016
1017 #[test]
1018 fn test_ty_prim_constructors() {
1019 let int = Ty::int_prim();
1020 assert!(int.is_prim());
1021 assert_eq!(int.as_prim(), Some(PrimTy::I64));
1022
1023 let double = Ty::double_prim();
1024 assert!(double.is_prim());
1025 assert_eq!(double.as_prim(), Some(PrimTy::F64));
1026
1027 let unit = Ty::unit();
1029 assert!(!unit.is_prim());
1030 assert_eq!(unit.as_prim(), None);
1031 }
1032
1033 #[test]
1034 fn test_prim_ty_no_free_vars() {
1035 let prim = Ty::int_prim();
1037 assert!(prim.free_vars().is_empty());
1038 }
1039
1040 #[test]
1041 fn test_subst_prim_unchanged() {
1042 let a = TyVar::new_star(0);
1044 let mut subst = Subst::new();
1045 subst.insert(&a, Ty::unit());
1046
1047 let prim = Ty::int_prim();
1048 let result = subst.apply(&prim);
1049 assert_eq!(result, prim);
1050 }
1051
1052 #[test]
1053 fn test_types_match_variable_binding() {
1054 let a = TyVar::new_star(0);
1055 let int_con = TyCon::new(Symbol::intern("Int"), Kind::Star);
1056 let int_ty = Ty::Con(int_con);
1057
1058 let result = types_match(&Ty::Var(a.clone()), &int_ty);
1059 assert!(result.is_some());
1060 let subst = result.unwrap();
1061 assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1062 }
1063
1064 #[test]
1065 fn test_types_match_constructor() {
1066 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1067 let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1068
1069 assert!(types_match(&int_ty, &int_ty).is_some());
1070 assert!(types_match(&int_ty, &bool_ty).is_none());
1071 }
1072
1073 #[test]
1074 fn test_types_match_application() {
1075 let a = TyVar::new_star(0);
1076 let list_con = Ty::Con(TyCon::new(Symbol::intern("[]"), Kind::star_to_star()));
1077 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1078
1079 let pattern = Ty::App(Box::new(list_con.clone()), Box::new(Ty::Var(a.clone())));
1081 let target = Ty::App(Box::new(list_con), Box::new(int_ty.clone()));
1082
1083 let result = types_match(&pattern, &target);
1084 assert!(result.is_some());
1085 let subst = result.unwrap();
1086 assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1087 }
1088
1089 #[test]
1090 fn test_types_match_multi_basic() {
1091 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1092 let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1093
1094 assert!(types_match_multi(&[int_ty.clone()], &[int_ty.clone()]).is_some());
1095 assert!(types_match_multi(&[int_ty.clone()], &[bool_ty]).is_none());
1096 assert!(types_match_multi(&[], &[]).is_some());
1097 assert!(types_match_multi(&[int_ty.clone()], &[]).is_none());
1098 }
1099}