diff --git a/dm-core/src/lib.dm b/dm-core/src/lib.dm index eff4bc1..6cccb85 100644 --- a/dm-core/src/lib.dm +++ b/dm-core/src/lib.dm @@ -1,6 +1,9 @@ +mod number mod ops mod print mod string +pub use number::* +pub use ops::* pub use print::* pub use string::* diff --git a/dm-core/src/number.dm b/dm-core/src/number.dm new file mode 100644 index 0000000..b824cbc --- /dev/null +++ b/dm-core/src/number.dm @@ -0,0 +1,31 @@ +pub extern class Int end + +pub extern class Double end + +impl Add for Int + intrinsic fn add(right: Self::Right) -> Self +end + +impl Add for Int + intrinsic fn add(right: Self::Right) -> Self::Result +end + +impl Add for Int + intrinsic fn add(right: Self::Right) -> Self::Result +end + +impl Multiply for Int + intrinsic fn multiply(right: Self::Right) -> Self::Result +end + +impl Multiply for Int + intrinsic fn multiply(right: Self::Right) -> Self::Result +end + +impl Negate for Int + intrinsic fn negate() -> Self::Result +end + +impl Negate for Double + intrinsic fn negate() -> Self::Result +end \ No newline at end of file diff --git a/dm-core/src/ops.dm b/dm-core/src/ops.dm index 85d8927..cd4c04a 100644 --- a/dm-core/src/ops.dm +++ b/dm-core/src/ops.dm @@ -4,3 +4,16 @@ pub trait Add fn add(right: Self::Right) -> Self::Result end + +pub trait Multiply + type Right = Self + type Result = Self + + fn multiply(right: Self::Right) -> Self::Result +end + +pub trait Negate + type Result = Self + + fn negate() -> Self::Result +end diff --git a/dm-core/src/string.dm b/dm-core/src/string.dm index f11eb90..34a35ac 100644 --- a/dm-core/src/string.dm +++ b/dm-core/src/string.dm @@ -2,4 +2,14 @@ pub extern class String pub extern fn len() -> Long end -intrinsic impl Add for String end +impl Add for String + intrinsic fn add +end + +impl Add for String + intrinsic fn add +end + +impl Add for String + intrinsic fn add +end diff --git a/dmc-lib/src/intrinsics.rs b/dmc-lib/src/intrinsics.rs index 81dc850..612ff5e 100644 --- a/dmc-lib/src/intrinsics.rs +++ b/dmc-lib/src/intrinsics.rs @@ -5,6 +5,34 @@ use crate::semantic_analysis::type_info::{FunctionTypeInfo, InstanceTypeInfo, Ty use std::collections::HashMap; pub fn add_primitive_symbols_and_type_infos(ctx: &mut AnalysisContext, global_scope_id: ScopeId) { + let int_class_symbol = Symbol::Class(ClassSymbol::new( + "Int".into(), + None, + "core::Int".into(), + true, + None, + HashMap::new(), + HashMap::new(), + )); + let int_symbol_id = ctx + .try_insert_symbol_in_scope(int_class_symbol, global_scope_id) + .unwrap(); + + let double_symbol = Symbol::Class(ClassSymbol::new( + "Double".into(), + None, + "core::Double".into(), + true, + None, + HashMap::new(), + HashMap::new(), + )); + let _double_symbol_id = ctx + .try_insert_symbol_in_scope(double_symbol, global_scope_id) + .unwrap(); + + let int_instance_type_info_id = ctx.instance_type(int_symbol_id); + let string_len_symbol = Symbol::Function(FunctionSymbol::new( "len".into(), None, @@ -38,11 +66,9 @@ pub fn add_primitive_symbols_and_type_infos(ctx: &mut AnalysisContext, global_sc ctx.associate_symbol_to_type(string_class_symbol_id, string_instance_type_info_id); - let string_len_return_type_info_id = ctx.insert_type_info(TypeInfo::Int); - let string_len_type_info = TypeInfo::Function(FunctionTypeInfo::new( vec![], - string_len_return_type_info_id, + int_instance_type_info_id, Some(string_instance_type_info_id), )); diff --git a/dmc-lib/src/lowering/util.rs b/dmc-lib/src/lowering/util.rs index d2a66fb..7b35ef3 100644 --- a/dmc-lib/src/lowering/util.rs +++ b/dmc-lib/src/lowering/util.rs @@ -13,8 +13,6 @@ pub fn to_ir_type_info(ctx: &AnalysisContext, sa_type_info: &TypeInfo) -> IrType IrTypeInfo::Instance(IrInstanceTypeInfo::new(class_symbol.fqn_owned())) } } - TypeInfo::Int => IrTypeInfo::Int, - TypeInfo::Double => IrTypeInfo::Double, TypeInfo::Void => IrTypeInfo::Void, _ => { panic!("BUG! Unknown sa_type_info: {:?}", sa_type_info); @@ -27,9 +25,7 @@ pub fn return_type_info_to_ir_type_info( return_type_info: &TypeInfo, ) -> Option { match return_type_info { - TypeInfo::Instance(_) | TypeInfo::Int | TypeInfo::Double => { - Some(to_ir_type_info(ctx, return_type_info)) - } + TypeInfo::Instance(_) => Some(to_ir_type_info(ctx, return_type_info)), TypeInfo::Void => None, _ => panic!("BUG! Unknown return_type_info: {:?}", return_type_info), } diff --git a/dmc-lib/src/semantic_analysis/analysis_context/helpers.rs b/dmc-lib/src/semantic_analysis/analysis_context/helpers.rs index ded7754..ef746d3 100644 --- a/dmc-lib/src/semantic_analysis/analysis_context/helpers.rs +++ b/dmc-lib/src/semantic_analysis/analysis_context/helpers.rs @@ -19,8 +19,6 @@ pub fn get_name_for_type<'ctx>(type_info: &TypeInfo, ctx: &'ctx AnalysisContext) let class_symbol = &ctx.symbols()[class_symbol_id]; class_symbol.declared_name() } - TypeInfo::Int => "Int", - TypeInfo::Double => "Double", TypeInfo::Void => "Void", TypeInfo::__Error => panic!("TypeInfo::__Error should never be shown to the user."), } diff --git a/dmc-lib/src/semantic_analysis/analysis_context/mod.rs b/dmc-lib/src/semantic_analysis/analysis_context/mod.rs index a2a295b..cd2cbd3 100644 --- a/dmc-lib/src/semantic_analysis/analysis_context/mod.rs +++ b/dmc-lib/src/semantic_analysis/analysis_context/mod.rs @@ -366,6 +366,10 @@ impl AnalysisContext { .expect(&format!("node_id {} has no associated type", node_id)) } + pub fn get_type_info_id_for_node_2(&self, node_id: NodeId) -> Option { + self.state.nodes_to_type_infos.get(&node_id).cloned() + } + pub fn get_type_info_for_node(&self, node_id: NodeId) -> &TypeInfo { let type_info_id = self.get_type_info_id_for_node(node_id); self.state @@ -402,6 +406,10 @@ impl AnalysisContext { *symbol_type_info_id } + pub fn get_type_info_id_for_symbol(&self, symbol_id: SymbolId) -> Option { + self.state.symbols_to_type_infos.get(&symbol_id).cloned() + } + pub fn find_symbol_by_fqn(&self, fqn: &str) -> Option { // This is quite naive and probably very slow; we will certainly use trees of HashMaps (or // structs with a HashMap and children) to represent the fqn hierarchy. @@ -427,16 +435,11 @@ impl AnalysisContext { self.state.nodes_to_scopes.get(&node_id).cloned() } - pub fn make_class_type( - &mut self, - class_symbol_id: SymbolId, - constructor_symbol_id: Option, - ) -> TypeInfoId { + pub fn make_class_type(&mut self, class_symbol_id: SymbolId) -> TypeInfoId { if let Some(type_info_id) = self.state.symbols_to_type_infos.get(&class_symbol_id) { *type_info_id } else { - let type_info = - TypeInfo::Class(ClassTypeInfo::new(class_symbol_id, constructor_symbol_id)); + let type_info = TypeInfo::Class(ClassTypeInfo::new(class_symbol_id)); self.state.type_infos.push(type_info); let type_info_id = self.state.type_infos.len() - 1; self.state @@ -458,7 +461,26 @@ impl AnalysisContext { todo!() } - pub fn type_by_fqn(&mut self, fqn: &str) -> &TypeInfoId { + pub fn error_type(&self) -> TypeInfoId { todo!() } + + pub fn instance_type(&mut self, class_symbol_id: SymbolId) -> TypeInfoId { + todo!() + } + + pub fn instance_type_by_fqn(&self, fqn: &str) -> TypeInfoId { + todo!() + } + + pub fn get_symbol(&self, symbol_id: SymbolId) -> Option<&Symbol> { + self.state.symbols.get(symbol_id) + } + + pub fn get_type_info_for_symbol(&self, symbol_id: SymbolId) -> Option<&TypeInfo> { + self.state + .symbols_to_type_infos + .get(&symbol_id) + .and_then(|type_info_id| self.state.type_infos.get(*type_info_id)) + } } diff --git a/dmc-lib/src/semantic_analysis/collect_types.rs b/dmc-lib/src/semantic_analysis/collect_types.rs index f06e499..fb5c720 100644 --- a/dmc-lib/src/semantic_analysis/collect_types.rs +++ b/dmc-lib/src/semantic_analysis/collect_types.rs @@ -57,8 +57,6 @@ fn get_intrinsic_type(declared_name: &str) -> Option { match declared_name { "Any" => Some(TypeInfo::Any), "Void" => Some(TypeInfo::Void), - "Int" => Some(TypeInfo::Int), - "Double" => Some(TypeInfo::Double), _ => None, } } @@ -78,8 +76,6 @@ fn declared_name_to_type_info(ctx: &AnalysisContext, declared_name: &str) -> Typ .expect("Missing core::String from ctx"); ctx.type_infos()[symbol_id].clone() } - "Int" => TypeInfo::Int, - "Double" => TypeInfo::Double, "Void" => TypeInfo::Void, _ => { panic!() @@ -147,10 +143,8 @@ fn collect_types_class(class: &Class, ctx: &mut AnalysisContext) { // todo: fields, methods // Self type constructor - let type_constructor_type_info = TypeInfo::Class(ClassTypeInfo::new( - ctx.nodes_to_symbols()[&class.node_id()], - constructor_type_info_id, - )); + let type_constructor_type_info = + TypeInfo::Class(ClassTypeInfo::new(ctx.nodes_to_symbols()[&class.node_id()])); let type_constructor_type_info_id = ctx.insert_type_info(type_constructor_type_info); ctx.associate_node_and_symbol_to_type(class.node_id(), type_constructor_type_info_id); } diff --git a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs index d0a903c..8a5c884 100644 --- a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs +++ b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs @@ -150,10 +150,10 @@ fn resolve_types_expression( resolve_types_identifier(identifier, ctx); } Expression::Integer(integer_literal) => { - ctx.insert_type_info_for_node(TypeInfo::Int, integer_literal.node_id()); + panic!() } Expression::Double(double_literal) => { - ctx.insert_type_info_for_node(TypeInfo::Double, double_literal.node_id()); + panic!() } Expression::String(string_literal) => { let string_class_symbol_id = ctx diff --git a/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs b/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs index bb35600..67f8b36 100644 --- a/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs +++ b/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs @@ -23,7 +23,8 @@ pub fn are_binary_op_compatible( } fn is_number(type_info: &TypeInfo) -> bool { - matches!(type_info, TypeInfo::Int | TypeInfo::Double) + // matches!(type_info, TypeInfo::Int | TypeInfo::Double) + panic!() } fn are_numbers(t0: &TypeInfo, t1: &TypeInfo) -> bool { @@ -45,7 +46,8 @@ fn is_string(ctx: &AnalysisContext, type_info: &TypeInfo) -> bool { } fn are_ints(t0: &TypeInfo, t1: &TypeInfo) -> bool { - matches!(t0, TypeInfo::Int) && matches!(t1, TypeInfo::Int) + // matches!(t0, TypeInfo::Int) && matches!(t1, TypeInfo::Int) + panic!() } pub fn binary_op_result( @@ -56,7 +58,7 @@ pub fn binary_op_result( ) -> TypeInfo { match op { BinaryOperation::Multiply => numbers_binary_result(left, right), - BinaryOperation::Divide => TypeInfo::Double, // okay for now + BinaryOperation::Divide => panic!(), // okay for now BinaryOperation::Modulo => numbers_binary_result(left, right), // same properties as multiplication BinaryOperation::Add => add_result(ctx, left, right), BinaryOperation::Subtract => numbers_binary_result(left, right), @@ -64,23 +66,23 @@ pub fn binary_op_result( | BinaryOperation::RightShift | BinaryOperation::BitwiseAnd | BinaryOperation::BitwiseXor - | BinaryOperation::BitwiseOr => TypeInfo::Int, + | BinaryOperation::BitwiseOr => panic!(), } } fn numbers_binary_result(left: &TypeInfo, right: &TypeInfo) -> TypeInfo { match left { - TypeInfo::Int => match right { - TypeInfo::Int => TypeInfo::Int, - TypeInfo::Double => TypeInfo::Double, - TypeInfo::__Error => TypeInfo::__Error, - _ => panic!(), - }, - TypeInfo::Double => match right { - TypeInfo::Int | TypeInfo::Double => TypeInfo::Double, - TypeInfo::__Error => TypeInfo::__Error, - _ => panic!(), - }, + // TypeInfo::Int => match right { + // TypeInfo::Int => TypeInfo::Int, + // TypeInfo::Double => TypeInfo::Double, + // TypeInfo::__Error => TypeInfo::__Error, + // _ => panic!(), + // }, + // TypeInfo::Double => match right { + // TypeInfo::Int | TypeInfo::Double => TypeInfo::Double, + // TypeInfo::__Error => TypeInfo::__Error, + // _ => panic!(), + // }, TypeInfo::__Error => TypeInfo::__Error, _ => panic!(), } @@ -95,31 +97,31 @@ fn add_result(ctx: &AnalysisContext, left: &TypeInfo, right: &TypeInfo) -> TypeI todo!("Adding with non-String, non-number types") } } - TypeInfo::Int => match right { - TypeInfo::Instance(right_instance_type_info) => { - if is_string(ctx, right) { - TypeInfo::Instance(right_instance_type_info.clone()) - } else { - todo!("Adding with non-String, non-number types") - } - } - TypeInfo::Int => TypeInfo::Int, - TypeInfo::Double => TypeInfo::Double, - TypeInfo::__Error => TypeInfo::__Error, - _ => panic!(), - }, - TypeInfo::Double => match right { - TypeInfo::Instance(right_instance_type_info) => { - if is_string(ctx, right) { - TypeInfo::Instance(right_instance_type_info.clone()) - } else { - todo!("Adding with non-String, non-number types") - } - } - TypeInfo::Int | TypeInfo::Double => TypeInfo::Double, - TypeInfo::__Error => TypeInfo::__Error, - _ => panic!(), - }, + // TypeInfo::Int => match right { + // TypeInfo::Instance(right_instance_type_info) => { + // if is_string(ctx, right) { + // TypeInfo::Instance(right_instance_type_info.clone()) + // } else { + // todo!("Adding with non-String, non-number types") + // } + // } + // TypeInfo::Int => TypeInfo::Int, + // TypeInfo::Double => TypeInfo::Double, + // TypeInfo::__Error => TypeInfo::__Error, + // _ => panic!(), + // }, + // TypeInfo::Double => match right { + // TypeInfo::Instance(right_instance_type_info) => { + // if is_string(ctx, right) { + // TypeInfo::Instance(right_instance_type_info.clone()) + // } else { + // todo!("Adding with non-String, non-number types") + // } + // } + // // TypeInfo::Int | TypeInfo::Double => TypeInfo::Double, + // TypeInfo::__Error => TypeInfo::__Error, + // _ => panic!(), + // }, TypeInfo::__Error => TypeInfo::__Error, _ => panic!(), } @@ -128,14 +130,14 @@ fn add_result(ctx: &AnalysisContext, left: &TypeInfo, right: &TypeInfo) -> TypeI pub fn can_negate(operand: &TypeInfo) -> bool { matches!( operand, - TypeInfo::Int | TypeInfo::Double | TypeInfo::__Error + /* TypeInfo::Int | TypeInfo::Double */ | TypeInfo::__Error ) } pub fn negate_result(operand: &TypeInfo) -> TypeInfo { match operand { - TypeInfo::Int => TypeInfo::Int, - TypeInfo::Double => TypeInfo::Double, + // TypeInfo::Int => TypeInfo::Int, + // TypeInfo::Double => TypeInfo::Double, TypeInfo::__Error => TypeInfo::__Error, _ => panic!(), } @@ -159,14 +161,14 @@ pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo, ctx: &Analysi } _ => false, }, - TypeInfo::Int => match right { - TypeInfo::Int => true, - _ => false, - }, - TypeInfo::Double => match right { - TypeInfo::Double | TypeInfo::Int => true, - _ => false, - }, + // TypeInfo::Int => match right { + // TypeInfo::Int => true, + // _ => false, + // }, + // TypeInfo::Double => match right { + // TypeInfo::Double | TypeInfo::Int => true, + // _ => false, + // }, TypeInfo::Void => false, TypeInfo::__Error => true, } diff --git a/dmc-lib/src/semantic_analysis/type_analysis/collect.rs b/dmc-lib/src/semantic_analysis/type_analysis/collect.rs index 10eabe8..fab8939 100644 --- a/dmc-lib/src/semantic_analysis/type_analysis/collect.rs +++ b/dmc-lib/src/semantic_analysis/type_analysis/collect.rs @@ -1,7 +1,6 @@ use crate::ast::class::Class; use crate::ast::compilation_unit::CompilationUnit; use crate::semantic_analysis::analysis_context::AnalysisContext; -use crate::semantic_analysis::type_info::{InstanceTypeInfo, TypeInfo}; pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) { for class in compilation_unit.classes() { @@ -10,14 +9,10 @@ pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisConte } fn collect_types_class(class: &Class, ctx: &mut AnalysisContext) { - let maybe_constructor_type_info_id = class - .constructor() - .and_then(|constructor| ctx.get_symbol_id(constructor.node_id())); - let class_symbol_id = ctx.get_symbol_id(class.node_id()).expect(&format!( "Could not find symbol for {} {}", class.node_id(), class.declared_name() )); - ctx.make_class_type(class_symbol_id, maybe_constructor_type_info_id); + ctx.make_class_type(class_symbol_id); } diff --git a/dmc-lib/src/semantic_analysis/type_analysis/infer.rs b/dmc-lib/src/semantic_analysis/type_analysis/infer.rs deleted file mode 100644 index 8b13789..0000000 --- a/dmc-lib/src/semantic_analysis/type_analysis/infer.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/dmc-lib/src/semantic_analysis/type_analysis/infer/helpers.rs b/dmc-lib/src/semantic_analysis/type_analysis/infer/helpers.rs new file mode 100644 index 0000000..782b707 --- /dev/null +++ b/dmc-lib/src/semantic_analysis/type_analysis/infer/helpers.rs @@ -0,0 +1,57 @@ +use crate::ast::binary_expression::BinaryOperation; +use crate::semantic_analysis::analysis_context::AnalysisContext; +use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; + +pub fn are_binary_op_compatible( + op: &BinaryOperation, + lhs: &TypeInfo, + rhs: &TypeInfo, + ctx: &AnalysisContext, +) -> bool { + match lhs { + TypeInfo::Any + | TypeInfo::Class(_) + | TypeInfo::Constructor(_) + | TypeInfo::Function(_) + | TypeInfo::Void => false, + TypeInfo::Instance(instance_type_info) => { + instance_type_info.is_binary_op_compatible(op, rhs, ctx) + } + TypeInfo::__Error => true, + } +} + +pub fn binary_op_result( + op: &BinaryOperation, + lhs_type_info: &TypeInfo, + rhs_type_info: &TypeInfo, + ctx: &AnalysisContext, +) -> TypeInfoId { + todo!() +} + +pub fn is_negative_op_compatible(operand: &TypeInfo, ctx: &AnalysisContext) -> bool { + match operand { + TypeInfo::Any + | TypeInfo::Class(_) + | TypeInfo::Constructor(_) + | TypeInfo::Function(_) + | TypeInfo::Void => false, + TypeInfo::Instance(instance_type_info) => instance_type_info.is_negative_op_compatible(ctx), + TypeInfo::__Error => true, + } +} + +pub fn negative_op_result(operand: &TypeInfo, ctx: &AnalysisContext) -> TypeInfoId { + match operand { + TypeInfo::Any + | TypeInfo::Class(_) + | TypeInfo::Constructor(_) + | TypeInfo::Function(_) + | TypeInfo::Void => { + unreachable!() + } + TypeInfo::Instance(instance_type_info) => instance_type_info.negative_op_result(ctx), + TypeInfo::__Error => ctx.error_type(), + } +} diff --git a/dmc-lib/src/semantic_analysis/type_analysis/infer/mod.rs b/dmc-lib/src/semantic_analysis/type_analysis/infer/mod.rs new file mode 100644 index 0000000..7158d91 --- /dev/null +++ b/dmc-lib/src/semantic_analysis/type_analysis/infer/mod.rs @@ -0,0 +1,210 @@ +pub mod helpers; + +use crate::ast::binary_expression::{BinaryExpression, BinaryOperation}; +use crate::ast::call::Call; +use crate::ast::class::Class; +use crate::ast::compilation_unit::CompilationUnit; +use crate::ast::expression::Expression; +use crate::ast::function::Function; +use crate::ast::let_statement::LetStatement; +use crate::ast::negative_expression::NegativeExpression; +use crate::ast::statement::Statement; +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::{ + 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 { + let mut diagnostics = Diagnostics::new(); + for class in compilation_unit.classes() { + infer_types_class(class, ctx, &mut diagnostics); + } + diagnostics +} + +fn infer_types_class(class: &Class, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) { + for function in class.functions() { + infer_types_function(function, ctx, diagnostics); + } +} + +fn infer_types_function( + function: &Function, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) { + for statement in function.statements() { + match statement { + Statement::Let(let_statement) => { + infer_types_let_statement(let_statement, ctx, diagnostics); + } + Statement::Expression(expression_statement) => {} + Statement::Assign(assign_statement) => {} + } + } +} + +fn infer_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 +} + +fn infer_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) + } + Expression::Negative(negative_expression) => { + resolve_types_negative_expression(negative_expression, ctx, diagnostics) + } + Expression::Call(call) => resolve_types_call(call, ctx, diagnostics), + Expression::Path(path) => { + todo!("type of path") + } + Expression::Identifier(identifier) => { + let symbol_id = ctx.get_symbol_id(identifier.node_id()).expect(&format!( + "could not get symbol for node_id {}", + identifier.node_id() + )); + if let Some(type_info_id) = ctx.get_type_info_id_for_symbol(symbol_id) { + type_info_id + } else { + panic!("could not get type_info_id for symbol_id {}", symbol_id); + } + } + Expression::InstanceSelf(instance_self) => { + todo!("type of self (instance self)") + } + Expression::Integer(_) => ctx.instance_type_by_fqn("core::Int"), + Expression::Double(_) => ctx.instance_type_by_fqn("core::Double"), + Expression::String(_) => ctx.instance_type_by_fqn("core::String"), + } +} + +fn infer_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 = ctx.get_type_info_by_id(lhs_type_info_id); + let rhs_type_info = ctx.get_type_info_by_id(rhs_type_info_id); + + let result_type_info_id = + if are_binary_op_compatible(binary_expression.op(), lhs_type_info, rhs_type_info, ctx) { + binary_op_result(binary_expression.op(), lhs_type_info, rhs_type_info, ctx) + } else { + let op_name = match binary_expression.op() { + BinaryOperation::Multiply => "multiply", + BinaryOperation::Divide => "divide", + BinaryOperation::Modulo => "modulo", + BinaryOperation::Add => "add", + BinaryOperation::Subtract => "subtract", + BinaryOperation::LeftShift => "left shift", + BinaryOperation::RightShift => "right shift", + BinaryOperation::BitwiseAnd => "bitwise and", + BinaryOperation::BitwiseXor => "bitwise xor", + BinaryOperation::BitwiseOr => "bitwise or", + }; + let lhs_type_name = get_name_for_type(lhs_type_info, ctx); + let rhs_type_name = get_name_for_type(rhs_type_info, ctx); + let message = format!( + "Incompatible types: cannot {} {} and {}", + op_name, lhs_type_name, rhs_type_name + ); + let diagnostic = Diagnostic::new( + &message, + binary_expression.source_range().start(), + binary_expression.source_range().end(), + ) + .with_error_code(BINARY_INCOMPATIBLE_TYPES); + diagnostics.push(diagnostic); + + ctx.error_type() + }; + + ctx.associate_node_to_type(binary_expression.node_id(), result_type_info_id); + + result_type_info_id +} + +fn resolve_types_negative_expression( + negative_expression: &NegativeExpression, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) -> TypeInfoId { + let operand_type_info_id = + infer_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) + } else { + let diagnostic = Diagnostic::new( + &format!( + "Cannot negate operand of type {}", + get_name_for_type(operand_type_info, ctx) + ), + negative_expression.source_range().start(), + negative_expression.source_range().end(), + ); + diagnostics.push(diagnostic); + ctx.error_type() + }; + + ctx.associate_node_to_type(negative_expression.node_id(), operand_type_info_id); + + result_type_info_id +} + +fn resolve_types_call( + call: &Call, + ctx: &mut AnalysisContext, + diagnostics: &mut Diagnostics, +) -> TypeInfoId { + let receiver_type_info_id = infer_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 => { + let diagnostic = Diagnostic::new( + &format!( + "Cannot call receiver of type {}", + match receiver_type_info { + TypeInfo::Any => "Any", + TypeInfo::Void => "Void", + _ => unreachable!(), + } + ), + call.source_range().start(), + call.source_range().end(), + ); + diagnostics.push(diagnostic); + ctx.error_type() + } + TypeInfo::Class(class_type_info) => { + let symbol = class_type_info.class_symbol_id(); + ctx.instance_type(symbol) + } + TypeInfo::Constructor(_) => { + panic!("Cannot infer expression type directly on ConstructorTypeInfo"); + } + TypeInfo::Function(function_type_info) => function_type_info.return_type_id(), + TypeInfo::Instance(_) => { + unimplemented!("Calling instances of objects not yet supported.") + } + TypeInfo::__Error => ctx.error_type(), + } +} diff --git a/dmc-lib/src/semantic_analysis/type_analysis/mod.rs b/dmc-lib/src/semantic_analysis/type_analysis/mod.rs index 56ebac2..d472741 100644 --- a/dmc-lib/src/semantic_analysis/type_analysis/mod.rs +++ b/dmc-lib/src/semantic_analysis/type_analysis/mod.rs @@ -1,13 +1,19 @@ use crate::ast::compilation_unit::CompilationUnit; +use crate::diagnostic::Diagnostics; use crate::semantic_analysis::analysis_context::AnalysisContext; 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 collect; mod infer; mod resolve; -pub fn analyze_types(compilation_unit: &CompilationUnit, analysis_context: &mut AnalysisContext) { +pub fn analyze_types( + compilation_unit: &CompilationUnit, + analysis_context: &mut AnalysisContext, +) -> Diagnostics { collect_types(compilation_unit, analysis_context); resolve_types(compilation_unit, analysis_context); + infer_types(compilation_unit, analysis_context) } diff --git a/dmc-lib/src/semantic_analysis/type_info.rs b/dmc-lib/src/semantic_analysis/type_info.rs index 014d043..86104d9 100644 --- a/dmc-lib/src/semantic_analysis/type_info.rs +++ b/dmc-lib/src/semantic_analysis/type_info.rs @@ -1,5 +1,6 @@ +use crate::ast::binary_expression::BinaryOperation; use crate::semantic_analysis::analysis_context::AnalysisContext; -use crate::semantic_analysis::symbol::SymbolId; +use crate::semantic_analysis::symbol::{ClassSymbol, SymbolId}; pub type TypeInfoId = usize; @@ -10,8 +11,6 @@ pub enum TypeInfo { Constructor(ConstructorTypeInfo), Function(FunctionTypeInfo), Instance(InstanceTypeInfo), - Int, - Double, Void, __Error, } @@ -23,13 +22,6 @@ impl TypeInfo { _ => panic!("Attempt to unwrap {:?} as Constructor", self), } } - - pub fn unwrap_class(&self) -> &ClassTypeInfo { - match self { - TypeInfo::Class(type_constructor_info) => type_constructor_info, - _ => panic!("Attempt to unwrap {:?} as TypeConstructor", self), - } - } } #[derive(Clone, Debug)] @@ -91,24 +83,16 @@ impl ConstructorTypeInfo { #[derive(Clone, Debug)] pub struct ClassTypeInfo { class_symbol_id: SymbolId, - constructor_symbol_id: Option, } impl ClassTypeInfo { - pub fn new(class_symbol_id: SymbolId, constructor_symbol_id: Option) -> Self { - Self { - class_symbol_id, - constructor_symbol_id, - } + pub fn new(class_symbol_id: SymbolId) -> Self { + Self { class_symbol_id } } pub fn class_symbol_id(&self) -> SymbolId { self.class_symbol_id } - - pub fn constructor_type_info_id(&self) -> Option { - self.constructor_symbol_id - } } #[derive(Clone, Debug)] @@ -130,4 +114,94 @@ impl InstanceTypeInfo { let right_class_symbol = ctx.symbols()[right.class_symbol_id].unwrap_class_symbol(); self_class_symbol.can_assign_from(right_class_symbol, ctx) } + + fn get_self_class_symbol<'a>(&self, ctx: &'a AnalysisContext) -> &'a ClassSymbol { + ctx.get_symbol(self.class_symbol_id) + .expect(&format!( + "self_class_symbol {} not found", + self.class_symbol_id + )) + .unwrap_class_symbol() + } + + pub fn is_binary_op_compatible( + &self, + op: &BinaryOperation, + right: &TypeInfo, + ctx: &AnalysisContext, + ) -> bool { + match right { + TypeInfo::Any + | TypeInfo::Class(_) + | TypeInfo::Constructor(_) + | TypeInfo::Function(_) + | TypeInfo::Void => false, + TypeInfo::Instance(right_instance_type_info) => { + // this is all just filler until we have proper traits and classes + let right_fqn = right_instance_type_info.get_self_class_symbol(ctx).fqn(); + match self.get_self_class_symbol(ctx).fqn() { + "core::String" => { + if let BinaryOperation::Add = op { + true + } else { + false + } + } + "core::Int" => match op { + BinaryOperation::Add => true, + BinaryOperation::Multiply + | BinaryOperation::Divide + | BinaryOperation::Modulo + | BinaryOperation::Subtract => match right_fqn { + "core::Int" | "core::Double" => true, + _ => false, + }, + BinaryOperation::LeftShift + | BinaryOperation::RightShift + | BinaryOperation::BitwiseAnd + | BinaryOperation::BitwiseXor + | BinaryOperation::BitwiseOr => match right_fqn { + "core::Int" => true, + _ => false, + }, + }, + "core::Double" => match op { + BinaryOperation::Add => true, + BinaryOperation::Multiply + | BinaryOperation::Divide + | BinaryOperation::Modulo + | BinaryOperation::Subtract => match right_fqn { + "core::Int" => true, + _ => false, + }, + BinaryOperation::LeftShift + | BinaryOperation::RightShift + | BinaryOperation::BitwiseAnd + | BinaryOperation::BitwiseXor + | BinaryOperation::BitwiseOr => false, + }, + _ => panic!("unknown type: {}", self.get_self_class_symbol(ctx).fqn()), + } + } + TypeInfo::__Error => true, + } + } + + pub fn is_negative_op_compatible(&self, ctx: &AnalysisContext) -> bool { + match self.get_self_class_symbol(ctx).fqn() { + "core::Int" | "core::Double" => true, + _ => false, + } + } + + pub fn negative_op_result(&self, ctx: &AnalysisContext) -> TypeInfoId { + match self.get_self_class_symbol(ctx).fqn() { + "core::Int" => ctx.instance_type_by_fqn("core::Int"), + "core::Double" => ctx.instance_type_by_fqn("core::Double"), + _ => panic!( + "unknown negatable type: {}", + self.get_self_class_symbol(ctx).fqn() + ), + } + } }