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#![warn(clippy::all)]
59#![warn(clippy::pedantic)]
60
61pub mod dyn_tensor;
62pub mod nat;
63pub mod ty_list;
64
65pub use dyn_tensor::{dyn_tensor_of, dyn_tensor_tycon, shape_witness_of, shape_witness_tycon};
66pub use nat::TyNat;
67pub use ty_list::TyList;
68
69use bhc_index::Idx;
70use bhc_intern::Symbol;
71use bhc_span::Span;
72use serde::{Deserialize, Serialize};
73
74/// A unique identifier for types within the type system.
75///
76/// Type IDs are used to reference types in the type environment
77/// and during type inference.
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
79pub struct TyId(u32);
80
81impl Idx for TyId {
82    #[allow(clippy::cast_possible_truncation)]
83    fn new(idx: usize) -> Self {
84        Self(idx as u32)
85    }
86
87    fn index(self) -> usize {
88        self.0 as usize
89    }
90}
91
92/// A type variable, used for polymorphic types.
93///
94/// Type variables can be either:
95/// - **Unification variables**: Created during type inference, to be solved
96/// - **Rigid variables**: Bound by forall quantifiers, cannot be unified
97#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
98pub struct TyVar {
99    /// Unique identifier for this type variable.
100    pub id: u32,
101    /// The kind of this type variable.
102    pub kind: Kind,
103}
104
105impl TyVar {
106    /// Creates a new type variable with the given ID and kind.
107    #[must_use]
108    pub fn new(id: u32, kind: Kind) -> Self {
109        Self { id, kind }
110    }
111
112    /// Creates a new type variable with kind `*` (Type).
113    #[must_use]
114    pub fn new_star(id: u32) -> Self {
115        Self::new(id, Kind::Star)
116    }
117}
118
119/// Kinds classify types, just as types classify values.
120///
121/// - `*` (Star): The kind of types that have values (e.g., `Int`, `Bool`)
122/// - `* -> *`: The kind of type constructors (e.g., `Maybe`, `[]`)
123/// - `Constraint`: The kind of type class constraints
124/// - `Nat`: The kind of type-level natural numbers (M9)
125/// - `List k`: The kind of type-level lists with element kind `k` (M9)
126#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
127pub enum Kind {
128    /// `*` - The kind of proper types (types that have values).
129    Star,
130    /// `k1 -> k2` - The kind of type constructors.
131    Arrow(Box<Kind>, Box<Kind>),
132    /// `Constraint` - The kind of type class constraints.
133    Constraint,
134    /// A kind variable, for kind inference.
135    Var(u32),
136
137    // === M9 Dependent Types Preview ===
138    /// `Nat` - The kind of type-level natural numbers.
139    ///
140    /// Used for tensor dimensions in shape-indexed types:
141    /// ```text
142    /// Tensor :: [Nat] -> * -> *
143    /// ```
144    Nat,
145
146    /// `List k` - The kind of type-level lists with element kind `k`.
147    ///
148    /// Primarily used as `[Nat]` for tensor shapes:
149    /// ```text
150    /// '[1024, 768] :: [Nat]
151    /// ```
152    List(Box<Kind>),
153}
154
155impl Kind {
156    /// Returns the kind `* -> *`.
157    #[must_use]
158    pub fn star_to_star() -> Self {
159        Self::Arrow(Box::new(Self::Star), Box::new(Self::Star))
160    }
161
162    /// Returns true if this is a simple `*` kind.
163    #[must_use]
164    pub fn is_star(&self) -> bool {
165        matches!(self, Self::Star)
166    }
167
168    /// Returns true if this is a `Nat` kind.
169    #[must_use]
170    pub fn is_nat(&self) -> bool {
171        matches!(self, Self::Nat)
172    }
173
174    /// Returns the kind `[Nat]` for tensor shapes.
175    #[must_use]
176    pub fn nat_list() -> Self {
177        Self::List(Box::new(Self::Nat))
178    }
179
180    /// Returns the kind `[Nat] -> * -> *` for the Tensor type constructor.
181    #[must_use]
182    pub fn tensor_kind() -> Self {
183        // Tensor :: [Nat] -> * -> *
184        Self::Arrow(Box::new(Self::nat_list()), Box::new(Self::star_to_star()))
185    }
186
187    /// Returns true if this is a list kind.
188    #[must_use]
189    pub fn is_list(&self) -> bool {
190        matches!(self, Self::List(_))
191    }
192
193    /// Returns the element kind if this is a list kind.
194    #[must_use]
195    pub fn list_elem_kind(&self) -> Option<&Kind> {
196        match self {
197            Self::List(k) => Some(k),
198            _ => None,
199        }
200    }
201}
202
203/// The main type representation in BHC.
204///
205/// Types are represented as an algebraic data type with various constructors
206/// for different kinds of types in the Haskell type system.
207#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
208pub enum Ty {
209    /// A type variable (e.g., `a` in `a -> a`).
210    Var(TyVar),
211
212    /// A type constructor (e.g., `Int`, `Maybe`, `[]`).
213    Con(TyCon),
214
215    /// An unboxed primitive type (e.g., `Int#`, `Double#`).
216    /// Used in Numeric Profile for zero-overhead computation.
217    Prim(PrimTy),
218
219    /// Type application (e.g., `Maybe Int`, `Either String`).
220    App(Box<Ty>, Box<Ty>),
221
222    /// Function type (e.g., `Int -> Bool`).
223    /// This is sugar for `App(App(TyCon("->"), a), b)`.
224    Fun(Box<Ty>, Box<Ty>),
225
226    /// Tuple type (e.g., `(Int, Bool, String)`).
227    Tuple(Vec<Ty>),
228
229    /// List type (e.g., `[Int]`).
230    /// This is sugar for `App(TyCon("[]"), Int)`.
231    List(Box<Ty>),
232
233    /// A forall-quantified type (e.g., `forall a. a -> a`).
234    Forall(Vec<TyVar>, Box<Ty>),
235
236    /// An error type, used during type checking to allow recovery.
237    Error,
238
239    // === M9 Dependent Types Preview ===
240    /// A type-level natural number (e.g., `1024` in `Tensor '[1024] Float`).
241    ///
242    /// Has kind `Nat`. Used for tensor dimensions.
243    Nat(TyNat),
244
245    /// A type-level list (e.g., `'[1024, 768]` in `Tensor '[1024, 768] Float`).
246    ///
247    /// Has kind `[Nat]` when used for tensor shapes.
248    TyList(TyList),
249}
250
251impl Ty {
252    /// Creates a unit type `()`.
253    #[must_use]
254    pub fn unit() -> Self {
255        Self::Tuple(Vec::new())
256    }
257
258    /// Creates a function type `a -> b`.
259    #[must_use]
260    pub fn fun(from: Ty, to: Ty) -> Self {
261        Self::Fun(Box::new(from), Box::new(to))
262    }
263
264    /// Creates a list type `[a]`.
265    #[must_use]
266    pub fn list(elem: Ty) -> Self {
267        Self::List(Box::new(elem))
268    }
269
270    /// Creates an unboxed `Int#` type (64-bit signed integer).
271    #[must_use]
272    pub fn int_prim() -> Self {
273        Self::Prim(PrimTy::I64)
274    }
275
276    /// Creates an unboxed `Double#` type (64-bit float).
277    #[must_use]
278    pub fn double_prim() -> Self {
279        Self::Prim(PrimTy::F64)
280    }
281
282    /// Creates an unboxed `Float#` type (32-bit float).
283    #[must_use]
284    pub fn float_prim() -> Self {
285        Self::Prim(PrimTy::F32)
286    }
287
288    /// Returns true if this is an unboxed primitive type.
289    #[must_use]
290    pub fn is_prim(&self) -> bool {
291        matches!(self, Self::Prim(_))
292    }
293
294    /// Returns the primitive type if this is a Prim variant.
295    #[must_use]
296    pub fn as_prim(&self) -> Option<PrimTy> {
297        match self {
298            Self::Prim(p) => Some(*p),
299            _ => None,
300        }
301    }
302
303    /// Returns true if this is a function type.
304    #[must_use]
305    pub fn is_fun(&self) -> bool {
306        matches!(self, Self::Fun(_, _))
307    }
308
309    /// Returns true if this is an error type.
310    #[must_use]
311    pub fn is_error(&self) -> bool {
312        matches!(self, Self::Error)
313    }
314
315    /// Returns the free type variables in this type.
316    #[must_use]
317    pub fn free_vars(&self) -> Vec<TyVar> {
318        let mut vars = Vec::new();
319        self.collect_free_vars(&mut vars);
320        vars
321    }
322
323    fn collect_free_vars(&self, vars: &mut Vec<TyVar>) {
324        match self {
325            Self::Var(v) => {
326                if !vars.contains(v) {
327                    vars.push(v.clone());
328                }
329            }
330            Self::Con(_) | Self::Prim(_) | Self::Error => {}
331            Self::App(f, a) | Self::Fun(f, a) => {
332                f.collect_free_vars(vars);
333                a.collect_free_vars(vars);
334            }
335            Self::Tuple(tys) => {
336                for ty in tys {
337                    ty.collect_free_vars(vars);
338                }
339            }
340            Self::List(elem) => elem.collect_free_vars(vars),
341            Self::Forall(bound, body) => {
342                let mut body_vars = Vec::new();
343                body.collect_free_vars(&mut body_vars);
344                for v in body_vars {
345                    if !bound.contains(&v) && !vars.contains(&v) {
346                        vars.push(v);
347                    }
348                }
349            }
350            // M9: Type-level naturals and lists
351            Self::Nat(n) => {
352                for v in n.free_vars() {
353                    if !vars.contains(&v) {
354                        vars.push(v);
355                    }
356                }
357            }
358            Self::TyList(l) => {
359                for v in l.free_vars() {
360                    if !vars.contains(&v) {
361                        vars.push(v);
362                    }
363                }
364            }
365        }
366    }
367
368    /// Returns true if this type contains no unification variables.
369    #[must_use]
370    pub fn is_ground(&self) -> bool {
371        match self {
372            Self::Var(_) => false,
373            Self::Con(_) | Self::Prim(_) | Self::Error => true,
374            Self::App(f, a) | Self::Fun(f, a) => f.is_ground() && a.is_ground(),
375            Self::Tuple(tys) => tys.iter().all(Ty::is_ground),
376            Self::List(elem) => elem.is_ground(),
377            Self::Forall(_, body) => body.is_ground(),
378            Self::Nat(n) => n.is_ground(),
379            Self::TyList(l) => l.is_ground(),
380        }
381    }
382
383    /// Creates a type-level natural literal.
384    #[must_use]
385    pub fn nat_lit(n: u64) -> Self {
386        Self::Nat(TyNat::lit(n))
387    }
388
389    /// Creates a shape type from dimension values.
390    #[must_use]
391    pub fn shape(dims: &[u64]) -> Self {
392        Self::TyList(TyList::shape_from_dims(dims))
393    }
394
395    /// Returns true if this is a type-level natural.
396    #[must_use]
397    pub fn is_nat(&self) -> bool {
398        matches!(self, Self::Nat(_))
399    }
400
401    /// Returns true if this is a type-level list.
402    #[must_use]
403    pub fn is_ty_list(&self) -> bool {
404        matches!(self, Self::TyList(_))
405    }
406
407    /// Returns the type-level natural if this is a Nat variant.
408    #[must_use]
409    pub fn as_nat(&self) -> Option<&TyNat> {
410        match self {
411            Self::Nat(n) => Some(n),
412            _ => None,
413        }
414    }
415
416    /// Returns the type-level list if this is a TyList variant.
417    #[must_use]
418    pub fn as_ty_list(&self) -> Option<&TyList> {
419        match self {
420            Self::TyList(l) => Some(l),
421            _ => None,
422        }
423    }
424}
425
426impl std::fmt::Display for Ty {
427    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428        match self {
429            Self::Var(v) => write!(f, "t{}", v.id),
430            Self::Con(c) => write!(f, "{}", c.name.as_str()),
431            Self::Prim(p) => write!(f, "{p}"),
432            Self::App(fun, arg) => write!(f, "({fun} {arg})"),
433            Self::Fun(from, to) => write!(f, "({from} -> {to})"),
434            Self::Tuple(tys) => {
435                write!(f, "(")?;
436                for (i, ty) in tys.iter().enumerate() {
437                    if i > 0 {
438                        write!(f, ", ")?;
439                    }
440                    write!(f, "{ty}")?;
441                }
442                write!(f, ")")
443            }
444            Self::List(elem) => write!(f, "[{elem}]"),
445            Self::Forall(vars, body) => {
446                write!(f, "forall")?;
447                for v in vars {
448                    write!(f, " t{}", v.id)?;
449                }
450                write!(f, ". {body}")
451            }
452            Self::Error => write!(f, "<error>"),
453            Self::Nat(n) => write!(f, "{n}"),
454            Self::TyList(l) => write!(f, "{l}"),
455        }
456    }
457}
458
459/// A type constructor with its name and kind.
460#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
461pub struct TyCon {
462    /// The name of the type constructor.
463    pub name: Symbol,
464    /// The kind of the type constructor.
465    pub kind: Kind,
466}
467
468impl TyCon {
469    /// Creates a new type constructor with the given name and kind.
470    #[must_use]
471    pub fn new(name: Symbol, kind: Kind) -> Self {
472        Self { name, kind }
473    }
474}
475
476/// A polymorphic type scheme (forall-quantified type).
477///
478/// A scheme represents a type that may be instantiated with different
479/// type arguments. For example, `forall a. a -> a` can be instantiated
480/// as `Int -> Int` or `Bool -> Bool`.
481#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
482pub struct Scheme {
483    /// The bound type variables.
484    pub vars: Vec<TyVar>,
485    /// The constraints on the type variables.
486    pub constraints: Vec<Constraint>,
487    /// The underlying type.
488    pub ty: Ty,
489}
490
491impl Scheme {
492    /// Creates a monomorphic scheme (no quantified variables).
493    #[must_use]
494    pub fn mono(ty: Ty) -> Self {
495        Self {
496            vars: Vec::new(),
497            constraints: Vec::new(),
498            ty,
499        }
500    }
501
502    /// Creates a polymorphic scheme with the given variables and type.
503    #[must_use]
504    pub fn poly(vars: Vec<TyVar>, ty: Ty) -> Self {
505        Self {
506            vars,
507            constraints: Vec::new(),
508            ty,
509        }
510    }
511
512    /// Creates a qualified scheme with constraints.
513    #[must_use]
514    pub fn qualified(vars: Vec<TyVar>, constraints: Vec<Constraint>, ty: Ty) -> Self {
515        Self {
516            vars,
517            constraints,
518            ty,
519        }
520    }
521
522    /// Returns true if this is a monomorphic scheme.
523    #[must_use]
524    pub fn is_mono(&self) -> bool {
525        self.vars.is_empty()
526    }
527}
528
529/// A type class constraint (e.g., `Eq a`, `Num a`).
530#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
531pub struct Constraint {
532    /// The type class name.
533    pub class: Symbol,
534    /// The type arguments to the constraint.
535    pub args: Vec<Ty>,
536    /// Source location of the constraint.
537    pub span: Span,
538}
539
540impl Constraint {
541    /// Creates a new constraint with a single type argument.
542    #[must_use]
543    pub fn new(class: Symbol, ty: Ty, span: Span) -> Self {
544        Self {
545            class,
546            args: vec![ty],
547            span,
548        }
549    }
550
551    /// Creates a new constraint with multiple type arguments.
552    #[must_use]
553    pub fn new_multi(class: Symbol, args: Vec<Ty>, span: Span) -> Self {
554        Self { class, args, span }
555    }
556}
557
558/// A substitution mapping type variables to types.
559#[derive(Clone, Debug, Default, Serialize, Deserialize)]
560pub struct Subst {
561    /// The mapping from type variable IDs to types.
562    mapping: rustc_hash::FxHashMap<u32, Ty>,
563}
564
565impl Subst {
566    /// Creates an empty substitution.
567    #[must_use]
568    pub fn new() -> Self {
569        Self::default()
570    }
571
572    /// Inserts a mapping from a type variable to a type.
573    pub fn insert(&mut self, var: &TyVar, ty: Ty) {
574        self.mapping.insert(var.id, ty);
575    }
576
577    /// Looks up a type variable in the substitution.
578    #[must_use]
579    pub fn get(&self, var: &TyVar) -> Option<&Ty> {
580        self.mapping.get(&var.id)
581    }
582
583    /// Checks if a type variable has a mapping in this substitution.
584    #[must_use]
585    pub fn contains(&self, var: &TyVar) -> bool {
586        self.mapping.contains_key(&var.id)
587    }
588
589    /// Applies this substitution to a type.
590    #[must_use]
591    pub fn apply(&self, ty: &Ty) -> Ty {
592        match ty {
593            Ty::Var(v) => self.get(v).cloned().unwrap_or_else(|| ty.clone()),
594            Ty::Con(_) | Ty::Prim(_) => ty.clone(),
595            Ty::App(f, a) => Ty::App(Box::new(self.apply(f)), Box::new(self.apply(a))),
596            Ty::Fun(from, to) => Ty::Fun(Box::new(self.apply(from)), Box::new(self.apply(to))),
597            Ty::Tuple(tys) => Ty::Tuple(tys.iter().map(|t| self.apply(t)).collect()),
598            Ty::List(elem) => Ty::List(Box::new(self.apply(elem))),
599            Ty::Forall(vars, body) => {
600                // Don't substitute bound variables
601                let mut inner = self.clone();
602                for v in vars {
603                    inner.mapping.remove(&v.id);
604                }
605                Ty::Forall(vars.clone(), Box::new(inner.apply(body)))
606            }
607            Ty::Error => Ty::Error,
608            // M9: Apply substitution to type-level naturals and lists
609            Ty::Nat(n) => Ty::Nat(self.apply_nat(n)),
610            Ty::TyList(l) => Ty::TyList(self.apply_ty_list(l)),
611        }
612    }
613
614    /// Applies this substitution to a type-level natural.
615    #[must_use]
616    pub fn apply_nat(&self, n: &TyNat) -> TyNat {
617        match n {
618            TyNat::Lit(val) => TyNat::Lit(*val),
619            TyNat::Var(v) => {
620                // Check if this variable is mapped to a Nat type
621                match self.get(v) {
622                    Some(Ty::Nat(replacement)) => replacement.clone(),
623                    Some(_) => n.clone(), // Type mismatch, keep original
624                    None => n.clone(),
625                }
626            }
627            TyNat::Add(a, b) => TyNat::add(self.apply_nat(a), self.apply_nat(b)),
628            TyNat::Mul(a, b) => TyNat::mul(self.apply_nat(a), self.apply_nat(b)),
629        }
630    }
631
632    /// Applies this substitution to a type-level list.
633    #[must_use]
634    pub fn apply_ty_list(&self, l: &TyList) -> TyList {
635        match l {
636            TyList::Nil => TyList::Nil,
637            TyList::Cons(head, tail) => TyList::cons(self.apply(head), self.apply_ty_list(tail)),
638            TyList::Var(v) => {
639                // Check if this variable is mapped to a TyList type
640                match self.get(v) {
641                    Some(Ty::TyList(replacement)) => replacement.clone(),
642                    Some(_) => l.clone(), // Type mismatch, keep original
643                    None => l.clone(),
644                }
645            }
646            TyList::Append(xs, ys) => {
647                TyList::append(self.apply_ty_list(xs), self.apply_ty_list(ys))
648            }
649        }
650    }
651
652    /// Composes two substitutions: (self . other)
653    #[must_use]
654    pub fn compose(&self, other: &Self) -> Self {
655        let mut result = Self::new();
656        for (k, v) in &other.mapping {
657            result.mapping.insert(*k, self.apply(v));
658        }
659        for (k, v) in &self.mapping {
660            result.mapping.entry(*k).or_insert_with(|| v.clone());
661        }
662        result
663    }
664
665    /// Returns true if this substitution is empty.
666    #[must_use]
667    pub fn is_empty(&self) -> bool {
668        self.mapping.is_empty()
669    }
670}
671
672/// Unboxed primitive types for the Numeric Profile.
673///
674/// These types represent machine-level primitives that are:
675/// - Unboxed: No heap allocation, stored directly in registers/stack
676/// - Strict: Always fully evaluated, no thunks
677/// - Fixed-size: Known size at compile time
678///
679/// Used in Numeric Profile for zero-overhead numeric computation.
680/// See H26-SPEC Section 6.2 for unboxed type requirements.
681#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
682pub enum PrimTy {
683    /// 32-bit signed integer (Int32#).
684    I32,
685    /// 64-bit signed integer (Int64#, Int#).
686    I64,
687    /// 32-bit unsigned integer (Word32#).
688    U32,
689    /// 64-bit unsigned integer (Word64#, Word#).
690    U64,
691    /// 32-bit IEEE 754 float (Float#).
692    F32,
693    /// 64-bit IEEE 754 double (Double#).
694    F64,
695    /// 8-bit character/byte (Char#).
696    Char,
697    /// Machine-sized pointer (Addr#).
698    Addr,
699}
700
701impl PrimTy {
702    /// Returns the size in bytes of this primitive type.
703    #[must_use]
704    pub const fn size_bytes(self) -> usize {
705        match self {
706            Self::I32 | Self::U32 | Self::F32 => 4,
707            Self::I64 | Self::U64 | Self::F64 | Self::Addr => 8,
708            Self::Char => 4, // Unicode code point
709        }
710    }
711
712    /// Returns the alignment requirement in bytes.
713    #[must_use]
714    pub const fn alignment(self) -> usize {
715        self.size_bytes()
716    }
717
718    /// Returns the GHC-style name for this primitive type.
719    #[must_use]
720    pub const fn name(self) -> &'static str {
721        match self {
722            Self::I32 => "Int32#",
723            Self::I64 => "Int#",
724            Self::U32 => "Word32#",
725            Self::U64 => "Word#",
726            Self::F32 => "Float#",
727            Self::F64 => "Double#",
728            Self::Char => "Char#",
729            Self::Addr => "Addr#",
730        }
731    }
732
733    /// Returns true if this is a signed integer type.
734    #[must_use]
735    pub const fn is_signed_int(self) -> bool {
736        matches!(self, Self::I32 | Self::I64)
737    }
738
739    /// Returns true if this is an unsigned integer type.
740    #[must_use]
741    pub const fn is_unsigned_int(self) -> bool {
742        matches!(self, Self::U32 | Self::U64)
743    }
744
745    /// Returns true if this is a floating-point type.
746    #[must_use]
747    pub const fn is_float(self) -> bool {
748        matches!(self, Self::F32 | Self::F64)
749    }
750
751    /// Returns true if this is a numeric type.
752    #[must_use]
753    pub const fn is_numeric(self) -> bool {
754        matches!(
755            self,
756            Self::I32 | Self::I64 | Self::U32 | Self::U64 | Self::F32 | Self::F64
757        )
758    }
759}
760
761impl std::fmt::Display for PrimTy {
762    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
763        write!(f, "{}", self.name())
764    }
765}
766
767/// Match a pattern type against a target type, returning a substitution.
768///
769/// Performs one-way pattern matching where type variables in the pattern
770/// are bound to concrete types in the target. This enables polymorphic instance
771/// matching like `instance Eq a => Eq [a]` to match `Eq [Int]` with `{a -> Int}`.
772///
773/// # Arguments
774/// * `pattern` - The instance type pattern (may contain type variables)
775/// * `target` - The concrete type to match against
776///
777/// # Returns
778/// `Some(subst)` if the match succeeds, where `subst` maps type variables to types.
779/// `None` if the types cannot be matched.
780#[must_use]
781pub fn types_match(pattern: &Ty, target: &Ty) -> Option<Subst> {
782    let mut subst = Subst::new();
783    if types_match_with_subst(pattern, target, &mut subst) {
784        Some(subst)
785    } else {
786        None
787    }
788}
789
790/// Helper function that accumulates substitutions during type matching.
791///
792/// Returns `true` if the pattern matches the target, accumulating bindings
793/// from type variables to concrete types in `subst`.
794pub fn types_match_with_subst(pattern: &Ty, target: &Ty, subst: &mut Subst) -> bool {
795    match (pattern, target) {
796        // Type variable in pattern: bind to target type
797        (Ty::Var(v), _) => {
798            if let Some(bound_ty) = subst.get(v) {
799                // Must match the existing binding
800                types_equal(bound_ty, target)
801            } else {
802                subst.insert(v, target.clone());
803                true
804            }
805        }
806
807        // Type constructors must match by name
808        (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
809
810        // Primitive types must match exactly
811        (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
812
813        // Type applications: match both the function and argument
814        (Ty::App(f1, a1), Ty::App(f2, a2)) => {
815            types_match_with_subst(f1, f2, subst) && types_match_with_subst(a1, a2, subst)
816        }
817
818        // Function types: match argument and result types
819        (Ty::Fun(a1, r1), Ty::Fun(a2, r2)) => {
820            types_match_with_subst(a1, a2, subst) && types_match_with_subst(r1, r2, subst)
821        }
822
823        // Tuple types: must have same length and matching elements
824        (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
825            .iter()
826            .zip(ts2.iter())
827            .all(|(t1, t2)| types_match_with_subst(t1, t2, subst)),
828
829        // List types: match element types
830        (Ty::List(e1), Ty::List(e2)) => types_match_with_subst(e1, e2, subst),
831
832        // Forall types: match bodies (simplified, no alpha-renaming)
833        (Ty::Forall(_, body1), Ty::Forall(_, body2)) => types_match_with_subst(body1, body2, subst),
834
835        // Type-level naturals
836        (Ty::Nat(n1), Ty::Nat(n2)) => n1 == n2,
837
838        // Type-level lists
839        (Ty::TyList(l1), Ty::TyList(l2)) => l1 == l2,
840
841        // Error types match anything (to avoid cascading errors)
842        (Ty::Error, _) | (_, Ty::Error) => true,
843
844        // All other combinations don't match
845        _ => false,
846    }
847}
848
849/// Match multiple pattern types against target types, returning combined substitution.
850///
851/// Both lists must have the same length. The resulting substitution combines
852/// all bindings from matching each pair.
853#[must_use]
854pub fn types_match_multi(patterns: &[Ty], targets: &[Ty]) -> Option<Subst> {
855    if patterns.len() != targets.len() {
856        return None;
857    }
858
859    let mut subst = Subst::new();
860    for (pattern, target) in patterns.iter().zip(targets.iter()) {
861        if !types_match_with_subst(pattern, target, &mut subst) {
862            return None;
863        }
864    }
865    Some(subst)
866}
867
868/// Check if two types are structurally equal.
869#[must_use]
870pub fn types_equal(t1: &Ty, t2: &Ty) -> bool {
871    match (t1, t2) {
872        (Ty::Var(v1), Ty::Var(v2)) => v1.id == v2.id,
873        (Ty::Con(c1), Ty::Con(c2)) => c1.name == c2.name,
874        (Ty::Prim(p1), Ty::Prim(p2)) => p1 == p2,
875        (Ty::App(f1, a1), Ty::App(f2, a2)) => types_equal(f1, f2) && types_equal(a1, a2),
876        (Ty::Fun(a1, r1), Ty::Fun(a2, r2)) => types_equal(a1, a2) && types_equal(r1, r2),
877        (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
878            .iter()
879            .zip(ts2.iter())
880            .all(|(t1, t2)| types_equal(t1, t2)),
881        (Ty::List(e1), Ty::List(e2)) => types_equal(e1, e2),
882        (Ty::Error, Ty::Error) => true,
883        _ => false,
884    }
885}
886
887/// Errors that can occur during type operations.
888#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
889pub enum TypeError {
890    /// Unification failed between two types.
891    #[error("type mismatch: expected {expected}, found {found}")]
892    Mismatch {
893        /// The expected type.
894        expected: String,
895        /// The found type.
896        found: String,
897        /// The location of the error.
898        span: Span,
899    },
900
901    /// Occurs check failed (infinite type).
902    #[error("infinite type: {var} occurs in {ty}")]
903    OccursCheck {
904        /// The type variable.
905        var: String,
906        /// The type containing the variable.
907        ty: String,
908        /// The location of the error.
909        span: Span,
910    },
911
912    /// Unbound type variable.
913    #[error("unbound type variable: {name}")]
914    UnboundVar {
915        /// The variable name.
916        name: String,
917        /// The location of the error.
918        span: Span,
919    },
920
921    /// Kind mismatch.
922    #[error("kind mismatch: expected {expected}, found {found}")]
923    KindMismatch {
924        /// The expected kind.
925        expected: String,
926        /// The found kind.
927        found: String,
928        /// The location of the error.
929        span: Span,
930    },
931
932    /// Ambiguous type variable.
933    #[error("ambiguous type variable: {var}")]
934    Ambiguous {
935        /// The ambiguous variable.
936        var: String,
937        /// The location of the error.
938        span: Span,
939    },
940}
941
942#[cfg(test)]
943mod tests {
944    use super::*;
945
946    #[test]
947    fn test_ty_free_vars() {
948        let a = TyVar::new_star(0);
949        let b = TyVar::new_star(1);
950
951        // `a -> b` has free vars {a, b}
952        let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(b.clone()));
953        let vars = ty.free_vars();
954        assert_eq!(vars.len(), 2);
955        assert!(vars.contains(&a));
956        assert!(vars.contains(&b));
957    }
958
959    #[test]
960    fn test_subst_apply() {
961        let a = TyVar::new_star(0);
962        // SAFETY: 0 is a valid symbol index for testing purposes
963        let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
964
965        let mut subst = Subst::new();
966        subst.insert(&a, Ty::Con(int_con.clone()));
967
968        let ty = Ty::fun(Ty::Var(a.clone()), Ty::Var(a));
969        let result = subst.apply(&ty);
970
971        match result {
972            Ty::Fun(from, to) => {
973                assert!(matches!(*from, Ty::Con(_)));
974                assert!(matches!(*to, Ty::Con(_)));
975            }
976            _ => panic!("expected function type"),
977        }
978    }
979
980    #[test]
981    fn test_scheme_mono() {
982        // SAFETY: 0 is a valid symbol index for testing purposes
983        let int_con = TyCon::new(unsafe { Symbol::from_raw(0) }, Kind::Star);
984        let scheme = Scheme::mono(Ty::Con(int_con));
985        assert!(scheme.is_mono());
986    }
987
988    #[test]
989    fn test_prim_ty_properties() {
990        assert_eq!(PrimTy::I64.size_bytes(), 8);
991        assert_eq!(PrimTy::I32.size_bytes(), 4);
992        assert_eq!(PrimTy::F64.size_bytes(), 8);
993        assert_eq!(PrimTy::F32.size_bytes(), 4);
994
995        assert!(PrimTy::I64.is_signed_int());
996        assert!(PrimTy::I32.is_signed_int());
997        assert!(!PrimTy::U64.is_signed_int());
998
999        assert!(PrimTy::F64.is_float());
1000        assert!(PrimTy::F32.is_float());
1001        assert!(!PrimTy::I64.is_float());
1002
1003        assert!(PrimTy::I64.is_numeric());
1004        assert!(PrimTy::F64.is_numeric());
1005        assert!(!PrimTy::Char.is_numeric());
1006        assert!(!PrimTy::Addr.is_numeric());
1007    }
1008
1009    #[test]
1010    fn test_prim_ty_names() {
1011        assert_eq!(PrimTy::I64.name(), "Int#");
1012        assert_eq!(PrimTy::F64.name(), "Double#");
1013        assert_eq!(PrimTy::F32.name(), "Float#");
1014        assert_eq!(PrimTy::Char.name(), "Char#");
1015    }
1016
1017    #[test]
1018    fn test_ty_prim_constructors() {
1019        let int = Ty::int_prim();
1020        assert!(int.is_prim());
1021        assert_eq!(int.as_prim(), Some(PrimTy::I64));
1022
1023        let double = Ty::double_prim();
1024        assert!(double.is_prim());
1025        assert_eq!(double.as_prim(), Some(PrimTy::F64));
1026
1027        // Non-prim type
1028        let unit = Ty::unit();
1029        assert!(!unit.is_prim());
1030        assert_eq!(unit.as_prim(), None);
1031    }
1032
1033    #[test]
1034    fn test_prim_ty_no_free_vars() {
1035        // Primitive types have no free type variables
1036        let prim = Ty::int_prim();
1037        assert!(prim.free_vars().is_empty());
1038    }
1039
1040    #[test]
1041    fn test_subst_prim_unchanged() {
1042        // Substitution should leave primitive types unchanged
1043        let a = TyVar::new_star(0);
1044        let mut subst = Subst::new();
1045        subst.insert(&a, Ty::unit());
1046
1047        let prim = Ty::int_prim();
1048        let result = subst.apply(&prim);
1049        assert_eq!(result, prim);
1050    }
1051
1052    #[test]
1053    fn test_types_match_variable_binding() {
1054        let a = TyVar::new_star(0);
1055        let int_con = TyCon::new(Symbol::intern("Int"), Kind::Star);
1056        let int_ty = Ty::Con(int_con);
1057
1058        let result = types_match(&Ty::Var(a.clone()), &int_ty);
1059        assert!(result.is_some());
1060        let subst = result.unwrap();
1061        assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1062    }
1063
1064    #[test]
1065    fn test_types_match_constructor() {
1066        let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1067        let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1068
1069        assert!(types_match(&int_ty, &int_ty).is_some());
1070        assert!(types_match(&int_ty, &bool_ty).is_none());
1071    }
1072
1073    #[test]
1074    fn test_types_match_application() {
1075        let a = TyVar::new_star(0);
1076        let list_con = Ty::Con(TyCon::new(Symbol::intern("[]"), Kind::star_to_star()));
1077        let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1078
1079        // Pattern: [] a, Target: [] Int
1080        let pattern = Ty::App(Box::new(list_con.clone()), Box::new(Ty::Var(a.clone())));
1081        let target = Ty::App(Box::new(list_con), Box::new(int_ty.clone()));
1082
1083        let result = types_match(&pattern, &target);
1084        assert!(result.is_some());
1085        let subst = result.unwrap();
1086        assert_eq!(subst.apply(&Ty::Var(a)), int_ty);
1087    }
1088
1089    #[test]
1090    fn test_types_match_multi_basic() {
1091        let int_ty = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
1092        let bool_ty = Ty::Con(TyCon::new(Symbol::intern("Bool"), Kind::Star));
1093
1094        assert!(types_match_multi(&[int_ty.clone()], &[int_ty.clone()]).is_some());
1095        assert!(types_match_multi(&[int_ty.clone()], &[bool_ty]).is_none());
1096        assert!(types_match_multi(&[], &[]).is_some());
1097        assert!(types_match_multi(&[int_ty.clone()], &[]).is_none());
1098    }
1099}