diff --git a/src/frontend.rs b/src/frontend.rs index b99604a..c0478ba 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -8,6 +8,8 @@ pub use types::{Type, TypeStruct, TypingContext}; mod callable; pub mod lowering; +mod pretty_ir; +mod type_infer; pub mod typed_ir; pub mod types; @@ -323,191 +325,4 @@ impl Context { } Ok(typed_module) } - - pub fn infer_expr_types(&mut self, expr: &typed_ir::Expr) -> typed_ir::Expr { - if self.types.is_fully_typed(expr.typ) { - // there is nothing more to infer - return expr.clone(); - } - match &expr.kind { - typed_ir::ExprKind::Literal(lit) => expr.clone().with_type(lit.typ), - // we can not see beyond this expression right now - typed_ir::ExprKind::Path(_) => expr.clone(), - typed_ir::ExprKind::Call(call) => { - let args_typed: Vec<_> = call - .args - .iter() - .map(|ex| self.infer_expr_types(ex)) - .collect(); - let callee_def = self.callables.get(call.called); - - let param_types: Vec<_> = callee_def.args.iter().map(|param| param.1).collect(); - let inferred_args: Vec<_> = param_types - .iter() - .zip(&args_typed) - .map(|(param, arg)| self.types.infer_type(*param, arg.typ)) - .collect(); - - let mut genargs: Vec<_> = callee_def.genargs.iter().map(|a| a.1).collect(); - - let mut new_type = callee_def.ret_type; - - if !genargs.is_empty() { - // need to infer generic arguments - for inf_res in inferred_args { - match inf_res { - types::InferenceResult::First(_) => todo!(), - types::InferenceResult::Second(_) => todo!(), - types::InferenceResult::TypeVar(dbi, tvar, typ) => { - assert_eq!(dbi, 0); - // TODO: type check argument instead of just using it - genargs[tvar as usize] = typ; - } - types::InferenceResult::Incompatible => todo!(), - types::InferenceResult::Ambigous => todo!(), - } - } - - // TODO: HACKY HACKY HACK - new_type = genargs[0]; - } - - let mut new_expr = expr.clone(); - new_expr.typ = new_type; - new_expr.kind = typed_ir::ExprKind::Call(typed_ir::Call { - called: call.called, - args: args_typed, - genargs, - }); - new_expr - } - typed_ir::ExprKind::Match(match_) => { - let new_arms: Vec<_> = match_ - .arms - .iter() - .map(|(pat, val)| (self.infer_expr_types(pat), self.infer_expr_types(val))) - .collect(); - // TODO: hacky hacky hacky - let res_type = new_arms.first().unwrap().1.typ; - let new_match = typed_ir::Match { - expr: self.infer_expr_types(&match_.expr), - arms: new_arms, - }; - let mut new_expr = expr.clone().with_type(res_type); - new_expr.kind = typed_ir::ExprKind::Match(Box::new(new_match)); - new_expr - } - } - } - - pub fn infer_types(&mut self, mut block: typed_ir::Block) -> typed_ir::Block { - let new_root = self.infer_expr_types(&block.expr); - block.expr = new_root; - block - } - - pub fn pretty_typed_block( - &self, - w: &mut dyn std::fmt::Write, - block: &typed_ir::Block, - ) -> std::fmt::Result { - let callsig = self.callables.get(block.signature); - { - // TODO: ugly copy paste job - let args = callsig - .args - .iter() - .map(|(_name, typ)| { - let mut out = String::new(); - self.types.pretty_type(&mut out, *typ)?; - Ok(out) - }) - .collect::, std::fmt::Error>>()?; - let genargs = callsig - .genargs - .iter() - .map(|(_name, typ)| { - let mut type_str = String::new(); - self.types.pretty_type(&mut type_str, *typ)?; - Ok(type_str) - }) - .collect::, std::fmt::Error>>()?; - writeln!( - w, - "block {}<{}>({})", - callsig.name(), - genargs.join(", "), - args.join(", ") - )?; - } - for sig in &block.signals { - let mut typ_pretty = String::new(); - self.types.pretty_type(&mut typ_pretty, sig.typ)?; - writeln!(w, "sig_{}: {}", sig.id.0, typ_pretty)? - } - self.pretty_typed_expr(w, &block.expr)?; - Ok(()) - } - - pub fn pretty_typed_expr( - &self, - w: &mut dyn std::fmt::Write, - expr: &typed_ir::Expr, - ) -> std::fmt::Result { - let expr_pretty = match &expr.kind { - typed_ir::ExprKind::Literal(lit) => { - let mut lit_str = String::new(); - self.types.pretty_value(&mut lit_str, lit)?; - lit_str - } - typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0), - typed_ir::ExprKind::Call(call) => { - let args = call - .args - .iter() - .map(|arg| { - self.pretty_typed_expr(w, arg)?; - Ok(format!("_{}", arg.id.0)) - }) - .collect::, std::fmt::Error>>()?; - let callable = self.callables.get(call.called); - let genargs = call - .genargs - .iter() - .map(|param| { - let mut type_str = String::new(); - self.types.pretty_type(&mut type_str, *param)?; - Ok(type_str) - }) - .collect::, std::fmt::Error>>()?; - format!( - "{}<{}>({})", - callable.name(), - genargs.join(", "), - args.join(", ") - ) - } - typed_ir::ExprKind::Match(match_) => { - self.pretty_typed_expr(w, &match_.expr)?; - let arms = match_ - .arms - .iter() - .map(|(pat, val)| { - self.pretty_typed_expr(w, pat)?; - self.pretty_typed_expr(w, val)?; - Ok(format!(" _{} => _{}", pat.id.0, val.id.0)) - }) - .collect::, _>>()?; - format!( - "match (_{}) {{\n{}\n}}", - &match_.expr.id.0, - arms.join(",\n") - ) - } - }; - let mut type_pretty = String::new(); - self.types.pretty_type(&mut type_pretty, expr.typ)?; - writeln!(w, "let _{}: {} = {}", expr.id.0, type_pretty, expr_pretty)?; - Ok(()) - } } diff --git a/src/frontend/pretty_ir.rs b/src/frontend/pretty_ir.rs new file mode 100644 index 0000000..dbd0af9 --- /dev/null +++ b/src/frontend/pretty_ir.rs @@ -0,0 +1,109 @@ +use super::typed_ir; +use super::Context; + +impl Context { + pub fn pretty_typed_block( + &self, + w: &mut dyn std::fmt::Write, + block: &typed_ir::Block, + ) -> std::fmt::Result { + let callsig = self.callables.get(block.signature); + { + // TODO: ugly copy paste job + let args = callsig + .args + .iter() + .map(|(_name, typ)| { + let mut out = String::new(); + self.types.pretty_type(&mut out, *typ)?; + Ok(out) + }) + .collect::, std::fmt::Error>>()?; + let genargs = callsig + .genargs + .iter() + .map(|(_name, typ)| { + let mut type_str = String::new(); + self.types.pretty_type(&mut type_str, *typ)?; + Ok(type_str) + }) + .collect::, std::fmt::Error>>()?; + writeln!( + w, + "block {}<{}>({})", + callsig.name(), + genargs.join(", "), + args.join(", ") + )?; + } + for sig in &block.signals { + let mut typ_pretty = String::new(); + self.types.pretty_type(&mut typ_pretty, sig.typ)?; + writeln!(w, "sig_{}: {}", sig.id.0, typ_pretty)? + } + self.pretty_typed_expr(w, &block.expr)?; + Ok(()) + } + + pub fn pretty_typed_expr( + &self, + w: &mut dyn std::fmt::Write, + expr: &typed_ir::Expr, + ) -> std::fmt::Result { + let expr_pretty = match &expr.kind { + typed_ir::ExprKind::Literal(lit) => { + let mut lit_str = String::new(); + self.types.pretty_value(&mut lit_str, lit)?; + lit_str + } + typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0), + typed_ir::ExprKind::Call(call) => { + let args = call + .args + .iter() + .map(|arg| { + self.pretty_typed_expr(w, arg)?; + Ok(format!("_{}", arg.id.0)) + }) + .collect::, std::fmt::Error>>()?; + let callable = self.callables.get(call.called); + let genargs = call + .genargs + .iter() + .map(|param| { + let mut type_str = String::new(); + self.types.pretty_type(&mut type_str, *param)?; + Ok(type_str) + }) + .collect::, std::fmt::Error>>()?; + format!( + "{}<{}>({})", + callable.name(), + genargs.join(", "), + args.join(", ") + ) + } + typed_ir::ExprKind::Match(match_) => { + self.pretty_typed_expr(w, &match_.expr)?; + let arms = match_ + .arms + .iter() + .map(|(pat, val)| { + self.pretty_typed_expr(w, pat)?; + self.pretty_typed_expr(w, val)?; + Ok(format!(" _{} => _{}", pat.id.0, val.id.0)) + }) + .collect::, _>>()?; + format!( + "match (_{}) {{\n{}\n}}", + &match_.expr.id.0, + arms.join(",\n") + ) + } + }; + let mut type_pretty = String::new(); + self.types.pretty_type(&mut type_pretty, expr.typ)?; + writeln!(w, "let _{}: {} = {}", expr.id.0, type_pretty, expr_pretty)?; + Ok(()) + } +} diff --git a/src/frontend/type_infer.rs b/src/frontend/type_infer.rs new file mode 100644 index 0000000..c2999c3 --- /dev/null +++ b/src/frontend/type_infer.rs @@ -0,0 +1,87 @@ +use super::typed_ir; +use super::types; +use super::Context; + +impl Context { + pub fn infer_types(&mut self, mut block: typed_ir::Block) -> typed_ir::Block { + let new_root = self.infer_expr_types(&block.expr); + block.expr = new_root; + block + } + + pub fn infer_expr_types(&mut self, expr: &typed_ir::Expr) -> typed_ir::Expr { + if self.types.is_fully_typed(expr.typ) { + // there is nothing more to infer + return expr.clone(); + } + match &expr.kind { + typed_ir::ExprKind::Literal(lit) => expr.clone().with_type(lit.typ), + // we can not see beyond this expression right now + typed_ir::ExprKind::Path(_) => expr.clone(), + typed_ir::ExprKind::Call(call) => { + let args_typed: Vec<_> = call + .args + .iter() + .map(|ex| self.infer_expr_types(ex)) + .collect(); + let callee_def = self.callables.get(call.called); + + let param_types: Vec<_> = callee_def.args.iter().map(|param| param.1).collect(); + let inferred_args: Vec<_> = param_types + .iter() + .zip(&args_typed) + .map(|(param, arg)| self.types.infer_type(*param, arg.typ)) + .collect(); + + let mut genargs: Vec<_> = callee_def.genargs.iter().map(|a| a.1).collect(); + + let mut new_type = callee_def.ret_type; + + if !genargs.is_empty() { + // need to infer generic arguments + for inf_res in inferred_args { + match inf_res { + types::InferenceResult::First(_) => todo!(), + types::InferenceResult::Second(_) => todo!(), + types::InferenceResult::TypeVar(dbi, tvar, typ) => { + assert_eq!(dbi, 0); + // TODO: type check argument instead of just using it + genargs[tvar as usize] = typ; + } + types::InferenceResult::Incompatible => todo!(), + types::InferenceResult::Ambigous => todo!(), + } + } + + // TODO: HACKY HACKY HACK + new_type = genargs[0]; + } + + let mut new_expr = expr.clone(); + new_expr.typ = new_type; + new_expr.kind = typed_ir::ExprKind::Call(typed_ir::Call { + called: call.called, + args: args_typed, + genargs, + }); + new_expr + } + typed_ir::ExprKind::Match(match_) => { + let new_arms: Vec<_> = match_ + .arms + .iter() + .map(|(pat, val)| (self.infer_expr_types(pat), self.infer_expr_types(val))) + .collect(); + // TODO: hacky hacky hacky + let res_type = new_arms.first().unwrap().1.typ; + let new_match = typed_ir::Match { + expr: self.infer_expr_types(&match_.expr), + arms: new_arms, + }; + let mut new_expr = expr.clone().with_type(res_type); + new_expr.kind = typed_ir::ExprKind::Match(Box::new(new_match)); + new_expr + } + } + } +}