futilehdl/src/frontend/type_infer.rs

105 lines
4.2 KiB
Rust

use super::typed_ir;
use super::typed_ir::{Expr, ExprId, ExprKind};
use super::types;
use super::{CompileError, CompileErrorKind, Context};
use std::collections::HashMap;
impl Context {
pub fn infer_types(&mut self, block: typed_ir::Body) -> typed_ir::Body {
// TODO: ugly ugly hack
let try_1 = self
.infer_body_types(&block)
.expect("could not infer types");
let try_2 = self
.infer_body_types(&try_1)
.expect("could not infer types");
self.infer_body_types(&try_2)
.expect("could not infer types")
}
pub fn infer_body_types(
&mut self,
body: &typed_ir::Body,
) -> Result<typed_ir::Body, CompileError> {
let mut new_exprs = HashMap::new();
for (expr_id, expr) in &body.exprs {
if self.types.is_fully_typed(expr.typ) {
new_exprs.insert(*expr_id, expr.clone());
continue;
}
match &expr.kind {
ExprKind::Literal(lit) => {
// TODO: don't try to overwrite the type of a literal
let infres = self.types.infer_type(expr.typ, lit.typ);
new_exprs.insert(*expr_id, expr.clone().with_type(lit.typ));
}
ExprKind::Path(_) => {
new_exprs.insert(*expr_id, expr.clone());
}
ExprKind::Call(call) => {
let called_def = self.callables.get(call.called);
let param_types = called_def.args.iter().map(|param| param.1);
let mut genargs: Vec<_> = called_def.genargs.iter().map(|a| a.1).collect();
let inferred_args: Vec<_> = param_types
.zip(&call.args)
.map(|(param, arg)| {
self.types
.infer_type(param, body.exprs.get(arg).unwrap().typ)
})
.collect();
if !genargs.is_empty() {
// need to infer generic arguments
for inf_res in inferred_args {
match inf_res {
types::InferenceResult::TypeVar(dbi, tvar, typ) => {
assert_eq!(dbi, 0);
// TODO: type check argument instead of just using it
genargs[tvar as usize] = typ;
}
_ => todo!(),
}
}
}
let ret_type = match self.types.infer_type(expr.typ, called_def.ret_type) {
types::InferenceResult::TypeVar(dbi, tvar, typ) => {
assert_eq!(dbi, 0);
genargs[tvar as usize]
}
types::InferenceResult::First(typ) => typ,
x => todo!("{x:?}"),
};
let new_expr = typed_ir::Expr {
kind: typed_ir::ExprKind::Call(typed_ir::Call {
genargs,
..call.clone()
}),
typ: ret_type,
..expr.clone()
};
new_exprs.insert(*expr_id, new_expr);
}
ExprKind::Match(match_) => {
// TODO: hacky hacky hacky
let res_type = body.exprs.get(&match_.arms.first().unwrap().1).unwrap().typ;
let new_expr = expr.clone().with_type(res_type);
new_exprs.insert(*expr_id, new_expr);
}
}
}
/*
for (expr_id, expr) in &new_exprs {
if !self.types.is_fully_typed(expr.typ) {
return Err(CompileError::new(CompileErrorKind::TodoError("fail".to_owned())))
}
}
*/
let new_body = typed_ir::Body {
exprs: new_exprs,
..body.clone()
};
Ok(new_body)
}
}