cl-typeck/infer: Fix some inference errors

yay yippee type checking and inference woohoo
i am very tired
This commit is contained in:
John 2025-04-21 05:37:34 -04:00
parent 3b96833fcb
commit 7cf485fade
4 changed files with 474 additions and 418 deletions

View File

@ -7,421 +7,6 @@
pub mod engine;
pub mod inference;
pub mod error;
pub mod inference {
use std::iter;
use super::{engine::InferenceEngine, error::InferenceError};
use crate::{handle::Handle, type_expression::TypeExpression, type_kind::TypeKind};
use cl_ast::*;
pub trait Inference<'a> {
/// Performs type inference
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError>;
}
impl<'a> Inference<'a> for cl_ast::Expr {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
self.kind.infer(e)
}
}
impl<'a> Inference<'a> for cl_ast::ExprKind {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
match self {
ExprKind::Empty => Ok(e.from_type_kind(TypeKind::Empty)),
ExprKind::Quote(quote) => todo!("Quote: {quote}"),
ExprKind::Let(l) => {
let Let { mutable: _, name, ty, init } = l;
// Infer the pattern
let patty = name.infer(e)?;
// Deep copy the ty, if it exists
let ty = match ty {
Some(ty) => {
let ty = ty
.evaluate(e.table, e.at)
.map_err(InferenceError::AnnotationEval)?;
e.deep_clone(ty)
}
None => e.new_var(),
};
// Unify the pattern and the ty
e.unify(ty, patty)?;
// Infer the initializer
if let Some(init) = init {
// Unify the initializer and the ty
let initty = init.infer(e)?;
e.unify(ty, initty)?;
}
Ok(ty)
}
ExprKind::Match(m) => {
let Match { scrutinee, arms } = m;
// Infer the scrutinee
let scrutinee = scrutinee.infer(e)?;
let scope = e.new_var();
for arm in arms {
let _ty = arm.infer(e)?;
}
// For each pattern:
// Infer the pattern
// Unify it with the scrutinee
// Infer the Expr
// Unify the expr with the out variable
// Return out
todo!("Match: {m} {scrutinee} {scope}")
}
ExprKind::Assign(assign) => {
// Infer the tail expression
// Infer the head expression
// Unify head and tail
// Return Empty
todo!("Assign {assign}")
}
ExprKind::Modify(modify) => {
// Infer the tail expression
// Infer the head expression
// Search within the head type for `(op)_assign`
// Typecheck `op_assign(&mut head, tail)`
todo!("Modify {modify}")
}
ExprKind::Binary(binary) => {
let Binary { kind: _, parts } = binary;
let (head, tail) = parts.as_ref();
// Infer the tail expression
let tail = tail.infer(e)?;
// Infer the head expression
let head = head.infer(e)?;
// TODO: Search within the head type for `(op)`
e.unify(head, tail)?;
// Typecheck op(head, tail)
Ok(head)
}
ExprKind::Unary(unary) => {
let Unary { kind: _, tail } = unary;
// Infer the tail expression
let tail = tail.infer(e)?;
// TODO: Search within the tail type for `(op)`
// Typecheck `(op)(tail)`
Ok(tail)
}
ExprKind::Cast(cast) => {
// Infer the head expression
// Evaluate the type
// Decide whether the type is castable
// Return the type
todo!("Cast {cast}")
}
ExprKind::Member(member) => {
let Member { head, kind } = member;
// Infer the head expression
let head = head.infer(e)?;
// Get the type of head
let ty = e.entry(e.de_inst(head));
// Search within the head type for the memberkind
match kind {
MemberKind::Call(name, tuple) => match ty.nav(&[PathPart::Ident(*name)]) {
Some(ty) => match e.entry(e.de_inst(ty.id())).ty() {
Some(&TypeKind::FnSig { args, rety }) => {
let values = iter::once(Ok(ty.id()))
.chain(
tuple
.exprs
.iter()
// Infer each member
.map(|expr| expr.infer(e)),
)
// Construct tuple
.collect::<Result<Vec<_>, InferenceError>>()
// Return tuple
.map(|tys| e.from_type_kind(TypeKind::Tuple(tys)))?;
e.unify(args, values)?;
Ok(rety)
}
other => todo!("member-call {other:?}"),
},
None => Err(InferenceError::NotFound(Path::from(*name))),
},
MemberKind::Struct(name) => match ty.nav(&[PathPart::Ident(*name)]) {
Some(ty) => Ok(ty.id()),
None => Err(InferenceError::NotFound(Path::from(*name))),
},
MemberKind::Tuple(Literal::Int(idx)) => match ty.ty() {
Some(TypeKind::Tuple(tys)) => Ok(tys[*idx as usize]),
_ => Err(InferenceError::Mismatch(ty.id(), e.table.root())),
},
_ => Err(InferenceError::Mismatch(ty.id(), ty.root())),
}
// Type is required to be inferred at this point.
}
ExprKind::Index(index) => {
// Infer the head expression
// For each index expression:
// Infer the index type
// Decide whether the head can be indexed by that type
// head = result of indexing head
todo!("Index {index}")
}
ExprKind::Structor(structor) => {
// Evaluate the path in the current context
// Typecheck the fielders against the fields
todo!("Structor {structor}")
}
ExprKind::Path(path) => e
.by_name(path)
.map_err(|_| InferenceError::NotFound(path.clone())),
ExprKind::Literal(literal) => literal.infer(e),
ExprKind::Array(array) => {
let Array { values } = array;
let out = e.new_var();
for value in values {
let ty = value.infer(e)?;
e.unify(out, ty)?;
}
Ok(out)
}
ExprKind::ArrayRep(array_rep) => {
let ArrayRep { value, repeat } = array_rep;
let ty = value.infer(e)?;
Ok(e.from_type_kind(TypeKind::Array(ty, *repeat)))
}
ExprKind::AddrOf(addr_of) => {
let AddrOf { mutable: _, expr } = addr_of;
// TODO: mut ref
let ty = expr.infer(e)?;
Ok(e.from_type_kind(TypeKind::Ref(ty)))
}
ExprKind::Block(block) => block.infer(e),
ExprKind::Group(group) => {
let Group { expr } = group;
expr.infer(e)
}
ExprKind::Tuple(tuple) => tuple.infer(e),
ExprKind::While(w) => {
let While { cond, pass, fail } = w;
// Infer the condition
let cond = cond.infer(e)?;
// Unify the condition with bool
let boule = e
.primitive("bool".into())
.expect("Primitive bool should exist!");
e.unify(boule, cond)?;
// Enter a new breakset
let mut e = e.open_bset();
// Infer the fail branch
let fail = fail.infer(&mut e)?;
// Unify the fail branch with breakset
e.bset = fail;
// Infer the pass branch
let pass = pass.infer(&mut e)?;
// Unify the pass branch with Empty
let empt = e.from_type_kind(TypeKind::Empty);
e.unify(pass, empt)?;
// Return breakset
Ok(e.bset)
}
ExprKind::If(i) => {
let If { cond, pass, fail } = i;
// Do inference on the condition'
let cond = cond.infer(e)?;
// Unify the condition with bool
let boule = e
.primitive("bool".into())
.expect("Primitive bool should exist!");
e.unify(boule, cond)?;
// Do inference on the pass branch
let pass = pass.infer(e)?;
// Do inference on the fail branch
let fail = fail.infer(e)?;
// Unify pass and fail
e.unify(pass, fail)?;
// Return the result
Ok(pass)
}
ExprKind::For(f) => todo!("For {f}"),
ExprKind::Break(b) => {
let Break { body } = b;
// Infer the body of the break
let ty = body.infer(e)?;
// Unify it with the breakset of the loop
e.unify(ty, e.bset)?;
// Return never
Ok(e.from_type_kind(TypeKind::Never))
}
ExprKind::Return(r) => {
let Return { body } = r;
// Infer the body of the return
let ty = body.infer(e)?;
// Unify it with the return-set of the function
e.unify(ty, e.rset)?;
// Return never
Ok(e.from_type_kind(TypeKind::Never))
}
ExprKind::Continue => Ok(e.from_type_kind(TypeKind::Never)),
}
}
}
impl<'a> Inference<'a> for MatchArm {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let MatchArm(pat, expr) = self;
let table = &mut e.table;
let scope = table.new_entry(e.at, crate::table::NodeKind::Local);
let mut e = e.at(scope);
let pat_ty = pat.infer(&mut e)?;
// TODO: bind pattern variables in scope
let expr_ty = expr.infer(&mut e)?;
todo!("Finish pattern-matching: {pat_ty}, {expr_ty}")
}
}
impl<'a> Inference<'a> for Pattern {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
match self {
Pattern::Name(name) => {
let child = e.new_var();
e.table.add_child(e.at, *name, child);
Ok(child)
}
Pattern::Literal(literal) => literal.infer(e),
Pattern::Rest(_) => todo!("Infer rest-patterns"),
Pattern::Ref(_, pattern) => {
let ty = pattern.infer(e)?;
Ok(e.from_type_kind(TypeKind::Ref(ty)))
}
Pattern::RangeExc(pat1, pat2) => {
let ty1 = pat1.infer(e)?;
let ty2 = pat2.infer(e)?;
e.unify(ty1, ty2)?;
Ok(ty1)
}
Pattern::RangeInc(pat1, pat2) => {
let ty1 = pat1.infer(e)?;
let ty2 = pat2.infer(e)?;
e.unify(ty1, ty2)?;
Ok(ty1)
}
Pattern::Tuple(patterns) => {
let tys = patterns
.iter()
.map(|pat| pat.infer(e))
.collect::<Result<Vec<Handle>, InferenceError>>()?;
Ok(e.from_type_kind(TypeKind::Tuple(tys)))
}
Pattern::Array(patterns) => match patterns.as_slice() {
[one, rest @ ..] => {
let ty = one.infer(e)?;
for rest in rest {
let ty2 = rest.infer(e)?;
e.unify(ty, ty2)?;
}
Ok(e.from_type_kind(TypeKind::Slice(ty)))
}
[] => {
let ty = e.new_var();
Ok(e.from_type_kind(TypeKind::Slice(ty)))
}
},
Pattern::Struct(_path, _items) => todo!(),
Pattern::TupleStruct(_path, _patterns) => todo!(),
}
}
}
impl<'a> Inference<'a> for Tuple {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let Tuple { exprs } = self;
exprs
.iter()
// Infer each member
.map(|expr| expr.infer(e))
// Construct tuple
.collect::<Result<Vec<_>, InferenceError>>()
// Return tuple
.map(|tys| e.from_type_kind(TypeKind::Tuple(tys)))
}
}
impl<'a> Inference<'a> for Block {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let Block { stmts } = self;
let mut e = e.block_scope();
let empty = e.from_type_kind(TypeKind::Empty);
if let [stmts @ .., ret] = stmts.as_slice() {
for stmt in stmts {
match (&stmt.kind, &stmt.semi) {
(StmtKind::Expr(expr), Semi::Terminated) => {
expr.infer(&mut e)?;
}
(StmtKind::Expr(expr), Semi::Unterminated) => {
let ty = expr.infer(&mut e)?;
e.unify(ty, empty)?;
}
_ => {}
}
}
match (&ret.kind, &ret.semi) {
(StmtKind::Expr(expr), Semi::Terminated) => {
expr.infer(&mut e)?;
}
(StmtKind::Expr(expr), Semi::Unterminated) => {
return expr.infer(&mut e);
}
_ => {}
}
}
Ok(empty)
}
}
impl<'a> Inference<'a> for Else {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
self.body.infer(e)
}
}
impl<'a, I: Inference<'a>> Inference<'a> for Option<I> {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
match self {
Some(expr) => expr.infer(e),
None => Ok(e.from_type_kind(TypeKind::Empty)),
}
}
}
impl<'a, I: Inference<'a>> Inference<'a> for Box<I> {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
self.as_ref().infer(e)
}
}
impl<'a> Inference<'a> for Literal {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let ty = match self {
Literal::Bool(_) => Ok(e
.primitive("bool".into())
.expect("Primitive bool should exist!")),
Literal::Char(_) => Ok(e
.primitive("char".into())
.expect("Primitive char should exist!")),
Literal::Int(_) => Ok(e
.primitive("isize".into())
.expect("Primitive isize should exist!")),
Literal::Float(_) => Ok(e
.primitive("f64".into())
.expect("Primitive f64 should exist!")),
Literal::String(_) => Ok(e
.primitive("str".into())
.expect("Primitive str should exist!")),
}?;
Ok(e.new_inst(ty))
}
}
}

