diff --git a/checker/specification/specification.md b/checker/specification/specification.md index dc9fb748..ceec1388 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -1668,6 +1668,18 @@ try { - Expected string, found 3 +#### Catch annotation + +```ts +try { + throw 3 +} catch (err: string) { + console.log(err) +} +``` + +- Cannot catch type string because the try block throws 3 + #### Object destructuring assignment ```ts diff --git a/checker/src/diagnostics.rs b/checker/src/diagnostics.rs index efdd025b..59aed846 100644 --- a/checker/src/diagnostics.rs +++ b/checker/src/diagnostics.rs @@ -343,6 +343,12 @@ mod defined_errors_and_warnings { expected: TypeStringRepresentation, found: TypeStringRepresentation, }, + // catch type is not compatible with thrown type + CatchTypeDoesNotMatch { + at: SpanWithSource, + expected: TypeStringRepresentation, + found: TypeStringRepresentation, + }, Unsupported { thing: &'static str, at: SpanWithSource, @@ -643,6 +649,18 @@ mod defined_errors_and_warnings { position: returned_position, kind, }, + TypeCheckError::CatchTypeDoesNotMatch { + expected, + found, + at, + } => Diagnostic::Position { + reason: format!( + "Cannot catch type {found} because the try block throws {expected}", + + ), + position: at, + kind, + }, TypeCheckError::TypeHasNoGenericParameters(name, position) => { Diagnostic::Position { reason: format!("Type '{name}' has no generic parameters",), @@ -650,6 +668,7 @@ mod defined_errors_and_warnings { kind, } } + TypeCheckError::InvalidComparison(_, _) => todo!(), TypeCheckError::InvalidAddition(_, _) => todo!(), TypeCheckError::InvalidUnaryOperation(_, _) => todo!(), diff --git a/checker/src/synthesis/statements.rs b/checker/src/synthesis/statements.rs index d8c761ac..c4a8ea89 100644 --- a/checker/src/synthesis/statements.rs +++ b/checker/src/synthesis/statements.rs @@ -6,13 +6,16 @@ use super::{ }; use crate::{ context::{Scope, VariableRegisterArguments}, - diagnostics::TypeCheckError, + diagnostics::{TypeCheckError, TypeStringRepresentation}, features::iteration::{synthesise_iteration, IterationBehavior}, + subtyping::{type_is_subtype, BasicEquality}, synthesis::EznoParser, CheckingData, Environment, TypeId, }; -use parser::{expressions::MultipleExpression, ASTNode, BlockOrSingleStatement, Statement}; +use parser::{ + expressions::MultipleExpression, ASTNode, BlockOrSingleStatement, Statement, TypeAnnotation, +}; use std::collections::HashMap; pub type ExportedItems = HashMap; @@ -237,11 +240,22 @@ pub(super) fn synthesise_statement( checking_data, |environment, checking_data| { if let Some((clause, ty_annotation)) = &stmt.exception_var { - let catch_variable_type = ty_annotation.as_ref().map(|annotation| { - synthesise_type_annotation(annotation, environment, checking_data) - }); - - // TODO subtype thrown here with catch_variable_type + let mut catch_variable_type = None; + if let Some(ty_annotation) = ty_annotation { + let catch_type_id = synthesise_type_annotation( + ty_annotation, + environment, + checking_data, + ); + check_catch_type( + ty_annotation, + catch_type_id, + thrown_type, + environment, + checking_data, + ); + catch_variable_type = Some(catch_type_id); + } register_variable( clause.get_ast_ref(), @@ -274,6 +288,51 @@ pub(super) fn synthesise_statement( } } +fn check_catch_type( + catch_annotation: &TypeAnnotation, + catch_type: TypeId, + thrown_type: TypeId, + environment: &mut Environment, + checking_data: &mut CheckingData, +) { + let mut basic_equality = BasicEquality { + add_property_restrictions: false, + position: source_map::Nullable::NULL, + object_constraints: Default::default(), + allow_errors: false, + }; + let result = type_is_subtype( + catch_type, + thrown_type, + &mut basic_equality, + environment, + &checking_data.types, + ); + + if let crate::subtyping::SubTypeResult::IsNotSubType(_) = result { + let expected = TypeStringRepresentation::from_type_id( + thrown_type, + environment, + &checking_data.types, + false, + ); + let found = TypeStringRepresentation::from_type_id( + catch_type, + environment, + &checking_data.types, + false, + ); + + let at = catch_annotation.get_position().with_source(environment.get_source()); + + checking_data.diagnostics_container.add_error(TypeCheckError::CatchTypeDoesNotMatch { + at, + expected, + found, + }); + } +} + /// Expects that this caller has already create a context for this to run in fn synthesise_block_or_single_statement( block_or_single_statement: &BlockOrSingleStatement,