feat: implement type classes / traits

Add support for type classes (traits) with full parsing, type checking, and
validation. The implementation includes:

- Trait declarations: trait Show { fn show(x: T): String }
- Trait implementations: impl Show for Int { fn show(x: Int) = ... }
- Super traits: trait Ord: Eq { ... }
- Trait constraints in where clauses: where T: Show + Eq
- Type parameters on traits: trait Functor<F> { ... }
- Default method implementations
- Validation of required method implementations

This provides a foundation for ad-hoc polymorphism and enables
more expressive type-safe abstractions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 04:51:06 -05:00
parent df5c0a1a32
commit 05a85ea27f
8 changed files with 675 additions and 10 deletions

View File

@@ -3,16 +3,16 @@
#![allow(dead_code, unused_variables)]
use crate::ast::{
self, BinaryOp, Declaration, EffectDecl, Expr, FunctionDecl, HandlerDecl, Ident, ImportDecl,
LetDecl, Literal, LiteralKind, MatchArm, Parameter, Pattern, Program, Span, Statement,
TypeDecl, TypeExpr, UnaryOp, VariantFields,
self, BinaryOp, Declaration, EffectDecl, Expr, FunctionDecl, HandlerDecl, Ident, ImplDecl,
ImportDecl, LetDecl, Literal, LiteralKind, MatchArm, Parameter, Pattern, Program, Span,
Statement, TraitDecl, TypeDecl, TypeExpr, UnaryOp, VariantFields,
};
use crate::diagnostics::{Diagnostic, Severity};
use crate::exhaustiveness::{check_exhaustiveness, missing_patterns_hint};
use crate::modules::ModuleLoader;
use crate::types::{
self, unify, EffectDef, EffectOpDef, EffectSet, HandlerDef, PropertySet, Type, TypeEnv,
TypeScheme, VariantDef, VariantFieldsDef,
self, unify, EffectDef, EffectOpDef, EffectSet, HandlerDef, PropertySet, TraitBoundDef,
TraitDef, TraitImpl, TraitMethodDef, Type, TypeEnv, TypeScheme, VariantDef, VariantFieldsDef,
};
/// Type checking error
@@ -256,6 +256,15 @@ impl TypeChecker {
};
self.env.bind(&let_decl.name.name, TypeScheme::mono(typ));
}
Declaration::Trait(trait_decl) => {
let trait_def = self.trait_def(trait_decl);
self.env.traits.insert(trait_decl.name.name.clone(), trait_def);
}
Declaration::Impl(impl_decl) => {
// Will be checked in second pass
let trait_impl = self.collect_impl(impl_decl);
self.env.trait_impls.push(trait_impl);
}
}
}
@@ -271,7 +280,10 @@ impl TypeChecker {
Declaration::Handler(handler) => {
self.check_handler(handler);
}
// Effects and types don't need checking beyond collection
Declaration::Impl(impl_decl) => {
self.check_impl(impl_decl);
}
// Effects, types, and traits don't need checking beyond collection
_ => {}
}
}
@@ -354,6 +366,29 @@ impl TypeChecker {
// For now, we just type-check the predicate expression
// (would need 'result' in scope, which we don't have yet)
}
ast::WhereClause::TraitConstraint(constraint) => {
// Validate that the type parameter exists
if !func.type_params.iter().any(|p| p.name == constraint.type_param.name)
&& !func.params.iter().any(|p| p.name.name == constraint.type_param.name)
{
self.errors.push(TypeError {
message: format!(
"Unknown type parameter '{}' in where clause",
constraint.type_param.name
),
span: constraint.span,
});
}
// Validate that each trait in the bounds exists
for bound in &constraint.bounds {
if !self.env.traits.contains_key(&bound.trait_name.name) {
self.errors.push(TypeError {
message: format!("Unknown trait: {}", bound.trait_name.name),
span: bound.span,
});
}
}
}
}
}
}
@@ -1304,6 +1339,165 @@ impl TypeChecker {
}
}
fn trait_def(&self, trait_decl: &TraitDecl) -> TraitDef {
let methods = trait_decl
.methods
.iter()
.map(|m| TraitMethodDef {
name: m.name.name.clone(),
type_params: m.type_params.iter().map(|p| p.name.clone()).collect(),
params: m
.params
.iter()
.map(|p| (p.name.name.clone(), self.resolve_type(&p.typ)))
.collect(),
return_type: self.resolve_type(&m.return_type),
has_default: m.default_impl.is_some(),
})
.collect();
let super_traits = trait_decl
.super_traits
.iter()
.map(|b| TraitBoundDef {
trait_name: b.trait_name.name.clone(),
type_args: b.type_args.iter().map(|t| self.resolve_type(t)).collect(),
})
.collect();
TraitDef {
name: trait_decl.name.name.clone(),
type_params: trait_decl.type_params.iter().map(|p| p.name.clone()).collect(),
super_traits,
methods,
}
}
fn collect_impl(&self, impl_decl: &ImplDecl) -> TraitImpl {
use std::collections::HashMap;
let methods: HashMap<String, Type> = impl_decl
.methods
.iter()
.map(|m| {
let return_type = m
.return_type
.as_ref()
.map(|t| self.resolve_type(t))
.unwrap_or_else(Type::var);
let param_types: Vec<Type> = m
.params
.iter()
.map(|p| self.resolve_type(&p.typ))
.collect();
let func_type = Type::function(param_types, return_type);
(m.name.name.clone(), func_type)
})
.collect();
let constraints = impl_decl
.constraints
.iter()
.map(|c| {
let bounds = c
.bounds
.iter()
.map(|b| TraitBoundDef {
trait_name: b.trait_name.name.clone(),
type_args: b.type_args.iter().map(|t| self.resolve_type(t)).collect(),
})
.collect();
(c.type_param.name.clone(), bounds)
})
.collect();
TraitImpl {
trait_name: impl_decl.trait_name.name.clone(),
trait_args: impl_decl
.trait_args
.iter()
.map(|t| self.resolve_type(t))
.collect(),
target_type: self.resolve_type(&impl_decl.target_type),
type_params: impl_decl.type_params.iter().map(|p| p.name.clone()).collect(),
constraints,
methods,
}
}
fn check_impl(&mut self, impl_decl: &ImplDecl) {
// Verify the trait exists
let trait_name = &impl_decl.trait_name.name;
let trait_def = match self.env.traits.get(trait_name) {
Some(def) => def.clone(),
None => {
self.errors.push(TypeError {
message: format!("Unknown trait: {}", trait_name),
span: impl_decl.span,
});
return;
}
};
// Verify all required methods are implemented
for method_def in &trait_def.methods {
if !method_def.has_default {
let implemented = impl_decl.methods.iter().any(|m| m.name.name == method_def.name);
if !implemented {
self.errors.push(TypeError {
message: format!(
"Missing implementation for required method '{}' of trait '{}'",
method_def.name, trait_name
),
span: impl_decl.span,
});
}
}
}
// Type check each implemented method
for impl_method in &impl_decl.methods {
// Find the method signature in the trait
let method_def = trait_def.methods.iter().find(|m| m.name == impl_method.name.name);
if method_def.is_none() {
self.errors.push(TypeError {
message: format!(
"Method '{}' is not defined in trait '{}'",
impl_method.name.name, trait_name
),
span: impl_method.span,
});
continue;
}
// Set up local environment with parameters
let mut local_env = self.env.clone();
for param in &impl_method.params {
let param_type = self.resolve_type(&param.typ);
local_env.bind(&param.name.name, TypeScheme::mono(param_type));
}
// Type check the body
let old_env = std::mem::replace(&mut self.env, local_env);
let body_type = self.infer_expr(&impl_method.body);
self.env = old_env;
// Check return type matches if specified
if let Some(ref return_type_expr) = impl_method.return_type {
let return_type = self.resolve_type(return_type_expr);
if let Err(e) = unify(&body_type, &return_type) {
self.errors.push(TypeError {
message: format!(
"Method '{}' body has type {}, but declared return type is {}: {}",
impl_method.name.name, body_type, return_type, e
),
span: impl_method.span,
});
}
}
}
}
fn resolve_type(&self, type_expr: &TypeExpr) -> Type {
match type_expr {
TypeExpr::Named(ident) => match ident.name.as_str() {