View File

@ -45,7 +45,7 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
}
pub fn open_bset(&mut self) -> InferenceEngine<'_, 'a> {
let bset = self.from_type_kind(TypeKind::Empty);
let bset = self.new_var();
InferenceEngine { at: self.at, table: self.table, bset, rset: self.rset }
}
@ -124,6 +124,12 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
pub fn set_instance(&mut self, to: Handle, of: Handle) {
let mut e = self.table.entry_mut(to);
match e.as_ref().ty() {
Some(TypeKind::Uninferred) => {
if let Some(ty) = self.table.ty(of) {
self.table.set_ty(to, ty.clone());
}
None
}
Some(TypeKind::Variable) => e.set_ty(TypeKind::Instance(of)),
other => todo!("Cannot set {} to instance of: {other:?}", e.as_ref()),
};
@ -355,12 +361,21 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
if a.len() != b.len() {
return Err(InferenceError::Mismatch(ah, bh));
}
let (a, b) = (a.clone(), b.clone());
for (a, b) in a.iter().zip(b.iter()) {
self.unify(*a, *b)?;
}
Ok(())
}
(&TypeKind::FnSig { args: a1, rety: r1 }, &TypeKind::FnSig { args: a2, rety: r2 }) => {
self.unify(a1, a2)?;
self.unify(r1, r2)
}
(TypeKind::Empty, TypeKind::Tuple(t)) | (TypeKind::Tuple(t), TypeKind::Empty)
if t.is_empty() =>
{
Ok(())
}
(TypeKind::Empty, TypeKind::Empty) => Ok(()),
(TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()),
(TypeKind::Module, TypeKind::Module) => Ok(()),

