Skip to main content

bhc_types/
lib.rs

1//! # BHC Type System
2//!
3//! This crate implements the type system for the Basel Haskell Compiler (BHC),
4//! including type representations, type inference, unification, and typeclass
5//! resolution.
6//!
7//! ## Overview
8//!
9//! The BHC type system is based on Hindley-Milner type inference with extensions
10//! for:
11//! - Higher-kinded types
12//! - Type classes with functional dependencies
13//! - Type families
14//! - GADTs (Generalized Algebraic Data Types)
15//! - Rank-N polymorphism (limited)
16//! - **M9 Dependent Types Preview**: Shape-indexed tensors with compile-time
17//!   dimension checking
18//!
19//! ## Core Types
20//!
21//! The type system is built around several key types:
22//!
23//! - [`Ty`]: The main type representation
24//! - [`TyVar`]: Type variables for polymorphism
25//! - [`TyId`]: Unique type identifiers
26//! - [`Kind`]: Kinds for higher-kinded types
27//! - [`Scheme`]: Polymorphic type schemes
28//! - [`TyNat`]: Type-level natural numbers (M9)
29//! - [`TyList`]: Type-level lists for shapes (M9)
30//!
31//! ## Type Inference
32//!
33//! Type inference proceeds in several phases:
34//!
35//! 1. **Constraint generation**: Walk the AST and generate type constraints
36//! 2. **Unification**: Solve constraints to find substitutions
37//! 3. **Generalization**: Generalize types at let-bindings
38//! 4. **Defaulting**: Apply defaulting rules for ambiguous types
39//!
40//! ## Shape-Indexed Tensors (M9)
41//!
42//! BHC supports shape-indexed tensor types for compile-time dimension checking:
43//!
44//! ```text
45//! matmul :: Tensor '[m, k] Float -> Tensor '[k, n] Float -> Tensor '[m, n] Float
46//! ```
47//!
48//! See [`nat`] and [`ty_list`] modules for type-level constructs.
49//!
50//! ## See Also
51//!
52//! - `bhc-hir`: High-level IR that uses these types
53//! - `bhc-core`: Core IR with explicit type annotations
54//! - H26-SPEC Section 4: Type System Specification
55//! - H26-SPEC Section 7: Tensor Model
56
57#![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/// A unique identifier for types within the type system.
73///
74/// Type IDs are used to reference types in the type environment
75/// and during type inference.
76#[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/// A type variable, used for polymorphic types.
91///
92/// Type variables can be either:
93/// - **Unification variables**: Created during type inference, to be solved
94/// - **Rigid variables**: Bound by forall quantifiers, cannot be unified
95#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
96pub struct TyVar {
97    /// Unique identifier for this type variable.
98    pub id: u32,
99    /// The kind of this type variable.
100    pub kind: Kind,
101}
102
103impl TyVar {
104    /// Creates a new type variable with the given ID and kind.
105    #[must_use]
106    pub fn new(id: u32, kind: Kind) -> Self {
107        Self { id, kind }
108    }
109
110    /// Creates a new type variable with kind `*` (Type).
111    #[must_use]
112    pub fn new_star(id: u32) -> Self {
113        Self::new(id, Kind::Star)
114    }
115}
116
117/// Kinds classify types, just as types classify values.
118///
119/// - `*` (Star): The kind of types that have values (e.g., `Int`, `Bool`)
120/// - `* -> *`: The kind of type constructors (e.g., `Maybe`, `[]`)
121/// - `Constraint`: The kind of type class constraints
122/// - `Nat`: The kind of type-level natural numbers (M9)
123/// - `List k`: The kind of type-level lists with element kind `k` (M9)
124#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
125pub enum Kind {
126    /// `*` - The kind of proper types (types that have values).
127    Star,
128    /// `k1 -> k2` - The kind of type constructors.
129    Arrow(Box<Kind>, Box<Kind>),
130    /// `Constraint` - The kind of type class constraints.
131    Constraint,
132    /// A kind variable, for kind inference.
133    Var(u32),
134
135    // === M9 Dependent Types Preview ===
136    /// `Nat` - The kind of type-level natural numbers.
137    ///
138    /// Used for tensor dimensions in shape-indexed types:
139    /// ```text
140    /// Tensor :: [Nat] -> * -> *
141    /// ```
142    Nat,
143
144    /// `List k` - The kind of type-level lists with element kind `k`.
145    ///
146    /// Primarily used as `[Nat]` for tensor shapes:
147    /// ```text
148    /// '[1024, 768] :: [Nat]
149    /// ```
150    List(Box<Kind>),
151}
152
153impl Kind {
154    /// Returns the kind `* -> *`.
155    #[must_use]
156    pub fn star_to_star() -> Self {
157        Self::Arrow(Box::new(Self::Star), Box::new(Self::Star))
158    }
159
160    /// Returns true if this is a simple `*` kind.
161    #[must_use]
162    pub fn is_star(&self) -> bool {
163        matches!(self, Self::Star)
164    }
165
166    /// Returns true if this is a `Nat` kind.
167    #[must_use]
168    pub fn is_nat(&self) -> bool {
169        matches!(self, Self::Nat)
170    }
171
172    /// Returns the kind `[Nat]` for tensor shapes.
173    #[must_use]
174    pub fn nat_list() -> Self {
175        Self::List(Box::new(Self::Nat))
176    }
177
178    /// Returns the kind `[Nat] -> * -> *` for the Tensor type constructor.
179    #[must_use]
180    pub fn tensor_kind() -> Self {
181        // Tensor :: [Nat] -> * -> *
182        Self::Arrow(Box::new(Self::nat_list()), Box::new(Self::star_to_star()))
183    }
184
185    /// Returns true if this is a list kind.
186    #[must_use]
187    pub fn is_list(&self) -> bool {
188        matches!(self, Self::List(_))
189    }
190
191    /// Returns the element kind if this is a list kind.
192    #[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/// The main type representation in BHC.
202///
203/// Types are represented as an algebraic data type with various constructors
204/// for different kinds of types in the Haskell type system.
205#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
206pub enum Ty {
207    /// A type variable (e.g., `a` in `a -> a`).
208    Var(TyVar),
209
210    /// A type constructor (e.g., `Int`, `Maybe`, `[]`).
211    Con(TyCon),
212
213    /// An unboxed primitive type (e.g., `Int#`, `Double#`).
214    /// Used in Numeric Profile for zero-overhead computation.
215    Prim(PrimTy),
216
217    /// Type application (e.g., `Maybe Int`, `Either String`).
218    App(Box<Ty>, Box<Ty>),
219
220    /// Function type (e.g., `Int -> Bool`).
221    /// This is sugar for `App(App(TyCon("->"), a), b)`.
222    Fun(Box<Ty>, Box<Ty>),
223
224    /// Tuple type (e.g., `(Int, Bool, String)`).
225    Tuple(Vec<Ty>),
226
227    /// List type (e.g., `[Int]`).
228    /// This is sugar for `App(TyCon("[]"), Int)`.
229    List(Box<Ty>),
230
231    /// A forall-quantified type (e.g., `forall a. a -> a`).
232    Forall(Vec<TyVar>, Box<Ty>),
233
234    /// An error type, used during type checking to allow recovery.
235    Error,
236
237    // === M9 Dependent Types Preview ===
238    /// A type-level natural number (e.g., `1024` in `Tensor '[1024] Float`).
239    ///
240    /// Has kind `Nat`. Used for tensor dimensions.
241    Nat(TyNat),
242
243    /// A type-level list (e.g., `'[1024, 768]` in `Tensor '[1024, 768] Float`).
244    ///
245    /// Has kind `[Nat]` when used for tensor shapes.
246    TyList(TyList),
247}
248
249impl Ty {
250    /// Creates a unit type `()`.
251    #[must_use]
252    pub fn unit() -> Self {
253        Self::Tuple(Vec::new())
254    }
255
256    /// Creates a function type `a -> b`.
257    #[must_use]
258    pub fn fun(from: Ty, to: Ty) -> Self {
259        Self::Fun(Box::new(from), Box::new(to))
260    }
261
262    /// Creates a list type `[a]`.
263    #[must_use]
264    pub fn list(elem: Ty) -> Self {
265        Self::List(Box::new(elem))
266    }
267
268    /// Creates an unboxed `Int#` type (64-bit signed integer).
269    #[must_use]
270    pub fn int_prim() -> Self {
271        Self::Prim(PrimTy::I64)
272    }
273
274    /// Creates an unboxed `Double#` type (64-bit float).
275    #[must_use]
276    pub fn double_prim() -> Self {
277        Self::Prim(PrimTy::F64)
278    }
279
280    /// Creates an unboxed `Float#` type (32-bit float).
281    #[must_use]
282    pub fn float_prim() -> Self {
283        Self::Prim(PrimTy::F32)
284    }
285
286    /// Returns true if this is an unboxed primitive type.
287    #[must_use]
288    pub fn is_prim(&self) -> bool {
289        matches!(self, Self::Prim(_))
290    }
291
292    /// Returns the primitive type if this is a Prim variant.
293    #[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    /// Returns true if this is a function type.
302    #[must_use]
303    pub fn is_fun(&self) -> bool {
304        matches!(self, Self::Fun(_, _))
305    }
306
307    /// Returns true if this is an error type.
308    #[must_use]
309    pub fn is_error(&self) -> bool {
310        matches!(self, Self::Error)
311    }
312
313    /// Returns the free type variables in this type.
314    #[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            // M9: Type-level naturals and lists
349            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    /// Returns true if this type contains no unification variables.
367    #[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    /// Creates a type-level natural literal.
382    #[must_use]
383    pub fn nat_lit(n: u64) -> Self {
384        Self::Nat(TyNat::lit(n))
385    }
386
387    /// Creates a shape type from dimension values.
388    #[must_use]
389    pub fn shape(dims: &[u64]) -> Self {
390        Self::TyList(TyList::shape_from_dims(dims))
391    }
392
393    /// Returns true if this is a type-level natural.
394    #[must_use]
395    pub fn is_nat(&self) -> bool {
396        matches!(self, Self::Nat(_))
397    }
398
399    /// Returns true if this is a type-level list.
400    #[must_use]
401    pub fn is_ty_list(&self) -> bool {
402        matches!(self, Self::TyList(_))
403    }
404
405    /// Returns the type-level natural if this is a Nat variant.
406    #[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    /// Returns the type-level list if this is a `TyList` variant.
415    #[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/// A type constructor with its name and kind.
458#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
459pub struct TyCon {
460    /// The name of the type constructor.
461    pub name: Symbol,
462    /// The kind of the type constructor.
463    pub kind: Kind,
464}
465
466impl TyCon {
467    /// Creates a new type constructor with the given name and kind.
468    #[must_use]
469    pub fn new(name: Symbol, kind: Kind) -> Self {
470        Self { name, kind }
471    }
472}
473
474/// A polymorphic type scheme (forall-quantified type).
475///
476/// A scheme represents a type that may be instantiated with different
477/// type arguments. For example, `forall a. a -> a` can be instantiated
478/// as `Int -> Int` or `Bool -> Bool`.
479#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
480pub struct Scheme {
481    /// The bound type variables.
482    pub vars: Vec<TyVar>,
483    /// The constraints on the type variables.
484    pub constraints: Vec<Constraint>,
485    /// The underlying type.
486    pub ty: Ty,
487}
488
489impl Scheme {
490    /// Creates a monomorphic scheme (no quantified variables).
491    #[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    /// Creates a polymorphic scheme with the given variables and type.
501    #[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    /// Creates a qualified scheme with constraints.
511    #[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    /// Returns true if this is a monomorphic scheme.
521    #[must_use]
522    pub fn is_mono(&self) -> bool {
523        self.vars.is_empty()
524    }
525}
526
527/// A type class constraint (e.g., `Eq a`, `Num a`).
528#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
529pub struct Constraint {
530    /// The type class name.
531    pub class: Symbol,
532    /// The type arguments to the constraint.
533    pub args: Vec<Ty>,
534    /// Source location of the constraint.
535    pub span: Span,
536}
537
538impl Constraint {
539    /// Creates a new constraint with a single type argument.
540    #[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    /// Creates a new constraint with multiple type arguments.
550    #[must_use]
551    pub fn new_multi(class: Symbol, args: Vec<Ty>, span: Span) -> Self {
552        Self { class, args, span }
553    }
554}
555
556/// A substitution mapping type variables to types.
557#[derive(Clone, Debug, Default, Serialize, Deserialize)]
558pub struct Subst {
559    /// The mapping from type variable IDs to types.
560    mapping: rustc_hash::FxHashMap<u32, Ty>,
561}
562
563impl Subst {
564    /// Creates an empty substitution.
565    #[must_use]
566    pub fn new() -> Self {
567        Self::default()
568    }
569
570    /// Inserts a mapping from a type variable to a type.
571    pub fn insert(&mut self, var: &TyVar, ty: Ty) {
572        self.mapping.insert(var.id, ty);
573    }
574
575    /// Looks up a type variable in the substitution.
576    #[must_use]
577    pub fn get(&self, var: &TyVar) -> Option<&Ty> {
578        self.mapping.get(&var.id)
579    }
580
581    /// Checks if a type variable has a mapping in this substitution.
582    #[must_use]
583    pub fn contains(&self, var: &TyVar) -> bool {
584        self.mapping.contains_key(&var.id)
585    }
586
587    /// Applies this substitution to a type.
588    #[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                // Don't substitute bound variables
599                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            // M9: Apply substitution to type-level naturals and lists
607            Ty::Nat(n) => Ty::Nat(self.apply_nat(n)),
608            Ty::TyList(l) => Ty::TyList(self.apply_ty_list(l)),
609        }
610    }
611
612    /// Applies this substitution to a type-level natural.
613    #[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                // Check if this variable is mapped to a Nat type
619                match self.get(v) {
620                    Some(Ty::Nat(replacement)) => replacement.clone(),
621                    Some(_) => n.clone(), // Type mismatch, keep original
622                    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    /// Applies this substitution to a type-level list.
631    #[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                // Check if this variable is mapped to a TyList type
638                match self.get(v) {
639                    Some(Ty::TyList(replacement)) => replacement.clone(),
640                    Some(_) => l.clone(), // Type mismatch, keep original
641                    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    /// Composes two substitutions: (self . other)
651    #[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    /// Returns true if this substitution is empty.
664    #[must_use]
665    pub fn is_empty(&self) -> bool {
666        self.mapping.is_empty()
667    }
668}
669
670/// Unboxed primitive types for the Numeric Profile.
671///
672/// These types represent machine-level primitives that are:
673/// - Unboxed: No heap allocation, stored directly in registers/stack
674/// - Strict: Always fully evaluated, no thunks
675/// - Fixed-size: Known size at compile time
676///
677/// Used in Numeric Profile for zero-overhead numeric computation.
678/// See H26-SPEC Section 6.2 for unboxed type requirements.
679#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
680pub enum PrimTy {
681    /// 32-bit signed integer (Int32#).
682    I32,
683    /// 64-bit signed integer (Int64#, Int#).
684    I64,
685    /// 32-bit unsigned integer (Word32#).
686    U32,
687    /// 64-bit unsigned integer (Word64#, Word#).
688    U64,
689    /// 32-bit IEEE 754 float (Float#).
690    F32,
691    /// 64-bit IEEE 754 double (Double#).
692    F64,
693    /// 8-bit character/byte (Char#).
694    Char,
695    /// Machine-sized pointer (Addr#).
696    Addr,
697}
698
699impl PrimTy {
700    /// Returns the size in bytes of this primitive type.
701    #[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, // Unicode code point
707        }
708    }
709
710    /// Returns the alignment requirement in bytes.
711    #[must_use]
712    pub const fn alignment(self) -> usize {
713        self.size_bytes()
714    }
715
716    /// Returns the GHC-style name for this primitive type.
717    #[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    /// Returns true if this is a signed integer type.
732    #[must_use]
733    pub const fn is_signed_int(self) -> bool {
734        matches!(self, Self::I32 | Self::I64)
735    }
736
737    /// Returns true if this is an unsigned integer type.
738    #[must_use]
739    pub const fn is_unsigned_int(self) -> bool {
740        matches!(self, Self::U32 | Self::U64)
741    }
742
743    /// Returns true if this is a floating-point type.
744    #[must_use]
745    pub const fn is_float(self) -> bool {
746        matches!(self, Self::F32 | Self::F64)
747    }
748
749    /// Returns true if this is a numeric type.
750    #[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/// Match a pattern type against a target type, returning a substitution.
766///
767/// Performs one-way pattern matching where type variables in the pattern
768/// are bound to concrete types in the target. This enables polymorphic instance
769/// matching like `instance Eq a => Eq [a]` to match `Eq [Int]` with `{a -> Int}`.
770///
771/// # Arguments
772/// * `pattern` - The instance type pattern (may contain type variables)
773/// * `target` - The concrete type to match against
774///
775/// # Returns
776/// `Some(subst)` if the match succeeds, where `subst` maps type variables to types.
777/// `None` if the types cannot be matched.
778#[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
788/// Helper function that accumulates substitutions during type matching.
789///
790/// Returns `true` if the pattern matches the target, accumulating bindings
791/// from type variables to concrete types in `subst`.
792pub fn types_match_with_subst(pattern: &Ty, target: &Ty, subst: &mut Subst) -> bool {
793    match (pattern, target) {
794        // Type variable in pattern: bind to target type
795        (Ty::Var(v), _) => {
796            if let Some(bound_ty) = subst.get(v) {
797                // Must match the existing binding
798                types_equal(bound_ty, target)
799            } else {
800                subst.insert(v, target.clone());
801                true
802            }
803        }
804
805        // Type constructors must match by name
806        (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
807
808        // Primitive types must match exactly
809        (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
810
811        // Type applications: match both the function and argument
812        (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        // Function types: match argument and result types
817        (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        // Tuple types: must have same length and matching elements
822        (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        // List types: match element types
828        (Ty::List(e1), Ty::List(e2)) => types_match_with_subst(e1, e2, subst),
829
830        // A list `[e]` is sugar for `[] e`, so an application pattern `f a` can
831        // match it (and vice-versa): match `f` against the list type constructor
832        // and `a` against the element. This lets a higher-kinded instance head
833        // like `Walkable a (t b)` resolve against a concrete `Walkable a [b]`.
834        (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        // Forall types: match bodies (simplified, no alpha-renaming)
844        (Ty::Forall(_, body1), Ty::Forall(_, body2)) => types_match_with_subst(body1, body2, subst),
845
846        // Type-level naturals
847        (Ty::Nat(n1), Ty::Nat(n2)) => n1 == n2,
848
849        // Type-level lists
850        (Ty::TyList(l1), Ty::TyList(l2)) => l1 == l2,
851
852        // Error types match anything (to avoid cascading errors)
853        (Ty::Error, _) | (_, Ty::Error) => true,
854
855        // All other combinations don't match
856        _ => false,
857    }
858}
859
860/// Match multiple pattern types against target types, returning combined substitution.
861///
862/// Both lists must have the same length. The resulting substitution combines
863/// all bindings from matching each pair.
864#[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/// Check if two types are structurally equal.
880#[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/// Errors that can occur during type operations.
899#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
900pub enum TypeError {
901    /// Unification failed between two types.
902    #[error("type mismatch: expected {expected}, found {found}")]
903    Mismatch {
904        /// The expected type.
905        expected: String,
906        /// The found type.
907        found: String,
908        /// The location of the error.
909        span: Span,
910    },
911
912    /// Occurs check failed (infinite type).
913    #[error("infinite type: {var} occurs in {ty}")]
914    OccursCheck {
915        /// The type variable.
916        var: String,
917        /// The type containing the variable.
918        ty: String,
919        /// The location of the error.
920        span: Span,
921    },
922
923    /// Unbound type variable.
924    #[error("unbound type variable: {name}")]
925    UnboundVar {
926        /// The variable name.
927        name: String,
928        /// The location of the error.
929        span: Span,
930    },
931
932    /// Kind mismatch.
933    #[error("kind mismatch: expected {expected}, found {found}")]
934    KindMismatch {
935        /// The expected kind.
936        expected: String,
937        /// The found kind.
938        found: String,
939        /// The location of the error.
940        span: Span,
941    },
942
943    /// Ambiguous type variable.
944    #[error("ambiguous type variable: {var}")]
945    Ambiguous {
946        /// The ambiguous variable.
947        var: String,
948        /// The location of the error.
949        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        // `a -> b` has free vars {a, b}
963        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        // SAFETY: 0 is a valid symbol index for testing purposes
974        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        // SAFETY: 0 is a valid symbol index for testing purposes
994        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        // Non-prim type
1039        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        // Primitive types have no free type variables
1047        let prim = Ty::int_prim();
1048        assert!(prim.free_vars().is_empty());
1049    }
1050
1051    #[test]
1052    fn test_subst_prim_unchanged() {
1053        // Substitution should leave primitive types unchanged
1054        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        // Pattern: [] a, Target: [] Int
1091        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}