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::App(f, a), Ty::List(e)) => {
835 let list_con = Ty::Con(TyCon::new(Symbol::intern("[]"), Kind::star_to_star()));
836 types_match_with_subst(f, &list_con, subst) && types_match_with_subst(a, e, subst)
837 }
838 (Ty::List(e), Ty::App(f, a)) => {
839 let list_con = Ty::Con(TyCon::new(Symbol::intern("[]"), Kind::star_to_star()));
840 types_match_with_subst(&list_con, f, subst) && types_match_with_subst(e, a, subst)
841 }
842
843 (Ty::Forall(_, body1), Ty::Forall(_, body2)) => types_match_with_subst(body1, body2, subst),
845
846 (Ty::Nat(n1), Ty::Nat(n2)) => n1 == n2,
848
849 (Ty::TyList(l1), Ty::TyList(l2)) => l1 == l2,
851
852 (Ty::Error, _) | (_, Ty::Error) => true,
854
855 _ => false,
857 }
858}
859
860#[must_use]
865pub fn types_match_multi(patterns: &[Ty], targets: &[Ty]) -> Option<Subst> {
866 if patterns.len() != targets.len() {
867 return None;
868 }
869
870 let mut subst = Subst::new();
871 for (pattern, target) in patterns.iter().zip(targets.iter()) {
872 if !types_match_with_subst(pattern, target, &mut subst) {
873 return None;
874 }
875 }
876 Some(subst)
877}
878
879#[must_use]
881pub fn types_equal(t1: &Ty, t2: &Ty) -> bool {
882 match (t1, t2) {
883 (Ty::Var(v1), Ty::Var(v2)) => v1.id == v2.id,
884 (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
885 (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
886 (Ty::App(f1, a1), Ty::App(f2, a2)) => types_equal(f1, f2) && types_equal(a1, a2),
887 (Ty::Fun(a1, r1), Ty::Fun(a2, r2)) => types_equal(a1, a2) && types_equal(r1, r2),
888 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
889 .iter()
890 .zip(ts2.iter())
891 .all(|(t1, t2)| types_equal(t1, t2)),
892 (Ty::List(e1), Ty::List(e2)) => types_equal(e1, e2),
893 (Ty::Error, Ty::Error) => true,
894 _ => false,
895 }
896}
897
898#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
900pub enum TypeError {
901 #[error("type mismatch: expected {expected}, found {found}")]
903 Mismatch {
904 expected: String,
906 found: String,
908 span: Span,
910 },
911
912 #[error("infinite type: {var} occurs in {ty}")]
914 OccursCheck {
915 var: String,
917 ty: String,
919 span: Span,
921 },
922
923 #[error("unbound type variable: {name}")]
925 UnboundVar {
926 name: String,
928 span: Span,
930 },
931
932 #[error("kind mismatch: expected {expected}, found {found}")]
934 KindMismatch {
935 expected: String,
937 found: String,
939 span: Span,
941 },
942
943 #[error("ambiguous type variable: {var}")]
945 Ambiguous {
946 var: String,
948 span: Span,
950 },
951}
952
953#[cfg(test)]
954mod tests {
955 use super::*;
956
957 #[test]
958 fn test_ty_free_vars() {
959 let a = TyVar::new_star(0);
960 let b = TyVar::new_star(1);
961
962 let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(b.clone()));
964 let vars = ty.free_vars();
965 assert_eq!(vars.len(), 2);
966 assert!(vars.contains(&a));
967 assert!(vars.contains(&b));
968 }
969
970 #[test]
971 fn test_subst_apply() {
972 let a = TyVar::new_star(0);
973 let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
975
976 let mut subst = Subst::new();
977 subst.insert(&a, Ty::Con(int_con.clone()));
978
979 let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(a));
980 let result = subst.apply(&ty);
981
982 match result {
983 Ty::Fun(from, to) => {
984 assert!(matches!(*from, Ty::Con(_)));
985 assert!(matches!(*to, Ty::Con(_)));
986 }
987 _ => panic!("expected function type"),
988 }
989 }
990
991 #[test]
992 fn test_scheme_mono() {
993 let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
995 let scheme = Scheme::mono(Ty::Con(int_con));
996 assert!(scheme.is_mono());
997 }
998
999 #[test]
1000 fn test_prim_ty_properties() {
1001 assert_eq!(PrimTy::I64.size_bytes(), 8);
1002 assert_eq!(PrimTy::I32.size_bytes(), 4);
1003 assert_eq!(PrimTy::F64.size_bytes(), 8);
1004 assert_eq!(PrimTy::F32.size_bytes(), 4);
1005
1006 assert!(PrimTy::I64.is_signed_int());
1007 assert!(PrimTy::I32.is_signed_int());
1008 assert!(!PrimTy::U64.is_signed_int());
1009
1010 assert!(PrimTy::F64.is_float());
1011 assert!(PrimTy::F32.is_float());
1012 assert!(!PrimTy::I64.is_float());
1013
1014 assert!(PrimTy::I64.is_numeric());
1015 assert!(PrimTy::F64.is_numeric());
1016 assert!(!PrimTy::Char.is_numeric());
1017 assert!(!PrimTy::Addr.is_numeric());
1018 }
1019
1020 #[test]
1021 fn test_prim_ty_names() {
1022 assert_eq!(PrimTy::I64.name(), "Int#");
1023 assert_eq!(PrimTy::F64.name(), "Double#");
1024 assert_eq!(PrimTy::F32.name(), "Float#");
1025 assert_eq!(PrimTy::Char.name(), "Char#");
1026 }
1027
1028 #[test]
1029 fn test_ty_prim_constructors() {
1030 let int = Ty::int_prim();
1031 assert!(int.is_prim());
1032 assert_eq!(int.as_prim(), Some(PrimTy::I64));
1033
1034 let double = Ty::double_prim();
1035 assert!(double.is_prim());
1036 assert_eq!(double.as_prim(), Some(PrimTy::F64));
1037
1038 let unit = Ty::unit();
1040 assert!(!unit.is_prim());
1041 assert_eq!(unit.as_prim(), None);
1042 }
1043
1044 #[test]
1045 fn test_prim_ty_no_free_vars() {
1046 let prim = Ty::int_prim();
1048 assert!(prim.free_vars().is_empty());
1049 }
1050
1051 #[test]
1052 fn test_subst_prim_unchanged() {
1053 let a = TyVar::new_star(0);
1055 let mut subst = Subst::new();
1056 subst.insert(&a, Ty::unit());
1057
1058 let prim = Ty::int_prim();
1059 let result = subst.apply(&prim);
1060 assert_eq!(result, prim);
1061 }
1062
1063 #[test]
1064 fn test_types_match_variable_binding() {
1065 let a = TyVar::new_star(0);
1066 let int_con = TyCon::new(Symbol::intern("Int"), Kind::Star);
1067 let int_ty = Ty::Con(int_con);
1068
1069 let result = types_match(&Ty::Var(a.clone()), &int_ty);
1070 assert!(result.is_some());
1071 let subst = result.unwrap();
1072 assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1073 }
1074
1075 #[test]
1076 fn test_types_match_constructor() {
1077 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1078 let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1079
1080 assert!(types_match(&int_ty, &int_ty).is_some());
1081 assert!(types_match(&int_ty, &bool_ty).is_none());
1082 }
1083
1084 #[test]
1085 fn test_types_match_application() {
1086 let a = TyVar::new_star(0);
1087 let list_con = Ty::Con(TyCon::new(Symbol::intern("[]"), Kind::star_to_star()));
1088 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1089
1090 let pattern = Ty::App(Box::new(list_con.clone()), Box::new(Ty::Var(a.clone())));
1092 let target = Ty::App(Box::new(list_con), Box::new(int_ty.clone()));
1093
1094 let result = types_match(&pattern, &target);
1095 assert!(result.is_some());
1096 let subst = result.unwrap();
1097 assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1098 }
1099
1100 #[test]
1101 fn test_types_match_multi_basic() {
1102 let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1103 let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1104
1105 assert!(
1106 types_match_multi(std::slice::from_ref(&int_ty), std::slice::from_ref(&int_ty))
1107 .is_some()
1108 );
1109 assert!(types_match_multi(std::slice::from_ref(&int_ty), &[bool_ty]).is_none());
1110 assert!(types_match_multi(&[], &[]).is_some());
1111 assert!(types_match_multi(std::slice::from_ref(&int_ty), &[]).is_none());
1112 }
1113}