Skip to content

Commit

Permalink
More work on type checking
Browse files Browse the repository at this point in the history
  • Loading branch information
JonathanBrouwer committed Nov 12, 2023
1 parent c1b9dc9 commit 0e7a36e
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 133 deletions.
97 changes: 55 additions & 42 deletions compiler/src/passes/validate/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::passes::parse::types::Type;
use miette::Diagnostic;
use thiserror::Error;
use crate::passes::validate::generate_constraints::PartialType;

#[derive(Debug, Error, Diagnostic)]
pub enum TypeError {
Expand All @@ -10,48 +11,48 @@ pub enum TypeError {
#[label = "This variable `{sym}` was not declared yet"]
span: (usize, usize),
},
#[error("Type was mismatched.")]
MismatchedType {
expect: Type<String>,
got: Type<String>,
#[label = "Expected this to be of type `{expect}`, but got `{got}`"]
span: (usize, usize),
},
#[error("Types were mismatched. Expected function, but found '{got}'.")]
TypeMismatchExpectFn { got: Type<String> },
#[error("Types were mismatched. Expected '{t1}' and '{t2}' to be equal.")]
MismatchedTypes {
t1: Type<String>,
t2: Type<String>,
#[label = "This has type `{t1}`"]
span_t1: (usize, usize),
#[label = "but this has type `{t2}`"]
span_t2: (usize, usize),
},
#[error("There are multiple functions named `{sym}`.")]
DuplicateFunction { sym: String },
#[error("Function `{sym}` has duplicate argument names.")]
DuplicateArg { sym: String },
#[error("Function `{expected}` has {expected} arguments, but found {got} arguments.")]
ArgCountMismatch { expected: usize, got: usize },
#[error("Found a break outside of a loop.")]
BreakOutsideLoop,
#[error("Tried to modify immutable variable '{sym}'")]
ModifyImmutable { sym: String },
#[error("The name {sym} should refer to a variable binding.'")]
VariableShouldBeExpr { sym: String },
#[error("The name `{sym}` should refer to a struct type.'")]
VariableShouldBeStruct { sym: String },
#[error("The field `{sym}` is not present in the struct definition.'")]
UnknownStructField { sym: String },
#[error("The field `{sym}` is missing in the struct.'")]
VariableConstructMissingField { sym: String },
#[error("The field `{sym}` was already provided earlier.'")]
VariableConstructDuplicateField { sym: String },
#[error("The type `{typ}` should be a struct type.'")]
TypeShouldBeStruct { typ: Type<String> },
#[error("The type definition `{sym}` is not sized.'")]
UnsizedType { sym: String },
// #[error("Type was mismatched.")]
// MismatchedType {
// expect: Type<String>,
// got: Type<String>,
// #[label = "Expected this to be of type `{expect}`, but got `{got}`"]
// span: (usize, usize),
// },
// #[error("Types were mismatched. Expected function, but found '{got}'.")]
// TypeMismatchExpectFn { got: Type<String> },
// #[error("Types were mismatched. Expected '{t1}' and '{t2}' to be equal.")]
// MismatchedTypes {
// t1: Type<String>,
// t2: Type<String>,
// #[label = "This has type `{t1}`"]
// span_t1: (usize, usize),
// #[label = "but this has type `{t2}`"]
// span_t2: (usize, usize),
// },
// #[error("There are multiple functions named `{sym}`.")]
// DuplicateFunction { sym: String },
// #[error("Function `{sym}` has duplicate argument names.")]
// DuplicateArg { sym: String },
// #[error("Function `{expected}` has {expected} arguments, but found {got} arguments.")]
// ArgCountMismatch { expected: usize, got: usize },
// #[error("Found a break outside of a loop.")]
// BreakOutsideLoop,
// #[error("Tried to modify immutable variable '{sym}'")]
// ModifyImmutable { sym: String },
// #[error("The name {sym} should refer to a variable binding.'")]
// VariableShouldBeExpr { sym: String },
// #[error("The name `{sym}` should refer to a struct type.'")]
// VariableShouldBeStruct { sym: String },
// #[error("The field `{sym}` is not present in the struct definition.'")]
// UnknownStructField { sym: String },
// #[error("The field `{sym}` is missing in the struct.'")]
// VariableConstructMissingField { sym: String },
// #[error("The field `{sym}` was already provided earlier.'")]
// VariableConstructDuplicateField { sym: String },
// #[error("The type `{typ}` should be a struct type.'")]
// TypeShouldBeStruct { typ: Type<String> },
// #[error("The type definition `{sym}` is not sized.'")]
// UnsizedType { sym: String },

#[error("Integer out of bounds.")]
IntegerOutOfBounds {
Expand All @@ -66,4 +67,16 @@ pub enum TypeError {
},
#[error("The program doesn't have a main function.")]
NoMain,

#[error("Types did not match.")]
MismatchedFnReturn {
expect: String,
got: String,

#[label = "Expected this function to be of type: `{expect}`"]
span_expected: (usize, usize),

#[label = "but got this type: `{got}`"]
span_got: (usize, usize),
},
}
71 changes: 49 additions & 22 deletions compiler/src/passes/validate/generate_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ use crate::passes::validate::{CMeta, DefConstrained, DefUniquified, ExprConstrai
use crate::utils::gen_sym::UniqueSym;
use crate::utils::union_find::{UnionFind, UnionIndex};
use std::collections::HashMap;

pub struct GraphThingy {}
use itertools::Itertools;
use crate::passes::validate::error::TypeError::MismatchedFnReturn;

#[derive(Debug, Clone)]
pub enum PartialType<'p> {
I64,
U64,
Int,

Bool,
Unit,
Never,
Expand All @@ -25,26 +24,28 @@ pub enum PartialType<'p> {
params: Vec<UnionIndex>,
typ: UnionIndex,
},
// TODO Any,
}
// uf: &mut UnionFind<PartialType<'p>>
fn combine_partial_types<'p>(a: &PartialType<'p>, b: &PartialType<'p>) -> Result<PartialType<'p>, ()> {
match (a, b) {
(PartialType::I64, PartialType::I64 | PartialType::Int) => Ok(PartialType::I64),
(PartialType::Int, PartialType::I64) => Ok(PartialType::I64),
(PartialType::U64, PartialType::U64 | PartialType::Int) => Ok(PartialType::U64),
(PartialType::Int, PartialType::U64) => Ok(PartialType::U64),
(PartialType::Int, PartialType::Int) => Ok(PartialType::Int),
(PartialType::Bool, PartialType::Bool) => Ok(PartialType::Bool),
(PartialType::Unit, PartialType::Unit) => Ok(PartialType::Unit),
(PartialType::Never, t) => Ok(t.clone()),
(t, PartialType::Never) => Ok(t.clone()),
(PartialType::Var { sym: sym_a }, PartialType::Var { sym: sym_b }) if sym_a == sym_b => Ok(PartialType::Var { sym: *sym_a }),
(PartialType::Fn { params: params_a, typ: typ_a}, PartialType::Fn {params: params_b, typ: typ_b}) => {
//check if params and typ are eq
todo!()

impl<'p> PartialType<'p> {
pub fn to_string(&self, uf: &mut UnionFind<PartialType>) -> String {
match self {
PartialType::I64 => "I64".to_string(),
PartialType::U64 => "U64".to_string(),
PartialType::Int => "{int}".to_string(),
PartialType::Bool => "Bool".to_string(),
PartialType::Unit => "Unit".to_string(),
PartialType::Never => "Never".to_string(),
PartialType::Var { sym } => format!("{}", sym.sym),
PartialType::Fn { params, typ } => {
let params_string = params.iter().map(|index| {
let pt = uf.get(*index).clone();
pt.to_string(uf)
}).format(", ").to_string();
let pt = uf.get(*typ).clone();
let typ_string = pt.to_string(uf);
format!("fn({params_string}) -> {typ_string}")
}
}
_ => Err(())
}
}

Expand Down Expand Up @@ -94,7 +95,12 @@ fn constrain_def<'p>(

let bdy = constrain_expr(bdy, &mut env)?;

uf.try_union_by(return_index, bdy.meta.index, combine_partial_types).map_err(|_| todo!())?;
uf.try_union_by(return_index, bdy.meta.index, combine_partial_types).map_err(|_| MismatchedFnReturn {
expect: format!("{}", typ.clone()),
got: uf.get(bdy.meta.index).to_string(uf),
span_expected: (0, 0),
span_got: (0, 0),
})?;

DefConstrained::Fn {
sym,
Expand Down Expand Up @@ -185,3 +191,24 @@ fn constrain_expr<'p>(
ExprUniquified::Switch { .. } => todo!(),
})
}

// uf: &mut UnionFind<PartialType<'p>>
fn combine_partial_types<'p>(a: &PartialType<'p>, b: &PartialType<'p>) -> Result<PartialType<'p>, ()> {
match (a, b) {
(PartialType::I64, PartialType::I64 | PartialType::Int) => Ok(PartialType::I64),
(PartialType::Int, PartialType::I64) => Ok(PartialType::I64),
(PartialType::U64, PartialType::U64 | PartialType::Int) => Ok(PartialType::U64),
(PartialType::Int, PartialType::U64) => Ok(PartialType::U64),
(PartialType::Int, PartialType::Int) => Ok(PartialType::Int),
(PartialType::Bool, PartialType::Bool) => Ok(PartialType::Bool),
(PartialType::Unit, PartialType::Unit) => Ok(PartialType::Unit),
(PartialType::Never, t) => Ok(t.clone()),
(t, PartialType::Never) => Ok(t.clone()),
(PartialType::Var { sym: sym_a }, PartialType::Var { sym: sym_b }) if sym_a == sym_b => Ok(PartialType::Var { sym: *sym_a }),
(PartialType::Fn { params: params_a, typ: typ_a}, PartialType::Fn {params: params_b, typ: typ_b}) => {
//check if params and typ are eq
todo!()
}
_ => Err(())
}
}
90 changes: 45 additions & 45 deletions compiler/src/passes/validate/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,51 @@ fn validate([test]: [&str; 1]) {
TypeError::UndeclaredVar { .. } => {
assert_eq!(expected_error, "UndeclaredVar")
}
TypeError::MismatchedType { .. } => {
assert_eq!(expected_error, "MismatchedType")
}
TypeError::TypeMismatchExpectFn { .. } => {
assert_eq!(expected_error, "TypeMismatchExpectFn")
}
TypeError::MismatchedTypes { .. } => {
assert_eq!(expected_error, "MismatchedTypes")
}
TypeError::DuplicateFunction { .. } => {
assert_eq!(expected_error, "DuplicateFunction")
}
TypeError::DuplicateArg { .. } => {
assert_eq!(expected_error, "DuplicateArg")
}
TypeError::ArgCountMismatch { .. } => {
assert_eq!(expected_error, "ArgCountMismatch")
}
TypeError::BreakOutsideLoop => {
assert_eq!(expected_error, "BreakOutsideLoop")
}
TypeError::ModifyImmutable { .. } => {
assert_eq!(expected_error, "ModifyImmutable")
}
TypeError::VariableShouldBeExpr { .. } => {
assert_eq!(expected_error, "VariableShouldBeExpr")
}
TypeError::VariableShouldBeStruct { .. } => {
assert_eq!(expected_error, "VariableShouldBeStruct")
}
TypeError::UnknownStructField { .. } => {
assert_eq!(expected_error, "UnknownStructField")
}
TypeError::VariableConstructMissingField { .. } => {
assert_eq!(expected_error, "VariableConstructMissingField")
}
TypeError::VariableConstructDuplicateField { .. } => {
assert_eq!(expected_error, "VariableConstructDuplicateField")
}
TypeError::TypeShouldBeStruct { .. } => {
assert_eq!(expected_error, "TypeShouldBeStruct")
}
TypeError::UnsizedType { .. } => {
assert_eq!(expected_error, "UnsizedType")
}
// TypeError::MismatchedType { .. } => {
// assert_eq!(expected_error, "MismatchedType")
// }
// TypeError::TypeMismatchExpectFn { .. } => {
// assert_eq!(expected_error, "TypeMismatchExpectFn")
// }
// TypeError::MismatchedTypes { .. } => {
// assert_eq!(expected_error, "MismatchedTypes")
// }
// TypeError::DuplicateFunction { .. } => {
// assert_eq!(expected_error, "DuplicateFunction")
// }
// TypeError::DuplicateArg { .. } => {
// assert_eq!(expected_error, "DuplicateArg")
// }
// TypeError::ArgCountMismatch { .. } => {
// assert_eq!(expected_error, "ArgCountMismatch")
// }
// TypeError::BreakOutsideLoop => {
// assert_eq!(expected_error, "BreakOutsideLoop")
// }
// TypeError::ModifyImmutable { .. } => {
// assert_eq!(expected_error, "ModifyImmutable")
// }
// TypeError::VariableShouldBeExpr { .. } => {
// assert_eq!(expected_error, "VariableShouldBeExpr")
// }
// TypeError::VariableShouldBeStruct { .. } => {
// assert_eq!(expected_error, "VariableShouldBeStruct")
// }
// TypeError::UnknownStructField { .. } => {
// assert_eq!(expected_error, "UnknownStructField")
// }
// TypeError::VariableConstructMissingField { .. } => {
// assert_eq!(expected_error, "VariableConstructMissingField")
// }
// TypeError::VariableConstructDuplicateField { .. } => {
// assert_eq!(expected_error, "VariableConstructDuplicateField")
// }
// TypeError::TypeShouldBeStruct { .. } => {
// assert_eq!(expected_error, "TypeShouldBeStruct")
// }
// TypeError::UnsizedType { .. } => {
// assert_eq!(expected_error, "UnsizedType")
// }
TypeError::IntegerOutOfBounds { .. } => {
assert_eq!(expected_error, "IntegerOutOfBounds")
}
Expand Down
26 changes: 2 additions & 24 deletions compiler/src/utils/gen_sym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ use std::sync::atomic::{AtomicUsize, Ordering};

static COUNT: AtomicUsize = AtomicUsize::new(0);

#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Ord, PartialOrd)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Ord, PartialOrd, Display)]
#[display(fmt = "{sym}.{id}")]
pub struct UniqueSym<'p> {
#[cfg(release)]
pub sym: &'p str,
#[cfg(not(release))]
pub sym: PhantomData<&'p str>,
pub id: usize,
}

Expand All @@ -23,26 +21,6 @@ impl<'p> UniqueSym<'p> {
}
}

impl Display for UniqueSym<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
#[cfg(release)]
let r = write!(f, "{}{}", self.sym, self.id);
#[cfg(not(release))]
let r = write!(f, "v{}", self.id);

r
}
}

#[cfg(not(release))]
pub fn gen_sym(_sym: &str) -> UniqueSym<'_> {
UniqueSym {
sym: PhantomData,
id: COUNT.fetch_add(1, Ordering::SeqCst),
}
}

#[cfg(release)]
pub fn gen_sym(sym: &str) -> UniqueSym<'_> {
UniqueSym {
sym,
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/utils/union_find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ impl<T> UnionFind<T> {
let index = self.find(index).0;
&self.data[index]
}

pub fn get_and<T2>(&mut self, index: UnionIndex, f: impl FnOnce(&T) -> T2) -> T2 {
let index = self.find(index).0;
&self.data[index]
}
}


Expand Down
4 changes: 4 additions & 0 deletions programs/fail/validate/bad_return_simple.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//* err: todo!
fn main() -> I64 {
true
}

0 comments on commit 0e7a36e

Please sign in to comment.