diff --git a/dmc-lib/src/lowering/mod.rs b/dmc-lib/src/lowering/mod.rs index 2043302..aa72e32 100644 --- a/dmc-lib/src/lowering/mod.rs +++ b/dmc-lib/src/lowering/mod.rs @@ -64,7 +64,7 @@ pub fn lower_to_ir_synthetic_function( lower_to_ir_statement(statement, &session.ctx, &mut fn_ctx, true); fn_ctx.finish_block(); - // infer return type from statement + // check return type from statement let maybe_return_ir_type_info = match statement { Statement::Let(_) | Statement::Assign(_) => None, Statement::Expression(expression_statement) => { diff --git a/dmc-lib/src/semantic_analysis/type_analysis/infer/helpers.rs b/dmc-lib/src/semantic_analysis/type_analysis/check/helpers.rs similarity index 100% rename from dmc-lib/src/semantic_analysis/type_analysis/infer/helpers.rs rename to dmc-lib/src/semantic_analysis/type_analysis/check/helpers.rs diff --git a/dmc-lib/src/semantic_analysis/type_analysis/infer/mod.rs b/dmc-lib/src/semantic_analysis/type_analysis/check/mod.rs similarity index 57% rename from dmc-lib/src/semantic_analysis/type_analysis/infer/mod.rs rename to dmc-lib/src/semantic_analysis/type_analysis/check/mod.rs index 7158d91..db8ff93 100644 --- a/dmc-lib/src/semantic_analysis/type_analysis/infer/mod.rs +++ b/dmc-lib/src/semantic_analysis/type_analysis/check/mod.rs @@ -1,5 +1,6 @@ pub mod helpers; +use crate::ast::assign_statement::AssignStatement; use crate::ast::binary_expression::{BinaryExpression, BinaryOperation}; use crate::ast::call::Call; use crate::ast::class::Class; @@ -13,26 +14,29 @@ use crate::diagnostic::{Diagnostic, Diagnostics}; use crate::error_codes::BINARY_INCOMPATIBLE_TYPES; use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::semantic_analysis::analysis_context::helpers::get_name_for_type; -use crate::semantic_analysis::type_analysis::infer::helpers::{ +use crate::semantic_analysis::type_analysis::check::helpers::{ are_binary_op_compatible, binary_op_result, is_negative_op_compatible, negative_op_result, }; use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; -pub fn infer_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) -> Diagnostics { +pub fn check_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) -> Diagnostics { let mut diagnostics = Diagnostics::new(); + for function in compilation_unit.functions() { + check_types_function(function, ctx, &mut diagnostics); + } for class in compilation_unit.classes() { - infer_types_class(class, ctx, &mut diagnostics); + check_types_class(class, ctx, &mut diagnostics); } diagnostics } -fn infer_types_class(class: &Class, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) { +fn check_types_class(class: &Class, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) { for function in class.functions() { - infer_types_function(function, ctx, diagnostics); + check_types_function(function, ctx, diagnostics); } } -fn infer_types_function( +fn check_types_function( function: &Function, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics, @@ -40,31 +44,117 @@ fn infer_types_function( for statement in function.statements() { match statement { Statement::Let(let_statement) => { - infer_types_let_statement(let_statement, ctx, diagnostics); + check_types_let_statement(let_statement, ctx, diagnostics); + } + Statement::Expression(expression_statement) => { + resolve_types_expression(expression_statement.expression(), ctx, diagnostics); + } + Statement::Assign(assign_statement) => { + check_types_assign_statement(assign_statement, ctx, diagnostics); } - Statement::Expression(expression_statement) => {} - Statement::Assign(assign_statement) => {} } } } -fn infer_types_let_statement( +fn check_types_let_statement( let_statement: &LetStatement, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics, ) { - let initializer_type = infer_types_expression(let_statement.initializer(), ctx, diagnostics); - // todo set symbol's type to the initializer type + let initializer_type = resolve_types_expression(let_statement.initializer(), ctx, diagnostics); + let symbol_id = ctx + .get_symbol_id(let_statement.node_id()) + .unwrap_or_else(|| { + panic!( + "LetStatement {} has no associated symbol", + let_statement.node_id() + ) + }); + ctx.associate_symbol_to_type(symbol_id, initializer_type); } -fn infer_types_expression( +fn check_types_assign_statement( + assign_statement: &AssignStatement, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { + let destination_type_info_id = + resolve_types_expression(assign_statement.destination(), ctx, diagnostics); + let value_type_info_id = resolve_types_expression(assign_statement.value(), ctx, diagnostics); + + let destination_type_info = ctx.get_type_info_by_id(destination_type_info_id); + let value_type_info = ctx.get_type_info_by_id(value_type_info_id); + + match destination_type_info { + TypeInfo::Any + | TypeInfo::Class(_) + | TypeInfo::Constructor(_) + | TypeInfo::Function(_) + | TypeInfo::Void => { + let diagnostic = Diagnostic::new( + &format!( + "Unassignable destination type: cannot assign {} to {}", + get_name_for_type(value_type_info, ctx), + get_name_for_type(destination_type_info, ctx) + ), + assign_statement.destination().source_range().start(), + assign_statement.value().source_range().end(), + ); + diagnostics.push(diagnostic); + } + TypeInfo::Instance(destination_instance_type_info) => { + match value_type_info { + TypeInfo::Any + | TypeInfo::Class(_) + | TypeInfo::Constructor(_) + | TypeInfo::Function(_) + | TypeInfo::Void => { + let diagnostic = Diagnostic::new( + &format!( + "Unassignable source type: cannot assign {} to {}", + get_name_for_type(value_type_info, ctx), + get_name_for_type(destination_type_info, ctx), + ), + assign_statement.destination().source_range().start(), + assign_statement.value().source_range().end(), + ); + diagnostics.push(diagnostic); + } + TypeInfo::Instance(value_instance_type_info) => { + if !destination_instance_type_info + .can_assign_from(value_instance_type_info, ctx) + { + let diagnostic = Diagnostic::new( + &format!( + "Incompatible types: cannot assign {} to {}", + get_name_for_type(value_type_info, ctx), + get_name_for_type(destination_type_info, ctx) + ), + assign_statement.destination().source_range().start(), + assign_statement.value().source_range().end(), + ); + diagnostics.push(diagnostic); + } + } + TypeInfo::__Error => { + // no-op + } + } + } + TypeInfo::__Error => { + // no-op + } + } +} + +fn resolve_types_expression( expression: &Expression, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics, ) -> TypeInfoId { match expression { Expression::Binary(binary_expression) => { - infer_types_binary_expression(binary_expression, ctx, diagnostics) + resolve_types_binary_expression(binary_expression, ctx, diagnostics) } Expression::Negative(negative_expression) => { resolve_types_negative_expression(negative_expression, ctx, diagnostics) @@ -93,13 +183,13 @@ fn infer_types_expression( } } -fn infer_types_binary_expression( +fn resolve_types_binary_expression( binary_expression: &BinaryExpression, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics, ) -> TypeInfoId { - let lhs_type_info_id = infer_types_expression(binary_expression.lhs(), ctx, diagnostics); - let rhs_type_info_id = infer_types_expression(binary_expression.rhs(), ctx, diagnostics); + let lhs_type_info_id = resolve_types_expression(binary_expression.lhs(), ctx, diagnostics); + let rhs_type_info_id = resolve_types_expression(binary_expression.rhs(), ctx, diagnostics); let lhs_type_info = ctx.get_type_info_by_id(lhs_type_info_id); let rhs_type_info = ctx.get_type_info_by_id(rhs_type_info_id); @@ -148,7 +238,7 @@ fn resolve_types_negative_expression( diagnostics: &mut Diagnostics, ) -> TypeInfoId { let operand_type_info_id = - infer_types_expression(negative_expression.operand(), ctx, diagnostics); + resolve_types_expression(negative_expression.operand(), ctx, diagnostics); let operand_type_info = ctx.get_type_info_by_id(operand_type_info_id); let result_type_info_id = if is_negative_op_compatible(operand_type_info, ctx) { negative_op_result(operand_type_info, ctx) @@ -175,7 +265,7 @@ fn resolve_types_call( ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics, ) -> TypeInfoId { - let receiver_type_info_id = infer_types_expression(call.callee(), ctx, diagnostics); + let receiver_type_info_id = resolve_types_expression(call.callee(), ctx, diagnostics); let receiver_type_info = ctx.get_type_info_by_id(receiver_type_info_id); match receiver_type_info { TypeInfo::Any | TypeInfo::Void => { @@ -199,7 +289,7 @@ fn resolve_types_call( ctx.instance_type(symbol) } TypeInfo::Constructor(_) => { - panic!("Cannot infer expression type directly on ConstructorTypeInfo"); + panic!("Cannot check expression type directly on ConstructorTypeInfo"); } TypeInfo::Function(function_type_info) => function_type_info.return_type_id(), TypeInfo::Instance(_) => { diff --git a/dmc-lib/src/semantic_analysis/type_analysis/mod.rs b/dmc-lib/src/semantic_analysis/type_analysis/mod.rs index d472741..923981b 100644 --- a/dmc-lib/src/semantic_analysis/type_analysis/mod.rs +++ b/dmc-lib/src/semantic_analysis/type_analysis/mod.rs @@ -1,12 +1,12 @@ use crate::ast::compilation_unit::CompilationUnit; use crate::diagnostic::Diagnostics; use crate::semantic_analysis::analysis_context::AnalysisContext; +use crate::semantic_analysis::type_analysis::check::check_types; use crate::semantic_analysis::type_analysis::collect::collect_types; -use crate::semantic_analysis::type_analysis::infer::infer_types; use crate::semantic_analysis::type_analysis::resolve::resolve_types; +mod check; mod collect; -mod infer; mod resolve; pub fn analyze_types( @@ -15,5 +15,5 @@ pub fn analyze_types( ) -> Diagnostics { collect_types(compilation_unit, analysis_context); resolve_types(compilation_unit, analysis_context); - infer_types(compilation_unit, analysis_context) + check_types(compilation_unit, analysis_context) }