View File

@ -6,6 +6,7 @@ use core::fmt;
/// An error produced during type inference
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InferenceError {
AnnotationEval(crate::type_expression::Error),
NotFound(Path),
Mismatch(Handle, Handle),
Recursive(Handle, Handle),
@ -15,6 +16,7 @@ impl std::error::Error for InferenceError {}
impl fmt::Display for InferenceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InferenceError::AnnotationEval(error) => write!(f, "{error}"),
InferenceError::NotFound(p) => write!(f, "Path not visible in scope: {p}"),
InferenceError::Mismatch(a, b) => write!(f, "Type mismatch: {a:?} != {b:?}"),
InferenceError::Recursive(_, _) => write!(f, "Recursive type!"),

View File

@ -0,0 +1,454 @@
//! The [Inference] trait is the heart of cl-typeck's type inference.
//!
//! Each syntax structure must describe how to unify its types.
use std::iter;
use super::{engine::InferenceEngine, error::InferenceError};
use crate::{handle::Handle, type_expression::TypeExpression, type_kind::TypeKind};
use cl_ast::*;
pub trait Inference<'a> {
/// Performs type inference
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError>;
}
impl<'a> Inference<'a> for cl_ast::Expr {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
self.kind.infer(e)
}
}
impl<'a> Inference<'a> for cl_ast::ExprKind {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
match self {
ExprKind::Empty => Ok(e.from_type_kind(TypeKind::Empty)),
ExprKind::Quote(quote) => todo!("Quote: {quote}"),
ExprKind::Let(l) => {
let Let { mutable: _, name, ty, init } = l;
// Infer the pattern
let patty = name.infer(e)?;
// Deep copy the ty, if it exists
let ty = match ty {
Some(ty) => {
let ty = ty
.evaluate(e.table, e.at)
.map_err(InferenceError::AnnotationEval)?;
e.deep_clone(ty)
}
None => e.new_var(),
};
// Unify the pattern and the ty
e.unify(ty, patty)?;
// Infer the initializer
if let Some(init) = init {
// Unify the initializer and the ty
let initty = init.infer(e)?;
e.unify(ty, initty)?;
}
Ok(ty)
}
ExprKind::Match(m) => {
let Match { scrutinee, arms } = m;
// Infer the scrutinee
let scrutinee = scrutinee.infer(e)?;
let scope = e.new_var();
for arm in arms {
let _ty = arm.infer(e)?;
}
// For each pattern:
// Infer the pattern
// Unify it with the scrutinee
// Infer the Expr
// Unify the expr with the out variable
// Return out
todo!("Match: {m} {scrutinee} {scope}")
}
ExprKind::Assign(assign) => {
let Assign { parts } = assign;
let (head, tail) = parts.as_ref();
// Infer the tail expression
let tail = tail.infer(e)?;
// Infer the head expression
let head = head.infer(e)?;
// Unify head and tail
e.unify(head, tail)?;
// Return Empty
Ok(e.from_type_kind(TypeKind::Empty))
}
ExprKind::Modify(modify) => {
let Modify { kind: _, parts } = modify;
let (head, tail) = parts.as_ref();
// Infer the tail expression
let tail = tail.infer(e)?;
// Infer the head expression
let head = head.infer(e)?;
// TODO: Search within the head type for `(op)_assign`
e.unify(head, tail)?;
// TODO: Typecheck `op_assign(&mut head, tail)`
Ok(e.from_type_kind(TypeKind::Empty))
}
ExprKind::Binary(binary) => {
let Binary { kind, parts } = binary;
let (head, tail) = parts.as_ref();
// Infer the tail expression
let tail = tail.infer(e)?;
// Infer the head expression
let head = head.infer(e)?;
// TODO: Search within the head type for `(op)`
if let BinaryKind::Call = kind {
let rety = e.new_var();
let tail = e.from_type_kind(TypeKind::FnSig { args: tail, rety });
e.unify(head, tail)?;
Ok(rety)
} else {
// Typecheck op(head, tail)
e.unify(head, tail)?;
Ok(head)
}
}
ExprKind::Unary(Unary { kind: UnaryKind::Loop, tail }) => {
let mut e = e.block_scope();
// Enter a new breakset
let mut e = e.open_bset();
// Infer the fail branch
let tail = tail.infer(&mut e)?;
// Unify the pass branch with Empty
let empt = e.from_type_kind(TypeKind::Empty);
e.unify(tail, empt)?;
// Return breakset
Ok(e.bset)
}
ExprKind::Unary(unary) => {
let Unary { kind: _, tail } = unary;
// Infer the tail expression
let tail = tail.infer(e)?;
// TODO: Search within the tail type for `(op)`
// Typecheck `(op)(tail)`
Ok(tail)
}
ExprKind::Cast(cast) => {
let Cast { head, ty } = cast;
// Infer the head expression
let _head = head.infer(e)?;
// Evaluate the type
let ty = ty
.evaluate(e.table, e.at)
.map_err(InferenceError::AnnotationEval)?;
// Decide whether the type is castable
// TODO: not deciding is absolutely unsound!!!
// Return the type
Ok(ty)
}
ExprKind::Member(member) => {
let Member { head, kind } = member;
// Infer the head expression
let head = head.infer(e)?;
// Get the type of head
let ty = e.entry(e.de_inst(head));
// Search within the head type for the memberkind
match kind {
MemberKind::Call(name, tuple) => match ty.nav(&[PathPart::Ident(*name)]) {
Some(ty) => match e.entry(e.de_inst(ty.id())).ty() {
Some(&TypeKind::FnSig { args, rety }) => {
let values = iter::once(Ok(ty.id()))
.chain(
tuple
.exprs
.iter()
// Infer each member
.map(|expr| expr.infer(e)),
)
// Construct tuple
.collect::<Result<Vec<_>, InferenceError>>()
// Return tuple
.map(|tys| e.from_type_kind(TypeKind::Tuple(tys)))?;
e.unify(args, values)?;
Ok(rety)
}
other => todo!("member-call {other:?}"),
},
None => Err(InferenceError::NotFound(Path::from(*name))),
},
MemberKind::Struct(name) => match ty.nav(&[PathPart::Ident(*name)]) {
Some(ty) => Ok(ty.id()),
None => Err(InferenceError::NotFound(Path::from(*name))),
},
MemberKind::Tuple(Literal::Int(idx)) => match ty.ty() {
Some(TypeKind::Tuple(tys)) => Ok(tys[*idx as usize]),
_ => Err(InferenceError::Mismatch(ty.id(), e.table.root())),
},
_ => Err(InferenceError::Mismatch(ty.id(), ty.root())),
}
// Type is required to be inferred at this point.
}
ExprKind::Index(index) => {
// Infer the head expression
// For each index expression:
// Infer the index type
// Decide whether the head can be indexed by that type
// head = result of indexing head
todo!("Index {index}")
}
ExprKind::Structor(structor) => {
// Evaluate the path in the current context
// Typecheck the fielders against the fields
todo!("Structor {structor}")
}
ExprKind::Path(path) => e
.by_name(path)
.map_err(|_| InferenceError::NotFound(path.clone())),
ExprKind::Literal(literal) => literal.infer(e),
ExprKind::Array(array) => {
let Array { values } = array;
let out = e.new_var();
for value in values {
let ty = value.infer(e)?;
e.unify(out, ty)?;
}
Ok(out)
}
ExprKind::ArrayRep(array_rep) => {
let ArrayRep { value, repeat } = array_rep;
let ty = value.infer(e)?;
Ok(e.from_type_kind(TypeKind::Array(ty, *repeat)))
}
ExprKind::AddrOf(addr_of) => {
let AddrOf { mutable: _, expr } = addr_of;
// TODO: mut ref
let ty = expr.infer(e)?;
Ok(e.from_type_kind(TypeKind::Ref(ty)))
}
ExprKind::Block(block) => block.infer(e),
ExprKind::Group(group) => {
let Group { expr } = group;
expr.infer(e)
}
ExprKind::Tuple(tuple) => tuple.infer(e),
ExprKind::While(w) => {
let While { cond, pass, fail } = w;
// Infer the condition
let cond = cond.infer(e)?;
// Unify the condition with bool
let boule = e
.primitive("bool".into())
.expect("Primitive bool should exist!");
e.unify(boule, cond)?;
// Enter a new breakset
let mut e = e.open_bset();
// Infer the fail branch
let fail = fail.infer(&mut e)?;
// Unify the fail branch with breakset
e.bset = fail;
// Infer the pass branch
let pass = pass.infer(&mut e)?;
// Unify the pass branch with Empty
let empt = e.from_type_kind(TypeKind::Empty);
e.unify(pass, empt)?;
// Return breakset
Ok(e.bset)
}
ExprKind::If(i) => {
let If { cond, pass, fail } = i;
// Do inference on the condition'
let cond = cond.infer(e)?;
// Unify the condition with bool
let boule = e
.primitive("bool".into())
.expect("Primitive bool should exist!");
e.unify(boule, cond)?;
// Do inference on the pass branch
let pass = pass.infer(e)?;
// Do inference on the fail branch
let fail = fail.infer(e)?;
// Unify pass and fail
e.unify(pass, fail)?;
// Return the result
Ok(pass)
}
ExprKind::For(f) => todo!("For {f}"),
ExprKind::Break(b) => {
let Break { body } = b;
// Infer the body of the break
let ty = body.infer(e)?;
// Unify it with the breakset of the loop
e.unify(ty, e.bset)?;
// Return never
Ok(e.from_type_kind(TypeKind::Never))
}
ExprKind::Return(r) => {
let Return { body } = r;
// Infer the body of the return
let ty = body.infer(e)?;
// Unify it with the return-set of the function
e.unify(ty, e.rset)?;
// Return never
Ok(e.from_type_kind(TypeKind::Never))
}
ExprKind::Continue => Ok(e.from_type_kind(TypeKind::Never)),
}
}
}
impl<'a> Inference<'a> for MatchArm {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let MatchArm(pat, expr) = self;
let table = &mut e.table;
let scope = table.new_entry(e.at, crate::table::NodeKind::Local);
let mut e = e.at(scope);
let pat_ty = pat.infer(&mut e)?;
// TODO: bind pattern variables in scope
let expr_ty = expr.infer(&mut e)?;
todo!("Finish pattern-matching: {pat_ty}, {expr_ty}")
}
}
impl<'a> Inference<'a> for Pattern {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
match self {
Pattern::Name(name) => {
let child = e.new_var();
e.table.add_child(e.at, *name, child);
Ok(child)
}
Pattern::Literal(literal) => literal.infer(e),
Pattern::Rest(_) => todo!("Infer rest-patterns"),
Pattern::Ref(_, pattern) => {
let ty = pattern.infer(e)?;
Ok(e.from_type_kind(TypeKind::Ref(ty)))
}
Pattern::RangeExc(pat1, pat2) => {
let ty1 = pat1.infer(e)?;
let ty2 = pat2.infer(e)?;
e.unify(ty1, ty2)?;
Ok(ty1)
}
Pattern::RangeInc(pat1, pat2) => {
let ty1 = pat1.infer(e)?;
let ty2 = pat2.infer(e)?;
e.unify(ty1, ty2)?;
Ok(ty1)
}
Pattern::Tuple(patterns) => {
let tys = patterns
.iter()
.map(|pat| pat.infer(e))
.collect::<Result<Vec<Handle>, InferenceError>>()?;
Ok(e.from_type_kind(TypeKind::Tuple(tys)))
}
Pattern::Array(patterns) => match patterns.as_slice() {
[one, rest @ ..] => {
let ty = one.infer(e)?;
for rest in rest {
let ty2 = rest.infer(e)?;
e.unify(ty, ty2)?;
}
Ok(e.from_type_kind(TypeKind::Slice(ty)))
}
[] => {
let ty = e.new_var();
Ok(e.from_type_kind(TypeKind::Slice(ty)))
}
},
Pattern::Struct(_path, _items) => todo!(),
Pattern::TupleStruct(_path, _patterns) => todo!(),
}
}
}
impl<'a> Inference<'a> for Tuple {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let Tuple { exprs } = self;
exprs
.iter()
// Infer each member
.map(|expr| expr.infer(e))
// Construct tuple
.collect::<Result<Vec<_>, InferenceError>>()
// Return tuple
.map(|tys| e.from_type_kind(TypeKind::Tuple(tys)))
}
}
impl<'a> Inference<'a> for Block {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let Block { stmts } = self;
let mut e = e.block_scope();
let empty = e.from_type_kind(TypeKind::Empty);
if let [stmts @ .., ret] = stmts.as_slice() {
for stmt in stmts {
match (&stmt.kind, &stmt.semi) {
(StmtKind::Expr(expr), Semi::Terminated) => {
expr.infer(&mut e)?;
}
(StmtKind::Expr(expr), Semi::Unterminated) => {
let ty = expr.infer(&mut e)?;
e.unify(ty, empty)?;
}
_ => {}
}
}
match (&ret.kind, &ret.semi) {
(StmtKind::Expr(expr), Semi::Terminated) => {
expr.infer(&mut e)?;
}
(StmtKind::Expr(expr), Semi::Unterminated) => {
return expr.infer(&mut e);
}
_ => {}
}
}
Ok(empty)
}
}
impl<'a> Inference<'a> for Else {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
self.body.infer(e)
}
}
impl<'a, I: Inference<'a>> Inference<'a> for Option<I> {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
match self {
Some(expr) => expr.infer(e),
None => Ok(e.from_type_kind(TypeKind::Empty)),
}
}
}
impl<'a, I: Inference<'a>> Inference<'a> for Box<I> {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
self.as_ref().infer(e)
}
}
impl<'a> Inference<'a> for Literal {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> Result<Handle, InferenceError> {
let ty = match self {
Literal::Bool(_) => Ok(e
.primitive("bool".into())
.expect("Primitive bool should exist!")),
Literal::Char(_) => Ok(e
.primitive("char".into())
.expect("Primitive char should exist!")),
Literal::Int(_) => Ok(e
.primitive("isize".into())
.expect("Primitive isize should exist!")),
Literal::Float(_) => Ok(e
.primitive("f64".into())
.expect("Primitive f64 should exist!")),
Literal::String(_) => Ok(e
.primitive("str".into())
.expect("Primitive str should exist!")),
}?;
Ok(e.new_inst(ty))
}
}