1use super::constraint::{Constraint, Substitution};
9use super::types::{InferType, TypeVarId};
10use crate::Type;
11use gruel_span::Span;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum UnifyResult {
16 Ok,
18
19 TypeMismatch {
21 expected: InferType,
22 found: InferType,
23 },
24
25 IntLiteralNonInteger { found: Type },
27
28 OccursCheck { var: TypeVarId, ty: InferType },
30
31 NotSigned { ty: Type },
33
34 NotInteger { ty: Type },
36
37 NotUnsigned { ty: Type },
39
40 NotNumeric { ty: Type },
42
43 ArrayLengthMismatch { expected: u64, found: u64 },
45}
46
47impl UnifyResult {
48 pub fn is_ok(&self) -> bool {
50 matches!(self, UnifyResult::Ok)
51 }
52}
53
54#[derive(Debug, Clone)]
58pub struct UnificationError {
59 pub kind: UnifyResult,
61 pub span: Span,
63}
64
65impl UnificationError {
66 pub fn new(kind: UnifyResult, span: Span) -> Self {
68 Self { kind, span }
69 }
70
71 pub fn message(&self) -> String {
73 match &self.kind {
74 UnifyResult::Ok => unreachable!("UnificationError should never contain Ok"),
75 UnifyResult::TypeMismatch { expected, found } => {
76 format!("type mismatch: expected {expected}, found {found}")
77 }
78 UnifyResult::IntLiteralNonInteger { found } => {
79 format!("integer literal cannot be used as {found}")
80 }
81 UnifyResult::OccursCheck { var, ty } => {
82 format!("infinite type: {var} cannot unify with {ty}")
83 }
84 UnifyResult::NotSigned { ty } => {
85 format!("cannot negate unsigned type {ty}")
86 }
87 UnifyResult::NotInteger { ty } => {
88 format!("expected integer type, found {ty}")
89 }
90 UnifyResult::NotUnsigned { ty } => {
91 format!("array index must be unsigned integer type, found {ty}")
92 }
93 UnifyResult::NotNumeric { ty } => {
94 format!("expected numeric type, found {ty}")
95 }
96 UnifyResult::ArrayLengthMismatch { expected, found } => {
97 format!("array length mismatch: expected {expected}, found {found}")
98 }
99 }
100 }
101}
102
103#[derive(Debug)]
108pub struct Unifier {
109 pub substitution: Substitution,
111}
112
113impl Default for Unifier {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119impl Unifier {
120 pub fn new() -> Self {
122 Unifier {
123 substitution: Substitution::new(),
124 }
125 }
126
127 pub fn with_capacity(type_var_count: u32) -> Self {
132 Unifier {
133 substitution: Substitution::with_capacity(type_var_count as usize),
134 }
135 }
136
137 pub fn unify(&mut self, lhs: &InferType, rhs: &InferType) -> UnifyResult {
153 let lhs_resolved = self.substitution.apply(lhs);
155 let rhs_resolved = self.substitution.apply(rhs);
156
157 match (&lhs_resolved, &rhs_resolved) {
158 (InferType::Concrete(t1), InferType::Concrete(t2)) => {
160 if t1 == t2 || t1.can_coerce_to(t2) || t2.can_coerce_to(t1) {
161 UnifyResult::Ok
162 } else {
163 UnifyResult::TypeMismatch {
164 expected: lhs_resolved,
165 found: rhs_resolved,
166 }
167 }
168 }
169
170 (InferType::Var(var), _) => self.bind(*var, &rhs_resolved),
172
173 (_, InferType::Var(var)) => self.bind(*var, &lhs_resolved),
175
176 (InferType::IntLiteral, InferType::Concrete(ty))
178 | (InferType::Concrete(ty), InferType::IntLiteral) => {
179 if ty.is_integer() {
180 self.rebind_int_literal_to_concrete(lhs, ty);
184 self.rebind_int_literal_to_concrete(rhs, ty);
185 UnifyResult::Ok
186 } else if ty.is_error() {
187 UnifyResult::Ok
189 } else {
190 UnifyResult::IntLiteralNonInteger { found: *ty }
191 }
192 }
193
194 (InferType::FloatLiteral, InferType::Concrete(ty))
196 | (InferType::Concrete(ty), InferType::FloatLiteral) => {
197 if ty.is_float() {
198 self.rebind_float_literal_to_concrete(lhs, ty);
199 self.rebind_float_literal_to_concrete(rhs, ty);
200 UnifyResult::Ok
201 } else if ty.is_error() {
202 UnifyResult::Ok
203 } else {
204 UnifyResult::TypeMismatch {
205 expected: lhs_resolved,
206 found: rhs_resolved,
207 }
208 }
209 }
210
211 (InferType::IntLiteral, InferType::IntLiteral) => UnifyResult::Ok,
213
214 (InferType::FloatLiteral, InferType::FloatLiteral) => UnifyResult::Ok,
216
217 (InferType::IntLiteral, InferType::FloatLiteral)
219 | (InferType::FloatLiteral, InferType::IntLiteral) => UnifyResult::TypeMismatch {
220 expected: lhs_resolved,
221 found: rhs_resolved,
222 },
223
224 (
226 InferType::Array {
227 element: elem1,
228 length: len1,
229 },
230 InferType::Array {
231 element: elem2,
232 length: len2,
233 },
234 ) => {
235 if len1 != len2 {
236 UnifyResult::ArrayLengthMismatch {
237 expected: *len1,
238 found: *len2,
239 }
240 } else {
241 self.unify(elem1, elem2)
243 }
244 }
245
246 (InferType::Array { .. }, _) | (_, InferType::Array { .. }) => {
250 UnifyResult::TypeMismatch {
251 expected: lhs_resolved,
252 found: rhs_resolved,
253 }
254 }
255 }
256 }
257
258 fn rebind_int_literal_to_concrete(&mut self, original: &InferType, concrete_ty: &Type) {
261 if let InferType::Var(var) = original {
262 if let Some(bound) = self.substitution.get(*var)
264 && bound.is_int_literal()
265 {
266 self.substitution
267 .insert(*var, InferType::Concrete(*concrete_ty));
268 }
269 }
270 }
271
272 fn rebind_float_literal_to_concrete(&mut self, original: &InferType, concrete_ty: &Type) {
273 if let InferType::Var(var) = original
274 && let Some(bound) = self.substitution.get(*var)
275 && matches!(bound, InferType::FloatLiteral)
276 {
277 self.substitution
278 .insert(*var, InferType::Concrete(*concrete_ty));
279 }
280 }
281
282 fn bind(&mut self, var: TypeVarId, ty: &InferType) -> UnifyResult {
286 if let InferType::Var(id) = ty
288 && *id == var
289 {
290 return UnifyResult::Ok;
291 }
292
293 if self.substitution.occurs_in(var, ty) {
295 return UnifyResult::OccursCheck {
296 var,
297 ty: ty.clone(),
298 };
299 }
300
301 self.substitution.insert(var, ty.clone());
302 UnifyResult::Ok
303 }
304
305 pub fn check_signed(&self, ty: &InferType) -> UnifyResult {
311 let ty = self.substitution.apply(ty);
312 match &ty {
313 InferType::Concrete(concrete) => {
314 if concrete.is_signed() || concrete.is_error() || concrete.is_never() {
315 UnifyResult::Ok
316 } else if concrete.is_unsigned() {
317 UnifyResult::NotSigned { ty: *concrete }
318 } else {
319 UnifyResult::Ok
321 }
322 }
323 InferType::Var(_) | InferType::IntLiteral => UnifyResult::Ok,
326 InferType::FloatLiteral => UnifyResult::Ok,
328 InferType::Array { .. } => UnifyResult::Ok,
330 }
331 }
332
333 pub fn check_integer(&self, ty: &InferType) -> UnifyResult {
338 let ty = self.substitution.apply(ty);
339 match &ty {
340 InferType::Concrete(concrete) => {
341 if concrete.is_integer() || concrete.is_error() || concrete.is_never() {
342 UnifyResult::Ok
343 } else {
344 UnifyResult::NotInteger { ty: *concrete }
345 }
346 }
347 InferType::Var(_) | InferType::IntLiteral => UnifyResult::Ok,
349 InferType::FloatLiteral => UnifyResult::NotInteger { ty: Type::F64 },
351 InferType::Array { .. } => UnifyResult::NotInteger { ty: Type::ERROR },
353 }
354 }
355
356 pub fn check_numeric(&self, ty: &InferType) -> UnifyResult {
361 let ty = self.substitution.apply(ty);
362 match &ty {
363 InferType::Concrete(concrete) => {
364 if concrete.is_numeric() || concrete.is_error() || concrete.is_never() {
365 UnifyResult::Ok
366 } else {
367 UnifyResult::NotNumeric { ty: *concrete }
368 }
369 }
370 InferType::Var(_) | InferType::IntLiteral | InferType::FloatLiteral => UnifyResult::Ok,
372 InferType::Array { .. } => UnifyResult::NotNumeric { ty: Type::ERROR },
374 }
375 }
376
377 pub fn check_unsigned(&self, ty: &InferType) -> UnifyResult {
383 let ty = self.substitution.apply(ty);
384 match &ty {
385 InferType::Concrete(concrete) => {
386 if concrete.is_unsigned() || concrete.is_error() || concrete.is_never() {
387 UnifyResult::Ok
388 } else if concrete.is_signed() {
389 UnifyResult::NotUnsigned { ty: *concrete }
390 } else {
391 UnifyResult::NotUnsigned { ty: *concrete }
393 }
394 }
395 InferType::Var(_) => UnifyResult::Ok,
397 InferType::IntLiteral => UnifyResult::Ok,
399 InferType::FloatLiteral => UnifyResult::NotUnsigned { ty: Type::F64 },
401 InferType::Array { .. } => UnifyResult::NotUnsigned { ty: Type::ERROR },
403 }
404 }
405
406 pub fn solve_constraints(&mut self, constraints: &[Constraint]) -> Vec<UnificationError> {
417 let mut errors = Vec::new();
418
419 for constraint in constraints {
420 let result = match constraint {
421 Constraint::Equal(lhs, rhs, span) => {
422 let result = self.unify(lhs, rhs);
423 if !result.is_ok() {
424 self.recover_from_error(lhs, rhs);
427 errors.push(UnificationError::new(result, *span));
428 }
429 continue;
430 }
431 Constraint::IsSigned(ty, span) => {
432 let result = self.check_signed(ty);
433 (result, *span)
434 }
435 Constraint::IsInteger(ty, span) => {
436 let result = self.check_integer(ty);
437 (result, *span)
438 }
439 Constraint::IsUnsigned(ty, span) => {
440 let applied = self.substitution.apply(ty);
443 match &applied {
444 InferType::IntLiteral => {
445 if let InferType::Var(var) = ty {
447 self.substitution
448 .insert(*var, InferType::Concrete(Type::U64));
449 }
450 }
451 InferType::Var(var) => {
452 self.substitution
456 .insert(*var, InferType::Concrete(Type::U64));
457 }
458 _ => {}
459 }
460 let result = self.check_unsigned(ty);
461 (result, *span)
462 }
463 Constraint::IsNumeric(ty, span) => {
464 let result = self.check_numeric(ty);
465 (result, *span)
466 }
467 };
468
469 if !result.0.is_ok() {
470 errors.push(UnificationError::new(result.0, result.1));
471 }
472 }
473
474 errors
475 }
476
477 fn recover_from_error(&mut self, lhs: &InferType, rhs: &InferType) {
482 let lhs_applied = self.substitution.apply(lhs);
484 let rhs_applied = self.substitution.apply(rhs);
485
486 if let InferType::Var(var) = lhs_applied {
488 self.substitution
489 .insert(var, InferType::Concrete(Type::ERROR));
490 }
491 if let InferType::Var(var) = rhs_applied {
492 self.substitution
493 .insert(var, InferType::Concrete(Type::ERROR));
494 }
495 }
496
497 pub fn default_int_literal_vars(&mut self, int_literal_vars: &[TypeVarId]) {
508 for &var in int_literal_vars {
509 let resolved = self.substitution.apply(&InferType::Var(var));
511 if let InferType::Var(_) = resolved {
512 self.substitution
514 .insert(var, InferType::Concrete(Type::I32));
515 }
516 }
518 }
519
520 pub fn default_float_literal_vars(&mut self, float_literal_vars: &[TypeVarId]) {
522 for &var in float_literal_vars {
523 let resolved = self.substitution.apply(&InferType::Var(var));
524 if let InferType::Var(_) = resolved {
525 self.substitution
526 .insert(var, InferType::Concrete(Type::F64));
527 }
528 }
529 }
530
531 pub fn resolve_infer_type(&self, ty: &InferType) -> InferType {
537 let resolved = self.substitution.apply(ty);
538 match resolved {
539 InferType::Concrete(_) => resolved,
540 InferType::Var(_) => resolved, InferType::IntLiteral => InferType::Concrete(Type::I32), InferType::FloatLiteral => InferType::Concrete(Type::F64), InferType::Array { element, length } => {
544 let resolved_element = self.resolve_infer_type(&element);
545 InferType::Array {
546 element: Box::new(resolved_element),
547 length,
548 }
549 }
550 }
551 }
552
553 pub fn resolve(&self, ty: &InferType) -> Option<Type> {
559 let resolved = self.resolve_infer_type(ty);
560 match resolved {
561 InferType::Concrete(t) => Some(t),
562 InferType::Var(_) => None, InferType::IntLiteral => Some(Type::I32), InferType::FloatLiteral => Some(Type::F64), InferType::Array { .. } => None, }
567 }
568
569 pub fn resolve_or_error(&self, ty: &InferType) -> Type {
578 self.resolve(ty).unwrap_or(Type::ERROR)
579 }
580}
581
582#[cfg(test)]
583mod tests {
584 use super::*;
585
586 #[test]
587 fn test_unify_same_concrete() {
588 let mut unifier = Unifier::new();
589 let result = unifier.unify(
590 &InferType::Concrete(Type::I32),
591 &InferType::Concrete(Type::I32),
592 );
593 assert!(result.is_ok());
594 }
595
596 #[test]
597 fn test_unify_different_concrete() {
598 let mut unifier = Unifier::new();
599 let result = unifier.unify(
600 &InferType::Concrete(Type::I32),
601 &InferType::Concrete(Type::I64),
602 );
603 assert!(!result.is_ok());
604 assert!(matches!(result, UnifyResult::TypeMismatch { .. }));
605 }
606
607 #[test]
608 fn test_unify_var_with_concrete() {
609 let mut unifier = Unifier::new();
610 let v0 = TypeVarId::new(0);
611
612 let result = unifier.unify(&InferType::Var(v0), &InferType::Concrete(Type::BOOL));
613 assert!(result.is_ok());
614
615 assert_eq!(
617 unifier.substitution.apply(&InferType::Var(v0)),
618 InferType::Concrete(Type::BOOL)
619 );
620 }
621
622 #[test]
623 fn test_unify_int_literal_with_integer() {
624 let mut unifier = Unifier::new();
625 let result = unifier.unify(&InferType::IntLiteral, &InferType::Concrete(Type::I64));
626 assert!(result.is_ok());
627 }
628
629 #[test]
630 fn test_unify_int_literal_with_non_integer() {
631 let mut unifier = Unifier::new();
632 let result = unifier.unify(&InferType::IntLiteral, &InferType::Concrete(Type::BOOL));
633 assert!(!result.is_ok());
634 assert!(matches!(result, UnifyResult::IntLiteralNonInteger { .. }));
635 }
636
637 #[test]
638 fn test_unify_two_int_literals() {
639 let mut unifier = Unifier::new();
640 let result = unifier.unify(&InferType::IntLiteral, &InferType::IntLiteral);
641 assert!(result.is_ok());
642 }
643
644 #[test]
645 fn test_unify_never_coerces() {
646 let mut unifier = Unifier::new();
647 let result = unifier.unify(
648 &InferType::Concrete(Type::NEVER),
649 &InferType::Concrete(Type::I32),
650 );
651 assert!(result.is_ok());
652 }
653
654 #[test]
655 fn test_unify_error_coerces() {
656 let mut unifier = Unifier::new();
657 let result = unifier.unify(
658 &InferType::Concrete(Type::ERROR),
659 &InferType::Concrete(Type::BOOL),
660 );
661 assert!(result.is_ok());
662 }
663
664 #[test]
665 fn test_unify_two_vars() {
666 let mut unifier = Unifier::new();
667 let v0 = TypeVarId::new(0);
668 let v1 = TypeVarId::new(1);
669
670 let result = unifier.unify(&InferType::Var(v0), &InferType::Var(v1));
671 assert!(result.is_ok());
672
673 }
676
677 #[test]
678 fn test_unify_chain_resolution() {
679 let mut unifier = Unifier::new();
680 let v0 = TypeVarId::new(0);
681 let v1 = TypeVarId::new(1);
682
683 let result = unifier.unify(&InferType::Var(v0), &InferType::Var(v1));
685 assert!(result.is_ok());
686
687 let result = unifier.unify(&InferType::Var(v1), &InferType::Concrete(Type::I64));
689 assert!(result.is_ok());
690
691 assert_eq!(
693 unifier.substitution.apply(&InferType::Var(v0)),
694 InferType::Concrete(Type::I64)
695 );
696 }
697
698 #[test]
699 fn test_resolve_concrete() {
700 let unifier = Unifier::new();
701 let ty = InferType::Concrete(Type::BOOL);
702 assert_eq!(unifier.resolve(&ty), Some(Type::BOOL));
703 }
704
705 #[test]
706 fn test_resolve_int_literal_defaults_to_i32() {
707 let unifier = Unifier::new();
708 let ty = InferType::IntLiteral;
709 assert_eq!(unifier.resolve(&ty), Some(Type::I32));
710 }
711
712 #[test]
713 fn test_resolve_unbound_var_returns_none() {
714 let unifier = Unifier::new();
715 let ty = InferType::Var(TypeVarId::new(0));
716 assert_eq!(unifier.resolve(&ty), None);
717 }
718
719 #[test]
720 fn test_resolve_bound_var() {
721 let mut unifier = Unifier::new();
722 let v0 = TypeVarId::new(0);
723 unifier
724 .substitution
725 .insert(v0, InferType::Concrete(Type::U8));
726
727 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::U8));
728 }
729
730 #[test]
731 fn test_check_signed_with_signed_type() {
732 let unifier = Unifier::new();
733 assert!(
734 unifier
735 .check_signed(&InferType::Concrete(Type::I32))
736 .is_ok()
737 );
738 assert!(
739 unifier
740 .check_signed(&InferType::Concrete(Type::I64))
741 .is_ok()
742 );
743 }
744
745 #[test]
746 fn test_check_signed_with_unsigned_type() {
747 let unifier = Unifier::new();
748 let result = unifier.check_signed(&InferType::Concrete(Type::U32));
749 assert!(!result.is_ok());
750 assert!(matches!(result, UnifyResult::NotSigned { ty: Type::U32 }));
751 }
752
753 #[test]
754 fn test_check_signed_with_int_literal() {
755 let unifier = Unifier::new();
756 assert!(unifier.check_signed(&InferType::IntLiteral).is_ok());
758 }
759
760 #[test]
761 fn test_unify_var_with_itself() {
762 let mut unifier = Unifier::new();
763 let v0 = TypeVarId::new(0);
764
765 let result = unifier.unify(&InferType::Var(v0), &InferType::Var(v0));
767 assert!(result.is_ok());
768 }
769
770 #[test]
771 fn test_occurs_check_prevents_infinite_type() {
772 let mut unifier = Unifier::new();
773 let v0 = TypeVarId::new(0);
774 let v1 = TypeVarId::new(1);
775
776 unifier.substitution.insert(v1, InferType::Var(v0));
778
779 let result = unifier.bind(v0, &InferType::Var(v1));
781 assert!(matches!(result, UnifyResult::OccursCheck { .. }));
782 }
783
784 #[test]
785 fn test_check_integer_with_integer_types() {
786 let unifier = Unifier::new();
787 assert!(
788 unifier
789 .check_integer(&InferType::Concrete(Type::I32))
790 .is_ok()
791 );
792 assert!(
793 unifier
794 .check_integer(&InferType::Concrete(Type::I64))
795 .is_ok()
796 );
797 assert!(
798 unifier
799 .check_integer(&InferType::Concrete(Type::U8))
800 .is_ok()
801 );
802 assert!(
803 unifier
804 .check_integer(&InferType::Concrete(Type::U64))
805 .is_ok()
806 );
807 }
808
809 #[test]
810 fn test_check_integer_with_non_integer_type() {
811 let unifier = Unifier::new();
812 let result = unifier.check_integer(&InferType::Concrete(Type::BOOL));
813 assert!(!result.is_ok());
814 assert!(matches!(result, UnifyResult::NotInteger { ty: Type::BOOL }));
815 }
816
817 #[test]
818 fn test_check_integer_with_int_literal() {
819 let unifier = Unifier::new();
820 assert!(unifier.check_integer(&InferType::IntLiteral).is_ok());
822 }
823
824 #[test]
825 fn test_check_integer_with_type_variable() {
826 let unifier = Unifier::new();
827 assert!(
829 unifier
830 .check_integer(&InferType::Var(TypeVarId::new(0)))
831 .is_ok()
832 );
833 }
834
835 #[test]
836 fn test_check_integer_with_error_type() {
837 let unifier = Unifier::new();
838 assert!(
840 unifier
841 .check_integer(&InferType::Concrete(Type::ERROR))
842 .is_ok()
843 );
844 }
845
846 #[test]
847 fn test_solve_constraints_empty() {
848 let mut unifier = Unifier::new();
849 let errors = unifier.solve_constraints(&[]);
850 assert!(errors.is_empty());
851 }
852
853 #[test]
854 fn test_solve_constraints_simple_equal() {
855 let mut unifier = Unifier::new();
856 let v0 = TypeVarId::new(0);
857 let constraints = vec![Constraint::equal(
858 InferType::Var(v0),
859 InferType::Concrete(Type::I64),
860 Span::new(0, 5),
861 )];
862 let errors = unifier.solve_constraints(&constraints);
863 assert!(errors.is_empty());
864 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::I64));
865 }
866
867 #[test]
868 fn test_solve_constraints_multiple_equal() {
869 let mut unifier = Unifier::new();
870 let v0 = TypeVarId::new(0);
871 let v1 = TypeVarId::new(1);
872 let constraints = vec![
873 Constraint::equal(InferType::Var(v0), InferType::Var(v1), Span::new(0, 5)),
874 Constraint::equal(
875 InferType::Var(v1),
876 InferType::Concrete(Type::BOOL),
877 Span::new(6, 10),
878 ),
879 ];
880 let errors = unifier.solve_constraints(&constraints);
881 assert!(errors.is_empty());
882 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::BOOL));
884 assert_eq!(unifier.resolve(&InferType::Var(v1)), Some(Type::BOOL));
885 }
886
887 #[test]
888 fn test_solve_constraints_type_mismatch() {
889 let mut unifier = Unifier::new();
890 let constraints = vec![Constraint::equal(
891 InferType::Concrete(Type::I32),
892 InferType::Concrete(Type::BOOL),
893 Span::new(0, 5),
894 )];
895 let errors = unifier.solve_constraints(&constraints);
896 assert_eq!(errors.len(), 1);
897 assert!(matches!(errors[0].kind, UnifyResult::TypeMismatch { .. }));
898 assert_eq!(errors[0].span, Span::new(0, 5));
899 }
900
901 #[test]
902 fn test_solve_constraints_int_literal_unifies() {
903 let mut unifier = Unifier::new();
904 let v0 = TypeVarId::new(0);
905 let constraints = vec![
906 Constraint::equal(InferType::Var(v0), InferType::IntLiteral, Span::new(0, 5)),
907 Constraint::equal(
908 InferType::Var(v0),
909 InferType::Concrete(Type::I64),
910 Span::new(6, 10),
911 ),
912 ];
913 let errors = unifier.solve_constraints(&constraints);
914 assert!(errors.is_empty());
915 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::I64));
917 }
918
919 #[test]
920 fn test_solve_constraints_is_signed() {
921 let mut unifier = Unifier::new();
922 let constraints = vec![Constraint::is_signed(
923 InferType::Concrete(Type::I32),
924 Span::new(0, 5),
925 )];
926 let errors = unifier.solve_constraints(&constraints);
927 assert!(errors.is_empty());
928 }
929
930 #[test]
931 fn test_solve_constraints_is_signed_fails_for_unsigned() {
932 let mut unifier = Unifier::new();
933 let constraints = vec![Constraint::is_signed(
934 InferType::Concrete(Type::U32),
935 Span::new(0, 5),
936 )];
937 let errors = unifier.solve_constraints(&constraints);
938 assert_eq!(errors.len(), 1);
939 assert!(matches!(
940 errors[0].kind,
941 UnifyResult::NotSigned { ty: Type::U32 }
942 ));
943 }
944
945 #[test]
946 fn test_solve_constraints_is_integer() {
947 let mut unifier = Unifier::new();
948 let constraints = vec![Constraint::is_integer(
949 InferType::Concrete(Type::U8),
950 Span::new(0, 5),
951 )];
952 let errors = unifier.solve_constraints(&constraints);
953 assert!(errors.is_empty());
954 }
955
956 #[test]
957 fn test_solve_constraints_is_integer_fails_for_bool() {
958 let mut unifier = Unifier::new();
959 let constraints = vec![Constraint::is_integer(
960 InferType::Concrete(Type::BOOL),
961 Span::new(0, 5),
962 )];
963 let errors = unifier.solve_constraints(&constraints);
964 assert_eq!(errors.len(), 1);
965 assert!(matches!(
966 errors[0].kind,
967 UnifyResult::NotInteger { ty: Type::BOOL }
968 ));
969 }
970
971 #[test]
972 fn test_solve_constraints_multiple_errors() {
973 let mut unifier = Unifier::new();
974 let constraints = vec![
975 Constraint::equal(
976 InferType::Concrete(Type::I32),
977 InferType::Concrete(Type::BOOL),
978 Span::new(0, 5),
979 ),
980 Constraint::is_signed(InferType::Concrete(Type::U64), Span::new(10, 15)),
981 ];
982 let errors = unifier.solve_constraints(&constraints);
983 assert_eq!(errors.len(), 2);
985 }
986
987 #[test]
988 fn test_solve_constraints_error_recovery() {
989 let mut unifier = Unifier::new();
990 let v0 = TypeVarId::new(0);
991 let constraints = vec![
992 Constraint::equal(
994 InferType::Var(v0),
995 InferType::Concrete(Type::I32),
996 Span::new(0, 5),
997 ),
998 Constraint::equal(
999 InferType::Var(v0),
1000 InferType::Concrete(Type::BOOL),
1001 Span::new(6, 10),
1002 ),
1003 ];
1004 let errors = unifier.solve_constraints(&constraints);
1005 assert_eq!(errors.len(), 1);
1007 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::I32));
1009 }
1010
1011 #[test]
1012 fn test_default_int_literal_vars() {
1013 let mut unifier = Unifier::new();
1014 let v0 = TypeVarId::new(0);
1015 let v1 = TypeVarId::new(1);
1016 let v2 = TypeVarId::new(2);
1017
1018 unifier
1021 .substitution
1022 .insert(v2, InferType::Concrete(Type::BOOL));
1023
1024 let int_literal_vars = vec![v0, v1];
1026
1027 unifier.default_int_literal_vars(&int_literal_vars);
1028
1029 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::I32));
1031 assert_eq!(unifier.resolve(&InferType::Var(v1)), Some(Type::I32));
1032 assert_eq!(unifier.resolve(&InferType::Var(v2)), Some(Type::BOOL));
1034 }
1035
1036 #[test]
1037 fn test_resolve_or_error_with_bound_var() {
1038 let mut unifier = Unifier::new();
1039 let v0 = TypeVarId::new(0);
1040 unifier
1041 .substitution
1042 .insert(v0, InferType::Concrete(Type::U8));
1043 assert_eq!(unifier.resolve_or_error(&InferType::Var(v0)), Type::U8);
1044 }
1045
1046 #[test]
1047 fn test_resolve_or_error_with_unbound_var() {
1048 let unifier = Unifier::new();
1049 let v0 = TypeVarId::new(0);
1050 assert_eq!(unifier.resolve_or_error(&InferType::Var(v0)), Type::ERROR);
1052 }
1053
1054 #[test]
1055 fn test_resolve_or_error_with_int_literal() {
1056 let unifier = Unifier::new();
1057 assert_eq!(unifier.resolve_or_error(&InferType::IntLiteral), Type::I32);
1059 }
1060
1061 #[test]
1062 fn test_unification_error_message() {
1063 let error = UnificationError::new(
1064 UnifyResult::TypeMismatch {
1065 expected: InferType::Concrete(Type::I32),
1066 found: InferType::Concrete(Type::BOOL),
1067 },
1068 Span::new(10, 20),
1069 );
1070 let msg = error.message();
1071 assert!(msg.contains("type mismatch"));
1072 assert!(msg.contains("i32"));
1073 assert!(msg.contains("bool"));
1074 }
1075
1076 #[test]
1077 fn test_unification_error_int_literal_non_integer_message() {
1078 let error = UnificationError::new(
1079 UnifyResult::IntLiteralNonInteger { found: Type::BOOL },
1080 Span::new(0, 5),
1081 );
1082 let msg = error.message();
1083 assert!(msg.contains("integer literal"));
1084 assert!(msg.contains("bool"));
1085 }
1086
1087 #[test]
1088 fn test_unification_error_not_signed_message() {
1089 let error =
1090 UnificationError::new(UnifyResult::NotSigned { ty: Type::U32 }, Span::new(0, 5));
1091 let msg = error.message();
1092 assert!(msg.contains("negate"));
1093 assert!(msg.contains("u32"));
1094 }
1095
1096 #[test]
1097 fn test_unification_error_not_integer_message() {
1098 let error =
1099 UnificationError::new(UnifyResult::NotInteger { ty: Type::BOOL }, Span::new(0, 5));
1100 let msg = error.message();
1101 assert!(msg.contains("expected integer"));
1102 assert!(msg.contains("bool"));
1103 }
1104
1105 #[test]
1106 fn test_solve_constraints_int_literal_with_non_integer() {
1107 let mut unifier = Unifier::new();
1108 let constraints = vec![Constraint::equal(
1109 InferType::IntLiteral,
1110 InferType::Concrete(Type::BOOL),
1111 Span::new(0, 5),
1112 )];
1113 let errors = unifier.solve_constraints(&constraints);
1114 assert_eq!(errors.len(), 1);
1115 assert!(matches!(
1116 errors[0].kind,
1117 UnifyResult::IntLiteralNonInteger { found: Type::BOOL }
1118 ));
1119 }
1120
1121 #[test]
1122 fn test_solve_constraints_chain_resolution() {
1123 let mut unifier = Unifier::new();
1126 let v0 = TypeVarId::new(0);
1127 let v1 = TypeVarId::new(1);
1128 let v2 = TypeVarId::new(2);
1129 let constraints = vec![
1130 Constraint::equal(InferType::Var(v0), InferType::Var(v1), Span::new(0, 5)),
1131 Constraint::equal(InferType::Var(v1), InferType::Var(v2), Span::new(6, 10)),
1132 Constraint::equal(
1133 InferType::Var(v2),
1134 InferType::Concrete(Type::I64),
1135 Span::new(11, 15),
1136 ),
1137 ];
1138 let errors = unifier.solve_constraints(&constraints);
1139 assert!(errors.is_empty());
1140 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::I64));
1141 assert_eq!(unifier.resolve(&InferType::Var(v1)), Some(Type::I64));
1142 assert_eq!(unifier.resolve(&InferType::Var(v2)), Some(Type::I64));
1143 }
1144
1145 #[test]
1146 fn test_solve_constraints_reverse_chain() {
1147 let mut unifier = Unifier::new();
1150 let v0 = TypeVarId::new(0);
1151 let v1 = TypeVarId::new(1);
1152 let v2 = TypeVarId::new(2);
1153 let constraints = vec![
1154 Constraint::equal(
1155 InferType::Var(v2),
1156 InferType::Concrete(Type::I64),
1157 Span::new(0, 5),
1158 ),
1159 Constraint::equal(InferType::Var(v1), InferType::Var(v2), Span::new(6, 10)),
1160 Constraint::equal(InferType::Var(v0), InferType::Var(v1), Span::new(11, 15)),
1161 ];
1162 let errors = unifier.solve_constraints(&constraints);
1163 assert!(errors.is_empty());
1164 assert_eq!(unifier.resolve(&InferType::Var(v0)), Some(Type::I64));
1165 assert_eq!(unifier.resolve(&InferType::Var(v1)), Some(Type::I64));
1166 assert_eq!(unifier.resolve(&InferType::Var(v2)), Some(Type::I64));
1167 }
1168
1169 #[test]
1174 fn test_check_unsigned_with_unsigned_types() {
1175 let unifier = Unifier::new();
1176 assert!(
1177 unifier
1178 .check_unsigned(&InferType::Concrete(Type::U8))
1179 .is_ok()
1180 );
1181 assert!(
1182 unifier
1183 .check_unsigned(&InferType::Concrete(Type::U16))
1184 .is_ok()
1185 );
1186 assert!(
1187 unifier
1188 .check_unsigned(&InferType::Concrete(Type::U32))
1189 .is_ok()
1190 );
1191 assert!(
1192 unifier
1193 .check_unsigned(&InferType::Concrete(Type::U64))
1194 .is_ok()
1195 );
1196 }
1197
1198 #[test]
1199 fn test_check_unsigned_with_signed_type() {
1200 let unifier = Unifier::new();
1201 let result = unifier.check_unsigned(&InferType::Concrete(Type::I32));
1202 assert!(!result.is_ok());
1203 assert!(matches!(result, UnifyResult::NotUnsigned { ty: Type::I32 }));
1204 }
1205
1206 #[test]
1207 fn test_check_unsigned_with_int_literal() {
1208 let unifier = Unifier::new();
1209 assert!(unifier.check_unsigned(&InferType::IntLiteral).is_ok());
1211 }
1212
1213 #[test]
1214 fn test_check_unsigned_with_non_integer_type() {
1215 let unifier = Unifier::new();
1216 let result = unifier.check_unsigned(&InferType::Concrete(Type::BOOL));
1217 assert!(!result.is_ok());
1218 assert!(matches!(
1219 result,
1220 UnifyResult::NotUnsigned { ty: Type::BOOL }
1221 ));
1222 }
1223
1224 #[test]
1225 fn test_solve_constraints_is_unsigned() {
1226 let mut unifier = Unifier::new();
1227 let constraints = vec![Constraint::is_unsigned(
1228 InferType::Concrete(Type::U64),
1229 Span::new(0, 5),
1230 )];
1231 let errors = unifier.solve_constraints(&constraints);
1232 assert!(errors.is_empty());
1233 }
1234
1235 #[test]
1236 fn test_solve_constraints_is_unsigned_fails_for_signed() {
1237 let mut unifier = Unifier::new();
1238 let constraints = vec![Constraint::is_unsigned(
1239 InferType::Concrete(Type::I32),
1240 Span::new(0, 5),
1241 )];
1242 let errors = unifier.solve_constraints(&constraints);
1243 assert_eq!(errors.len(), 1);
1244 assert!(matches!(
1245 errors[0].kind,
1246 UnifyResult::NotUnsigned { ty: Type::I32 }
1247 ));
1248 }
1249
1250 #[test]
1251 fn test_unify_array_same_element_same_length() {
1252 let mut unifier = Unifier::new();
1253 let arr1 = InferType::Array {
1254 element: Box::new(InferType::Concrete(Type::I32)),
1255 length: 5,
1256 };
1257 let arr2 = InferType::Array {
1258 element: Box::new(InferType::Concrete(Type::I32)),
1259 length: 5,
1260 };
1261 let result = unifier.unify(&arr1, &arr2);
1262 assert!(result.is_ok());
1263 }
1264
1265 #[test]
1266 fn test_unify_array_different_length() {
1267 let mut unifier = Unifier::new();
1268 let arr1 = InferType::Array {
1269 element: Box::new(InferType::Concrete(Type::I32)),
1270 length: 3,
1271 };
1272 let arr2 = InferType::Array {
1273 element: Box::new(InferType::Concrete(Type::I32)),
1274 length: 5,
1275 };
1276 let result = unifier.unify(&arr1, &arr2);
1277 assert!(!result.is_ok());
1278 assert!(matches!(result, UnifyResult::ArrayLengthMismatch { .. }));
1279 }
1280
1281 #[test]
1282 fn test_unify_array_different_element_type() {
1283 let mut unifier = Unifier::new();
1284 let arr1 = InferType::Array {
1285 element: Box::new(InferType::Concrete(Type::I32)),
1286 length: 3,
1287 };
1288 let arr2 = InferType::Array {
1289 element: Box::new(InferType::Concrete(Type::BOOL)),
1290 length: 3,
1291 };
1292 let result = unifier.unify(&arr1, &arr2);
1293 assert!(!result.is_ok());
1294 assert!(matches!(result, UnifyResult::TypeMismatch { .. }));
1295 }
1296
1297 #[test]
1298 fn test_unify_array_with_variable_element() {
1299 let mut unifier = Unifier::new();
1300 let v0 = TypeVarId::new(0);
1301 let arr1 = InferType::Array {
1302 element: Box::new(InferType::Var(v0)),
1303 length: 3,
1304 };
1305 let arr2 = InferType::Array {
1306 element: Box::new(InferType::Concrete(Type::I64)),
1307 length: 3,
1308 };
1309 let result = unifier.unify(&arr1, &arr2);
1310 assert!(result.is_ok());
1311
1312 assert_eq!(
1314 unifier.substitution.apply(&InferType::Var(v0)),
1315 InferType::Concrete(Type::I64)
1316 );
1317 }
1318
1319 #[test]
1320 fn test_unify_array_with_int_literal_element() {
1321 let mut unifier = Unifier::new();
1322 let arr1 = InferType::Array {
1323 element: Box::new(InferType::IntLiteral),
1324 length: 3,
1325 };
1326 let arr2 = InferType::Array {
1327 element: Box::new(InferType::Concrete(Type::I32)),
1328 length: 3,
1329 };
1330 let result = unifier.unify(&arr1, &arr2);
1331 assert!(result.is_ok());
1332 }
1333
1334 #[test]
1335 fn test_unify_array_with_concrete() {
1336 let mut unifier = Unifier::new();
1337 let arr = InferType::Array {
1338 element: Box::new(InferType::Concrete(Type::I32)),
1339 length: 3,
1340 };
1341 let concrete = InferType::Concrete(Type::I32);
1342 let result = unifier.unify(&arr, &concrete);
1343 assert!(!result.is_ok());
1344 assert!(matches!(result, UnifyResult::TypeMismatch { .. }));
1345 }
1346
1347 #[test]
1348 fn test_resolve_array_type() {
1349 let mut unifier = Unifier::new();
1350 let v0 = TypeVarId::new(0);
1351
1352 unifier.substitution.insert(
1354 v0,
1355 InferType::Array {
1356 element: Box::new(InferType::Concrete(Type::I32)),
1357 length: 10,
1358 },
1359 );
1360
1361 let resolved = unifier.resolve(&InferType::Var(v0));
1363 assert!(resolved.is_none());
1364
1365 let resolved_infer = unifier.resolve_infer_type(&InferType::Var(v0));
1367 match resolved_infer {
1368 InferType::Array { element, length } => {
1369 assert_eq!(*element, InferType::Concrete(Type::I32));
1370 assert_eq!(length, 10);
1371 }
1372 _ => panic!("Expected InferType::Array, got {:?}", resolved_infer),
1373 }
1374 }
1375
1376 #[test]
1377 fn test_unification_error_not_unsigned_message() {
1378 let error =
1379 UnificationError::new(UnifyResult::NotUnsigned { ty: Type::I32 }, Span::new(0, 5));
1380 let msg = error.message();
1381 assert!(msg.contains("unsigned"));
1382 assert!(msg.contains("i32"));
1383 }
1384
1385 #[test]
1386 fn test_unification_error_array_length_mismatch_message() {
1387 let error = UnificationError::new(
1388 UnifyResult::ArrayLengthMismatch {
1389 expected: 3,
1390 found: 5,
1391 },
1392 Span::new(0, 10),
1393 );
1394 let msg = error.message();
1395 assert!(msg.contains("3"));
1396 assert!(msg.contains("5"));
1397 }
1398
1399 #[test]
1400 fn test_unification_error_occurs_check_message() {
1401 let error = UnificationError::new(
1402 UnifyResult::OccursCheck {
1403 var: TypeVarId::new(0),
1404 ty: InferType::Array {
1405 element: Box::new(InferType::Var(TypeVarId::new(0))),
1406 length: 3,
1407 },
1408 },
1409 Span::new(0, 5),
1410 );
1411 let msg = error.message();
1412 assert!(msg.contains("infinite") || msg.contains("occurs"));
1413 }
1414}