diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index 90e5044..75b98d7 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -63,14 +63,19 @@ fn main() { // Typecheck the HIR match check(&ir) { - Ok(_) => {}, - Err(err) => { - diagnostics.add_typecheck_error(err); + Ok(_) => { + logif!(0, format!("Typechecking took {}ms", start.elapsed().as_millis())); + }, + Err(errs) => { + for err in errs { + diagnostics.add_typecheck_error(err); + } diagnostics.display(src); - logif!(0, "Typechecking failed"); + logif!(2, "Typechecking failed"); std::process::exit(1); } } + dbg!(check(&ir)); // Report lowering errors if any if diagnostics.has_error() { diff --git a/crates/typecheck/src/lib.rs b/crates/typecheck/src/lib.rs index 592e26b..8105a5f 100644 --- a/crates/typecheck/src/lib.rs +++ b/crates/typecheck/src/lib.rs @@ -1,4 +1,4 @@ -use hir::{IR, IRKind}; +use hir::{IR, IRKind, Value}; #[derive(Debug)] pub enum TypecheckErrorKind { @@ -29,6 +29,95 @@ pub struct TypecheckError { pub span: std::ops::Range, } -pub fn check(ir: &Vec) -> Result<(), TypecheckError> { - todo!(); +pub fn check(irs: &Vec) -> Result<(), Vec> { + let mut errors = Vec::new(); + for ir in irs { + match &ir.kind { + ir @ IRKind::Define { .. } => { + match check_define(&ir) { + Ok(()) => (), + Err(e) => errors.push(e), + } + } + _ => {} + } + } + if errors.is_empty() { Ok(()) } + else { Err(errors) } +} + +#[macro_export] +macro_rules! return_err { + ($kind:expr, $span:expr) => {{ + return Err(TypecheckError { + kind: $kind, + span: $span.clone() + }); + }}; +} + +/// Check the types of the definitions. +/// This is done by checking the type of the value against the type hint. +/// +/// # Examples +/// ```sml +/// let x: int = 1; -- Correct +/// +/// let x: string = 1; -- Incorrect +/// ``` +fn check_define(ir: &IRKind) -> Result<(), TypecheckError> { + match ir { + IRKind::Define { + type_hint, + value, + span, + .. + } => { + match &**value { + IRKind::Value { value } => { + match value { + Value::Int(_) => { + if type_hint != "number" { + return_err!( + TypecheckErrorKind::DefinitionTypeMismatch { + type_specified: type_hint.to_string(), + type_found: "number".to_string(), + }, + span.clone() + ); + } + } + Value::String(_) => { + if type_hint != "string" { + return_err!( + TypecheckErrorKind::DefinitionTypeMismatch { + type_specified: type_hint.to_string(), + type_found: "string".to_string(), + }, + span.clone() + ); + } + } + Value::Boolean(_) => { + if type_hint != "boolean" { + return_err!( + TypecheckErrorKind::DefinitionTypeMismatch { + type_specified: type_hint.to_string(), + type_found: "boolean".to_string(), + }, + span.clone() + ); + } + } + // TODO: other types + _ => {} + } + } + // TODO: other (right-hand side) IRKinds + _ => {} + } + } + _ => unreachable!() + } + Ok(()) } \ No newline at end of file diff --git a/example/err/define_wrong_type.hz b/example/err/define_wrong_type.hz new file mode 100644 index 0000000..ced8320 --- /dev/null +++ b/example/err/define_wrong_type.hz @@ -0,0 +1,3 @@ +let foo: int = "123"; +let bar: string = 69; +let baz: bool = "true"; \ No newline at end of file