diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b36f0b4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,82 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [{ + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'huia-compiler'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=huia-compiler" + ], + "filter": { + "name": "huia-compiler", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'huia-parser'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=huia-parser" + ], + "filter": { + "name": "huia-parser", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'huia'", + "cargo": { + "args": [ + "build", + "--bin=huia", + "--package=huia" + ], + "filter": { + "name": "huia", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'huia'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=huia", + "--package=huia" + ], + "filter": { + "name": "huia", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} diff --git a/huia-compiler/Cargo.toml b/huia-compiler/Cargo.toml index 30f5ba1..be7afa7 100644 --- a/huia-compiler/Cargo.toml +++ b/huia-compiler/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" huia-parser = { path = "../huia-parser" } string-interner = "0.7.0" cranelift = "0.29.0" +cranelift-module = "0.29.0" diff --git a/huia-compiler/src/block.rs b/huia-compiler/src/block.rs index c354e31..a4430a0 100644 --- a/huia-compiler/src/block.rs +++ b/huia-compiler/src/block.rs @@ -8,13 +8,26 @@ use std::slice::IterMut; /// A basic block. /// /// A basic block is a collection of -#[derive(Debug, Default, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone)] pub struct Block { env: Env, ir: Vec, + result_type: TyIdx, } impl Block { + pub fn env(&self) -> &Env { + &self.env + } + + pub fn env_get(&self, name: StringIdx) -> Option<&TyIdx> { + self.env.get(name) + } + + pub fn env_set(&mut self, name: StringIdx, ty: TyIdx) { + self.env.set(name, ty) + } + /// Perform IR-wise improvement. /// /// See `IR.improve` for more information. @@ -45,14 +58,20 @@ impl Block { self.ir.iter_mut() } + pub fn last_ir(&self) -> Option<&IR> { + let len = self.ir.len(); + self.ir.get(len - 1) + } + pub fn len(&self) -> usize { self.ir.len() } - pub fn new(env: Env) -> Block { + pub fn new(env: Env, result_type: TyIdx) -> Block { Block { env, ir: Vec::default(), + result_type, } } @@ -69,16 +88,8 @@ impl Block { self.ir.push(ir); } - pub fn env(&self) -> &Env { - &self.env - } - - pub fn env_get(&self, name: StringIdx) -> Option<&TyIdx> { - self.env.get(name) - } - - pub fn env_set(&mut self, name: StringIdx, ty: TyIdx) { - self.env.set(name, ty) + pub fn result_type(&self) -> &TyIdx { + &self.result_type } } diff --git a/huia-compiler/src/clause.rs b/huia-compiler/src/clause.rs index 1261751..9181465 100644 --- a/huia-compiler/src/clause.rs +++ b/huia-compiler/src/clause.rs @@ -1,21 +1,26 @@ use crate::block::BlockIdx; use crate::stable::StringIdx; -use crate::ty::TyIdx; +use crate::ty::{ResultType, TyIdx}; use std::collections::BTreeMap; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Clause { arguments: BTreeMap, body: BlockIdx, + result_type: TyIdx, } impl Clause { - pub fn new(arguments: Vec<(StringIdx, TyIdx)>, body: BlockIdx) -> Clause { + pub fn new(arguments: Vec<(StringIdx, TyIdx)>, body: BlockIdx, result_type: TyIdx) -> Clause { let arguments = arguments.iter().fold(BTreeMap::new(), |mut args, (k, v)| { args.insert(k.clone(), v.clone()); args }); - Clause { arguments, body } + Clause { + arguments, + body, + result_type, + } } pub fn arguments(&self) -> Vec<(&StringIdx, &TyIdx)> { @@ -27,6 +32,12 @@ impl Clause { } } +impl ResultType for Clause { + fn result_type(&self) -> Option<&TyIdx> { + Some(&self.result_type) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct ClauseIdx(usize); @@ -51,5 +62,10 @@ impl From<&ClauseIdx> for usize { pub trait Clauseable { fn clauses(&self) -> Vec<&Clause>; fn has_clauses(&self) -> bool; - fn push_clause(&mut self, arguments: Vec<(StringIdx, TyIdx)>, block: BlockIdx) -> ClauseIdx; + fn push_clause( + &mut self, + arguments: Vec<(StringIdx, TyIdx)>, + block: BlockIdx, + result_type: TyIdx, + ) -> ClauseIdx; } diff --git a/huia-compiler/src/context/display.rs b/huia-compiler/src/context/display.rs index 2183bf5..bbf151a 100644 --- a/huia-compiler/src/context/display.rs +++ b/huia-compiler/src/context/display.rs @@ -2,6 +2,8 @@ use super::Context; use crate::block::BlockIdx; use crate::clause::Clauseable; use crate::ir::IR; +use crate::location::Locatable; +use crate::ty::ResultType; use crate::ty::TyIdx; use std::fmt; @@ -51,6 +53,15 @@ fn fmt_ty_name(idx: &TyIdx, context: &Context) -> String { if let Some(name) = ty.name() { return context.get_string(&name).unwrap().to_string(); } + if ty.is_impl() { + let tr = fmt_ty_name(ty.impl_of().unwrap(), &context); + let ty = fmt_ty_name(ty.impl_for().unwrap(), &context); + return format!("{}<{}>", tr, ty); + } + if ty.is_resolved() { + let ty = fmt_ty_name(ty.target().unwrap(), &context); + return format!("&{}", ty); + } } format!("T{:0>4}", usize::from(idx)) } @@ -65,6 +76,13 @@ fn fmt_types(buffer: &mut String, context: &Context) { if let Some(name) = ty.name() { let name = context.get_string(&name).unwrap(); buffer.push_str(&format!("{} = ", name)); + } else if ty.is_impl() { + let tr = fmt_ty_name(ty.impl_of().unwrap(), &context); + let ty = fmt_ty_name(ty.impl_for().unwrap(), &context); + buffer.push_str(&format!("{}<{}> = ", tr, ty)); + } else if ty.is_resolved() { + let ty = fmt_ty_name(ty.target().unwrap(), &context); + buffer.push_str(&format!("&{} = ", ty)); } else { buffer.push_str(&format!("T{:0>4} = ", i)); }; @@ -135,6 +153,9 @@ fn fmt_types(buffer: &mut String, context: &Context) { let tr = fmt_ty_name(ty.impl_of().unwrap(), &context); let ty = fmt_ty_name(ty.impl_for().unwrap(), &context); buffer.push_str(&format!("impl {} for {}", tr, ty)); + } else if ty.is_resolved() { + let target_ty = fmt_ty_name(ty.target().unwrap(), &context); + buffer.push_str(&format!("alias for {}", target_ty)); } else if ty.is_unresolved() { buffer.push_str("unknown type"); } @@ -174,7 +195,7 @@ fn fmt_functions(mut buffer: &mut String, context: &Context) { } buffer.push_str(&format!( " -> {}\n", - fmt_ty_name(fun.return_type(), &context) + fmt_ty_name(clause.result_type().unwrap(), &context) )); let mut jumps = Vec::new(); @@ -197,7 +218,14 @@ fn fmt_block( mut jumps: &mut Vec, ) { if let Some(block) = context.get_block(idx) { - buffer.push_str(&format!(" {}:\n", fmt_block_name(idx))); + buffer.push_str(&format!(" {}:", fmt_block_name(idx))); + + buffer.push_str(&format!( + " -> {}", + fmt_ty_name(block.result_type(), &context) + )); + + buffer.push_str("\n"); let mut i: usize = 0; for ir in block.ir_ref() { @@ -225,7 +253,7 @@ fn fmt_ir( fmt_ir(&mut buffer, &context, arg, &mut i, &mut jumps); ivs.push(*i); } - buffer.push_str(&format!(" @{:0>2}: @{:0>2}", *i, callee)); + buffer.push_str(&format!(" v{:0>2}: v{:0>2}", *i, callee)); if ivs.is_empty() { buffer.push_str("()"); } @@ -234,7 +262,7 @@ fn fmt_ir( buffer.push_str("("); } - buffer.push_str(&format!("@{:0>2}", k)); + buffer.push_str(&format!("v{:0>2}", k)); if j == ivs.len() - 1 { buffer.push_str(")"); @@ -251,13 +279,13 @@ fn fmt_ir( ivs.push(*i); } *i += 1; - buffer.push_str(&format!(" @{:0>2}: ", *i)); + buffer.push_str(&format!(" v{:0>2}: ", *i)); for (j, k) in ivs.iter().enumerate() { if j == 0 { buffer.push_str("["); } - buffer.push_str(&format!("@{:0>2}", k)); + buffer.push_str(&format!("v{:0>2}", k)); if j == ivs.len() - 1 { buffer.push_str("]"); @@ -269,19 +297,19 @@ fn fmt_ir( let atom = value.atom().unwrap(); let atom = context.get_string(atom).unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: {}", i, atom)); + buffer.push_str(&format!(" v{:0>2}: {}", i, atom)); } else if value.is_boolean() { let boolean = value.boolean().unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: {}", i, boolean)); + buffer.push_str(&format!(" v{:0>2}: {}", i, boolean)); } else if value.is_float() { let float = value.float().unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: {}", i, float)); + buffer.push_str(&format!(" v{:0>2}: {}", i, float)); } else if value.is_integer() { let int = value.integer().unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: {}", i, int)); + buffer.push_str(&format!(" v{:0>2}: {}", i, int)); } else if value.is_map() { let map = value.map().unwrap(); let mut ivs = Vec::new(); @@ -292,13 +320,13 @@ fn fmt_ir( ivs.push(*i); } *i += 1; - buffer.push_str(&format!(" @{:0>2}: ", *i)); + buffer.push_str(&format!(" v{:0>2}: ", *i)); for (j, k) in ivs.iter().enumerate() { if j == 0 { buffer.push_str("{"); } - buffer.push_str(&format!("@{:0>2}", k)); + buffer.push_str(&format!("v{:0>2}", k)); if j == ivs.len() - 1 { buffer.push_str("}"); @@ -312,7 +340,7 @@ fn fmt_ir( let string = value.string().unwrap(); let string = context.get_string(string).unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: {:?}", *i, string)); + buffer.push_str(&format!(" v{:0>2}: {:?}", *i, string)); } } else if ir.is_constructor() { let constructor = ir.get_constructor().unwrap(); @@ -322,7 +350,7 @@ fn fmt_ir( ivs.push(*i); } *i += 1; - buffer.push_str(&format!(" @{:0>2}: ", *i)); + buffer.push_str(&format!(" v{:0>2}: ", *i)); buffer.push_str(&fmt_ty_name(&ir.result_type(), &context)); for (j, (key, _val)) in constructor.iter().enumerate() { if j == 0 { @@ -330,7 +358,7 @@ fn fmt_ir( } let key = context.get_string(key).unwrap(); - buffer.push_str(&format!("{}: @{:0>2}", key, ivs[j])); + buffer.push_str(&format!("{}: v{:0>2}", key, ivs[j])); if j == ivs.len() - 1 { buffer.push_str("}"); @@ -341,15 +369,15 @@ fn fmt_ir( } else if ir.is_function() { let idx = ir.get_function().unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: ref f{}", *i, usize::from(idx))); + buffer.push_str(&format!(" v{:0>2}: ref f{}", *i, usize::from(idx))); } else if ir.is_get_local() { let name = context.get_string(ir.get_local().unwrap()).unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: get {}", *i, name)); + buffer.push_str(&format!(" v{:0>2}: get {}", *i, name)); } else if ir.is_get_property() { let name = context.get_string(ir.get_property().unwrap()).unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: get @{}", *i, name)); + buffer.push_str(&format!(" v{:0>2}: get @{}", *i, name)); } else if ir.is_infix() { let (op, lhs, rhs) = ir.get_infix().unwrap(); fmt_ir(&mut buffer, &context, lhs, &mut i, &mut jumps); @@ -358,18 +386,19 @@ fn fmt_ir( let rhs = *i; *i += 1; buffer.push_str(&format!( - " @{:0>2}: {} @{:0>2} @{:0>2}", + " v{:0>2}: {} v{:0>2} v{:0>2}", *i, op, lhs, rhs )); } else if ir.is_jump() { let idx = ir.get_jump().unwrap(); - buffer.push_str(&format!(" @{:0>2}: jump {}", *i, fmt_block_name(idx))); + *i += 1; + buffer.push_str(&format!(" v{:0>2}: jump {}", *i, fmt_block_name(idx))); } else if ir.is_jump_if_false() { let (ir, idx) = ir.get_jump_if_false().unwrap(); fmt_ir(&mut buffer, &context, ir, &mut i, &mut jumps); *i += 1; buffer.push_str(&format!( - " @{:0>2}: jump if false {}", + " v{:0>2}: jump if false {}", *i, fmt_block_name(idx) )); @@ -378,7 +407,7 @@ fn fmt_ir( fmt_ir(&mut buffer, &context, ir, &mut i, &mut jumps); *i += 1; buffer.push_str(&format!( - " @{:0>2}: jump if true {}", + " v{:0>2}: jump if true {}", *i, fmt_block_name(idx) )); @@ -393,7 +422,7 @@ fn fmt_ir( ivs.push(*i); } *i += 1; - buffer.push_str(&format!(" @{:0>2}: @{:0>2}.{}", *i, rx, name)); + buffer.push_str(&format!(" v{:0>2}: v{:0>2}.{}", *i, rx, name)); if ivs.is_empty() { buffer.push_str("()"); } @@ -402,7 +431,7 @@ fn fmt_ir( buffer.push_str("("); } - buffer.push_str(&format!("@{:0>2}", k)); + buffer.push_str(&format!("v{:0>2}", k)); if j == ivs.len() - 1 { buffer.push_str(")"); @@ -415,14 +444,14 @@ fn fmt_ir( fmt_ir(&mut buffer, &context, value, &mut i, &mut jumps); let value = *i; *i += 1; - buffer.push_str(&format!(" @{:0>2}: return @{:0>2}", *i, value)); + buffer.push_str(&format!(" v{:0>2}: return v{:0>2}", *i, value)); } else if ir.is_set_local() { let (name, value) = ir.get_set_local().unwrap(); fmt_ir(&mut buffer, &context, value, &mut i, &mut jumps); let value = *i; let name = context.get_string(name).unwrap(); *i += 1; - buffer.push_str(&format!(" @{:0>2}: set {} @{:0>2}", *i, name, value)); + buffer.push_str(&format!(" v{:0>2}: set {} v{:0>2}", *i, name, value)); } else if ir.is_set_properties() { let properties = ir.get_set_properties().unwrap(); let mut ivs = Vec::new(); @@ -431,14 +460,14 @@ fn fmt_ir( ivs.push(*i); } *i += 1; - buffer.push_str(&format!(" @{:0>2}: ", *i)); + buffer.push_str(&format!(" v{:0>2}: ", *i)); for (j, (key, _val)) in properties.iter().enumerate() { if j == 0 { buffer.push_str("@{"); } let key = context.get_string(key).unwrap(); - buffer.push_str(&format!("{}: @{:0>2}", key, ivs[j])); + buffer.push_str(&format!("{}: v{:0>2}", key, ivs[j])); if j == ivs.len() - 1 { buffer.push_str("}"); @@ -448,13 +477,13 @@ fn fmt_ir( } } else if ir.is_type_reference() { let ty = ir.get_type_reference().unwrap(); - buffer.push_str(&format!(" @{:0>2}: {}", i, fmt_ty_name(ty, &context))); + buffer.push_str(&format!(" v{:0>2}: {}", i, fmt_ty_name(ty, &context))); } else if ir.is_unary() { let (op, rhs) = ir.get_unary().unwrap(); fmt_ir(&mut buffer, &context, rhs, &mut i, &mut jumps); let rhs = *i; *i += 1; - buffer.push_str(&format!(" @{:0>2}: {} @{:0>2}", *i, op, rhs)); + buffer.push_str(&format!(" v{:0>2}: {} v{:0>2}", *i, op, rhs)); } buffer.push_str(&format!(" <{}>", fmt_ty_name(&ir.result_type(), &context))); @@ -501,7 +530,7 @@ fn fmt_methods(mut buffer: &mut String, context: &Context) { } buffer.push_str(&format!( " -> {}\n", - fmt_ty_name(method.return_type(), &context) + fmt_ty_name(clause.result_type().unwrap(), &context) )); let mut jumps = vec![clause.body().clone()]; diff --git a/huia-compiler/src/context/mod.rs b/huia-compiler/src/context/mod.rs index addd7e1..d3800ba 100644 --- a/huia-compiler/src/context/mod.rs +++ b/huia-compiler/src/context/mod.rs @@ -156,11 +156,19 @@ impl Context { self.blocks.get(usize::from(idx)) } + pub fn get_block_mut(&mut self, idx: &BlockIdx) -> Option<&mut Block> { + self.blocks.get_mut(usize::from(idx)) + } + /// Retrieve a specific function by it's index. pub fn get_function(&self, idx: FunctionIdx) -> Option<&Function> { self.functions.get(usize::from(idx)) } + pub fn get_method(&self, idx: MethodIdx) -> Option<&Method> { + self.methods.get(usize::from(idx)) + } + pub fn get_method_mut(&mut self, idx: MethodIdx) -> Option<&mut Method> { self.methods.get_mut(usize::from(idx)) } @@ -179,6 +187,31 @@ impl Context { self.types.get_mut(usize::from(idx)) } + /// Perform blockwise improvement. + /// + /// Iterates through all blocks in the context applying a provided improver + /// function. + pub fn improve_blocks(&mut self, improver: F) { + let mut blocks = self.blocks.clone(); + + for block in blocks.iter_mut() { + improver(block, self); + } + + self.blocks = blocks; + } + + /// Perform a function-wise improvement. + pub fn improve_functions(&mut self, improver: F) { + let mut functions = self.functions.clone(); + + for function in functions.iter_mut() { + improver(function, self); + } + + self.functions = functions; + } + /// Perform IR-wise improvement. /// /// Iterates through all blocks in the context and all IRs in each block @@ -199,18 +232,15 @@ impl Context { self.blocks = blocks; } - /// Perform blockwise improvement. - /// - /// Iterates through all blocks in the context applying a provided improver - /// function. - pub fn improve_blocks(&mut self, improver: F) { - let mut blocks = self.blocks.clone(); + /// Perform a method-wise improvement. + pub fn improve_methods(&mut self, improver: F) { + let mut methods = self.methods.clone(); - for block in blocks.iter_mut() { - improver(block, self); + for method in methods.iter_mut() { + improver(method, self); } - self.blocks = blocks; + self.methods = methods; } /// Convert an AST `InputLocation` into a compiler `Location`. diff --git a/huia-compiler/src/error.rs b/huia-compiler/src/error.rs index d07109d..1bdec55 100644 --- a/huia-compiler/src/error.rs +++ b/huia-compiler/src/error.rs @@ -48,4 +48,6 @@ impl Error for CompileError { pub enum ErrorKind { TypeRedefined, UnknownVariable, + InconsistentBranchTypes, + TypeInferenceFailure, } diff --git a/huia-compiler/src/function.rs b/huia-compiler/src/function.rs index 91a94d4..b2c481e 100644 --- a/huia-compiler/src/function.rs +++ b/huia-compiler/src/function.rs @@ -1,10 +1,10 @@ use crate::block::BlockIdx; use crate::clause::{Clause, ClauseIdx, Clauseable}; -use crate::location::Location; +use crate::location::{Locatable, Location}; use crate::stable::StringIdx; -use crate::ty::TyIdx; +use crate::ty::{ResultType, TyIdx}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Function { clauses: Vec, location: Location, @@ -16,10 +16,6 @@ impl Function { self.clauses.is_empty() } - pub fn location(&self) -> &Location { - &self.location - } - pub fn new(return_type: TyIdx, location: Location) -> Function { Function { clauses: Vec::new(), @@ -27,10 +23,6 @@ impl Function { return_type, } } - - pub fn return_type(&self) -> &TyIdx { - &self.return_type - } } impl Clauseable for Function { @@ -42,13 +34,77 @@ impl Clauseable for Function { !self.clauses.is_empty() } - fn push_clause(&mut self, arguments: Vec<(StringIdx, TyIdx)>, block: BlockIdx) -> ClauseIdx { + fn push_clause( + &mut self, + arguments: Vec<(StringIdx, TyIdx)>, + block: BlockIdx, + result_type: TyIdx, + ) -> ClauseIdx { let idx = self.clauses.len(); - self.clauses.push(Clause::new(arguments, block)); + self.clauses + .push(Clause::new(arguments, block, result_type)); idx.into() } } +impl Clauseable for &mut Function { + fn clauses(&self) -> Vec<&Clause> { + self.clauses.iter().collect() + } + + fn has_clauses(&self) -> bool { + !self.clauses.is_empty() + } + + fn push_clause( + &mut self, + arguments: Vec<(StringIdx, TyIdx)>, + block: BlockIdx, + result_type: TyIdx, + ) -> ClauseIdx { + let idx = self.clauses.len(); + self.clauses + .push(Clause::new(arguments, block, result_type)); + idx.into() + } +} + +impl Locatable for Function { + fn location(&self) -> &Location { + &self.location + } +} + +impl Locatable for &Function { + fn location(&self) -> &Location { + &self.location + } +} + +impl Locatable for &mut Function { + fn location(&self) -> &Location { + &self.location + } +} + +impl ResultType for Function { + fn result_type(&self) -> Option<&TyIdx> { + Some(&self.return_type) + } +} + +impl ResultType for &Function { + fn result_type(&self) -> Option<&TyIdx> { + Some(&self.return_type) + } +} + +impl ResultType for &mut Function { + fn result_type(&self) -> Option<&TyIdx> { + Some(&self.return_type) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct FunctionIdx(usize); @@ -77,10 +133,10 @@ mod test { #[test] fn test_function_new() { let rt = TyIdx::from(13); - let mut fun = Function::new(rt, Location::test()); + let mut fun = Function::new(rt.clone(), Location::test()); assert!(fun.is_empty()); - fun.push_clause(Vec::new(), BlockIdx::from(123)); + fun.push_clause(Vec::new(), BlockIdx::from(123), rt); assert!(!fun.is_empty()); } diff --git a/huia-compiler/src/improvements/block_result_types.rs b/huia-compiler/src/improvements/block_result_types.rs new file mode 100644 index 0000000..fcfa834 --- /dev/null +++ b/huia-compiler/src/improvements/block_result_types.rs @@ -0,0 +1,32 @@ +use crate::block::BlockIdx; +use crate::clause::Clauseable; +use crate::context::Context; + +pub fn pass(fun: &mut T, mut context: &mut Context) { + for clause in fun.clauses() { + let block_idx = clause.body(); + set_block_result_type(block_idx, &mut context); + } +} + +fn set_block_result_type(block_idx: &BlockIdx, mut context: &mut Context) { + let block = context.get_block(block_idx).unwrap().clone(); + if block.is_empty() { + return; + } + + for ir in block.ir_ref() { + if let Some(target_idx) = ir.get_target() { + set_block_result_type(target_idx, &mut context); + } + } + + if let Some(last_ir) = block.last_ir() { + if last_ir.is_terminator() { + let block = context.get_block(block_idx).unwrap().clone(); + let block_ty_idx = block.result_type(); + let block_ty = context.get_type_mut(block_ty_idx).unwrap(); + block_ty.resolve(&last_ir.result_type()); + } + } +} diff --git a/huia-compiler/src/improvements/clause_return_types.rs b/huia-compiler/src/improvements/clause_return_types.rs new file mode 100644 index 0000000..b564052 --- /dev/null +++ b/huia-compiler/src/improvements/clause_return_types.rs @@ -0,0 +1,14 @@ +use crate::clause::Clauseable; +use crate::context::Context; +use crate::location::Locatable; +use crate::ty::ResultType; + +pub fn pass(fun: &mut T, context: &mut Context) { + for clause in fun.clauses() { + let block_idx = clause.body(); + let ty_idx = clause.result_type().unwrap(); + let result_type = context.get_block(block_idx).unwrap().result_type().clone(); + let ty = context.get_type_mut(ty_idx).unwrap(); + ty.resolve(&result_type); + } +} diff --git a/huia-compiler/src/improvements/mod.rs b/huia-compiler/src/improvements/mod.rs index 7eb7cb2..018d79b 100644 --- a/huia-compiler/src/improvements/mod.rs +++ b/huia-compiler/src/improvements/mod.rs @@ -1,3 +1,5 @@ +pub mod block_result_types; +pub mod clause_return_types; pub mod desugar_infix; pub mod desugar_unary; pub mod infix_folding; diff --git a/huia-compiler/src/ir/builder.rs b/huia-compiler/src/ir/builder.rs index e7e1a4d..95936aa 100644 --- a/huia-compiler/src/ir/builder.rs +++ b/huia-compiler/src/ir/builder.rs @@ -1,9 +1,11 @@ use crate::block::{Block, BlockIdx}; use crate::clause::Clauseable; use crate::context::Context; +use crate::env::Env; use crate::error::ErrorKind; use crate::function::Function; use crate::ir::{Val, IR}; +use crate::location::Locatable; use crate::location::Location as Loc; use crate::method::{Method, MethodIdx, Modifier}; use crate::stable::StringIdx; @@ -88,11 +90,10 @@ impl Builder { self.build(callee.clone(), &mut context); let callee = self.pop_ir().unwrap(); - self.push_block(); - for node in args { + let arguments = args.iter().map(|node| { self.build(node.clone(), &mut context); - } - let arguments = self.pop_block().unwrap().ir(); + self.pop_ir().unwrap() + }).collect(); let result_type = context.unknown_type(location.clone()); @@ -139,7 +140,7 @@ impl Builder { for clause in function.value_ref() { - self.build_function_clause(&mut context, &mut fun, clause.arguments(), clause.body()); + self.build_function_clause(&mut context, &mut fun, clause.arguments(), clause.body(), location.clone()); } let fun_idx = context.define_function(fun); @@ -163,11 +164,13 @@ impl Builder { following_block_idx += 1; } - self.push_block(); + self.push_block(context.unknown_type(location.clone())); for node in positive { self.build(node.clone(), &mut context); } if let Some(last_ir) = self.pop_ir() { + let rt = context.get_type_mut(&result_type).unwrap(); + rt.resolve(&last_ir.result_type()); self.push_ir(IR::new_set_local(last_ir.result_type(), location.clone(), value_var_name.clone(), last_ir.clone())); self.push_ir(IR::new_jump(last_ir.result_type(), location.clone(), following_block_idx.into())); } else { @@ -188,13 +191,17 @@ impl Builder { // with a jump to the following block and push a `jump_if_false` // to the block. if !negative.is_empty() { - self.push_block(); + self.push_block(context.unknown_type(location.clone())); for node in negative { self.build(node.clone(), &mut context); } if let Some(last_ir) = self.pop_ir() { - self.env_set(&value_var_name, &last_ir.result_type()); + let rt = context.get_type(&result_type).unwrap(); + if rt.target().unwrap() != &last_ir.result_type() { + context.compile_error("Both branches of an if statement must result in the same type.", location.clone(), ErrorKind::InconsistentBranchTypes); + } + self.push_ir(IR::new_set_local(last_ir.result_type(), location.clone(), value_var_name.clone(), last_ir.clone())); self.push_ir(IR::new_jump(last_ir.result_type(), location.clone(), following_block_idx.into())); } else { @@ -212,7 +219,7 @@ impl Builder { } // Finally, push the following block onto the stack for the following instructions. - self.push_block(); + self.push_block(context.unknown_type(location.clone())); if let Some(result_type) = self.env_get(&value_var_name) { self.push_ir(IR::new_get_local(result_type.clone(), location, value_var_name)); @@ -228,10 +235,10 @@ impl Builder { // implementation whilst not inside a type definition. let ty = self.peek_ty().unwrap().clone(); - let im = context.declare_impl(tr, ty, location); + let im = context.declare_impl(tr, ty, location.clone()); self.push_ty(im.clone()); - self.push_block(); + self.push_block(context.unknown_type(location.clone())); for node in body { self.build(node.clone(), &mut context); @@ -256,8 +263,13 @@ impl Builder { match self.env_get(&constant_name) { Some(result_type) => self.push_ir(IR::new_get_local(result_type.clone(), location, constant_name)), None => { + // Add a compiler error. let message = format!("Unknown variable {}", name); - context.compile_error(&message, location, ErrorKind::UnknownVariable); + context.compile_error(&message, location.clone(), ErrorKind::UnknownVariable); + + // Push a value of unknown type to the IR so that we can try and continue. + let result_type = context.unknown_type(location.clone()); + self.push_ir(IR::new_get_local(result_type, location, constant_name)); } } } @@ -288,12 +300,10 @@ impl Builder { self.build(callee.clone(), &mut context); let callee = self.pop_ir().unwrap(); - self.push_block(); - for argument in arguments { - self.build(argument.clone(), &mut context); - } - let arguments = self.pop_block().unwrap().ir(); - + let arguments = arguments.iter().map(|node| { + self.build(node.clone(), &mut context); + self.pop_ir().unwrap() + }).collect(); let result_type = context.unknown_type(location.clone()); self.push_ir(IR::new_method_call(result_type, location, callee, method_name, arguments)) @@ -395,9 +405,9 @@ impl Builder { Vec::new() }; - let ty = context.declare_trait(&ty_idx, types, location); + let ty = context.declare_trait(&ty_idx, types, location.clone()); self.push_ty(ty.clone()); - self.push_block(); + self.push_block(context.unknown_type(location)); for node in body { self.build(node.clone(), &mut context); @@ -429,9 +439,9 @@ impl Builder { }) .collect(); - let ty = context.declare_type(&ty_idx, properties, location); + let ty = context.declare_type(&ty_idx, properties, location.clone()); self.push_ty(ty.clone()); - self.push_block(); + self.push_block(context.unknown_type(location)); for node in body { self.build(node.clone(), &mut context); } @@ -494,7 +504,7 @@ impl Builder { ) -> (Vec<(StringIdx, TyIdx)>, BlockIdx) { let arguments = self.build_arguments(&mut context, arguments); - self.push_block(); + self.push_block(context.unknown_type(context.location(body[0].location()))); let current_block_len = self.blocks.len(); for (name, ty) in &arguments { @@ -538,9 +548,11 @@ impl Builder { fun: &mut Function, arguments: &[(Identifier, TypeSpec)], body: &[Term], + location: Loc, ) { let (arguments, block_idx) = self.build_clause(&mut context, arguments, body); - fun.push_clause(arguments, block_idx); + let result_type = context.unknown_type(location.clone()); + fun.push_clause(arguments, block_idx, result_type); } fn build_method( @@ -580,8 +592,10 @@ impl Builder { body: &[Term], ) { let (arguments, block_idx) = self.build_clause(&mut context, arguments, body); + let method = context.get_method(method_idx.clone()).unwrap(); + let result_type = context.unknown_type(method.location().clone()); let method = context.get_method_mut(method_idx.clone()).unwrap(); - method.push_clause(arguments, block_idx); + method.push_clause(arguments, block_idx, result_type); } fn env_set(&mut self, name: &StringIdx, ty: &TyIdx) { @@ -654,14 +668,15 @@ impl Builder { None } - fn push_block(&mut self) { + fn push_block(&mut self, result_type: TyIdx) { match self.peek_block() { Some(ref block) => { let env = block.env().clone(); - self.blocks.push(Block::new(env)); + self.blocks.push(Block::new(env, result_type)); } None => { - self.blocks.push(Block::default()); + let env = Env::default(); + self.blocks.push(Block::new(env, result_type)); } } } @@ -677,7 +692,7 @@ impl Builder { impl Default for Builder { fn default() -> Builder { - let blocks = vec![Block::default()]; + let blocks = Vec::default(); let types = Vec::default(); Builder { blocks, types } } diff --git a/huia-compiler/src/ir/mod.rs b/huia-compiler/src/ir/mod.rs index 02afa15..98c32f5 100644 --- a/huia-compiler/src/ir/mod.rs +++ b/huia-compiler/src/ir/mod.rs @@ -292,6 +292,15 @@ impl IR { } } + pub fn is_jump_any(&self) -> bool { + match self.kind { + IRKind::Jump(..) => true, + IRKind::JumpIfFalse(..) => true, + IRKind::JumpIfTrue(..) => true, + _ => false, + } + } + pub fn is_jump_if_false(&self) -> bool { match self.kind { IRKind::JumpIfFalse(..) => true, diff --git a/huia-compiler/src/lib.rs b/huia-compiler/src/lib.rs index d591ac0..36355d4 100644 --- a/huia-compiler/src/lib.rs +++ b/huia-compiler/src/lib.rs @@ -32,5 +32,15 @@ pub fn compile_file(path: &str) -> Context { improvements::desugar_unary::pass(&mut ir, &mut context); }); + context.improve_functions(|mut function, mut context| { + improvements::block_result_types::pass(&mut function, &mut context); + improvements::clause_return_types::pass(&mut function, &mut context); + }); + + context.improve_methods(|mut method, mut context| { + improvements::block_result_types::pass(&mut method, &mut context); + improvements::clause_return_types::pass(&mut method, &mut context); + }); + context } diff --git a/huia-compiler/src/location.rs b/huia-compiler/src/location.rs index f1379c5..d3ddf8d 100644 --- a/huia-compiler/src/location.rs +++ b/huia-compiler/src/location.rs @@ -29,3 +29,7 @@ impl fmt::Display for Location { write!(f, "{}:{}", self.source_path, self.location) } } + +pub trait Locatable { + fn location(&self) -> &Location; +} diff --git a/huia-compiler/src/method.rs b/huia-compiler/src/method.rs index cc87db4..9b7a026 100644 --- a/huia-compiler/src/method.rs +++ b/huia-compiler/src/method.rs @@ -1,11 +1,11 @@ use crate::block::BlockIdx; use crate::clause::{Clause, ClauseIdx, Clauseable}; -use crate::location::Location; +use crate::location::{Locatable, Location}; use crate::stable::StringIdx; -use crate::ty::TyIdx; +use crate::ty::{ResultType, TyIdx}; use std::collections::BTreeMap; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Method { clauses: Vec, modifier: Modifier, @@ -105,9 +105,15 @@ impl Clauseable for Method { !self.clauses.is_empty() } - fn push_clause(&mut self, arguments: Vec<(StringIdx, TyIdx)>, block: BlockIdx) -> ClauseIdx { + fn push_clause( + &mut self, + arguments: Vec<(StringIdx, TyIdx)>, + block: BlockIdx, + result_type: TyIdx, + ) -> ClauseIdx { let idx = self.clauses.len(); - self.clauses.push(Clause::new(arguments, block)); + self.clauses + .push(Clause::new(arguments, block, result_type)); idx.into() } } @@ -121,13 +127,43 @@ impl Clauseable for &mut Method { !self.clauses.is_empty() } - fn push_clause(&mut self, arguments: Vec<(StringIdx, TyIdx)>, block: BlockIdx) -> ClauseIdx { + fn push_clause( + &mut self, + arguments: Vec<(StringIdx, TyIdx)>, + block: BlockIdx, + result_type: TyIdx, + ) -> ClauseIdx { let idx = self.clauses.len(); - self.clauses.push(Clause::new(arguments, block)); + self.clauses + .push(Clause::new(arguments, block, result_type)); idx.into() } } +impl Locatable for Method { + fn location(&self) -> &Location { + &self.location + } +} + +impl Locatable for &Method { + fn location(&self) -> &Location { + &self.location + } +} + +impl Locatable for &mut Method { + fn location(&self) -> &Location { + &self.location + } +} + +impl ResultType for &mut Method { + fn result_type(&self) -> Option<&TyIdx> { + Some(&self.return_type) + } +} + #[derive(Debug, Clone, Copy, PartialEq)] pub enum Modifier { Public, diff --git a/huia-compiler/src/ty.rs b/huia-compiler/src/ty.rs index f4a20cc..d706140 100644 --- a/huia-compiler/src/ty.rs +++ b/huia-compiler/src/ty.rs @@ -1,7 +1,6 @@ use crate::location::Location; use crate::stable::StringIdx; use std::collections::BTreeMap; -use std::fmt; /// The main "Type" struct. /// Holds information about Huia Types and Traits. @@ -122,6 +121,13 @@ impl Ty { } } + pub fn is_resolved(&self) -> bool { + match self.kind { + TyKind::Variable => true, + _ => false, + } + } + pub fn is_unresolved(&self) -> bool { match self.kind { TyKind::Unresolved => true, @@ -278,6 +284,31 @@ impl Ty { inner: TyInner::NativeType, } } + + pub fn resolve(&mut self, target: &TyIdx) { + match self.kind { + TyKind::Unresolved => { + self.kind = TyKind::Variable; + self.inner = TyInner::Variable { + target: target.clone(), + }; + } + TyKind::Variable => { + if self.target().unwrap() == target { + return; + } + panic!("Attempt to resolve already resolved type"); + } + _ => panic!("Attempt to resolve unresolvable type"), + } + } + + pub fn target(&self) -> Option<&TyIdx> { + match self.inner { + TyInner::Variable { ref target } => Some(target), + _ => None, + } + } } #[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] @@ -308,18 +339,7 @@ pub enum TyKind { Trait, Impl, Unresolved, -} - -impl fmt::Display for TyKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TyKind::Native => write!(f, "native type"), - TyKind::Type => write!(f, "type"), - TyKind::Trait => write!(f, "trait"), - TyKind::Impl => write!(f, "trait implementation"), - TyKind::Unresolved => write!(f, "unresolved type"), - } - } + Variable, } #[derive(Debug, Clone, PartialEq)] @@ -343,4 +363,11 @@ enum TyInner { Type { properties: BTreeMap, }, + Variable { + target: TyIdx, + }, +} + +pub trait ResultType { + fn result_type(&self) -> Option<&TyIdx>; }