More type unification is working.

This commit is contained in:
James Harton 2019-04-03 18:44:55 +13:00
parent 48c66442e5
commit c465cdf9a4
17 changed files with 497 additions and 121 deletions

82
.vscode/launch.json vendored Normal file
View file

@ -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}"
}
]
}

View file

@ -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"

View file

@ -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<IR>,
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
}
}

View file

@ -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<StringIdx, TyIdx>,
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;
}

View file

@ -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<BlockIdx>,
) {
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()];

View file

@ -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<F: Fn(&mut Block, &mut Context)>(&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<F: Fn(&mut Function, &mut Context)>(&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<F: Fn(&mut Block, &mut Context)>(&mut self, improver: F) {
let mut blocks = self.blocks.clone();
/// Perform a method-wise improvement.
pub fn improve_methods<F: Fn(&mut Method, &mut Context)>(&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`.

View file

@ -48,4 +48,6 @@ impl Error for CompileError {
pub enum ErrorKind {
TypeRedefined,
UnknownVariable,
InconsistentBranchTypes,
TypeInferenceFailure,
}

View file

@ -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<Clause>,
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());
}

View file

@ -0,0 +1,32 @@
use crate::block::BlockIdx;
use crate::clause::Clauseable;
use crate::context::Context;
pub fn pass<T: Clauseable>(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());
}
}
}

View file

@ -0,0 +1,14 @@
use crate::clause::Clauseable;
use crate::context::Context;
use crate::location::Locatable;
use crate::ty::ResultType;
pub fn pass<T: Clauseable + Locatable + ResultType>(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);
}
}

View file

@ -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;

View file

@ -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 }
}

View file

@ -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,

View file

@ -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
}

View file

@ -29,3 +29,7 @@ impl fmt::Display for Location {
write!(f, "{}:{}", self.source_path, self.location)
}
}
pub trait Locatable {
fn location(&self) -> &Location;
}

View file

@ -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<Clause>,
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,

View file

@ -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<StringIdx, TyIdx>,
},
Variable {
target: TyIdx,
},
}
pub trait ResultType {
fn result_type(&self) -> Option<&TyIdx>;
}