diff --git a/dm/src/main.rs b/dm/src/main.rs index c58a42b..700f8ab 100644 --- a/dm/src/main.rs +++ b/dm/src/main.rs @@ -2,7 +2,7 @@ mod repl; mod run; use crate::repl::repl; -use crate::run::run; +use crate::run::compile_and_run_script; use clap::{Parser, Subcommand}; use std::path::PathBuf; @@ -42,7 +42,7 @@ fn main() { show_ir, register_count, } => { - run(script, *show_asm, *show_ir, *register_count); + compile_and_run_script(script, *show_asm, *show_ir, *register_count); } SubCommand::Repl { register_count } => { repl(*register_count); diff --git a/dm/src/repl.rs b/dm/src/repl.rs index d8fbca5..c09acb9 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -8,6 +8,7 @@ use dmc_lib::lexer::Lexer; use dmc_lib::parser::parse_expression; use dmc_lib::symbol_table::SymbolTable; use dmc_lib::token::TokenKind; +use dmc_lib::types_table::TypesTable; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; use dvm_lib::vm::operand::Operand; @@ -97,24 +98,30 @@ fn compile_expression( let mut expression = parse_result?; let mut symbol_table = SymbolTable::new(); + let mut types_table = TypesTable::new(); // "fake" scopes symbol_table.push_module_scope("__repl_module"); - symbol_table.push_function_scope("__repl_function"); + let function_scope_id = symbol_table.push_function_scope("__repl_function"); - expression.gather_declared_names(&mut symbol_table)?; + expression.init_scopes(&mut symbol_table, function_scope_id); symbol_table.pop_scope(); // function symbol_table.pop_scope(); // module - expression.check_name_usages(&symbol_table)?; - expression.type_check(&symbol_table)?; + let diagnostics = expression.check_static_fn_local_names(&symbol_table); + if !diagnostics.is_empty() { + return Err(diagnostics); + } + + expression.type_check(&symbol_table, &mut types_table)?; // synthesize a function let mut ir_builder = IrBuilder::new(); let entry_block_id = ir_builder.new_block(); - let maybe_ir_expression = expression.to_ir_expression(&mut ir_builder, &symbol_table); + let maybe_ir_expression = + expression.to_ir_expression(&mut ir_builder, &symbol_table, &types_table); // if Some, return the value ir_builder @@ -126,8 +133,8 @@ fn compile_expression( let mut ir_function = IrFunction::new( "__repl".into(), - &[], - expression.type_info(), + vec![], + expression.type_info(&symbol_table, &types_table), entry_block.clone(), ); diff --git a/dm/src/run.rs b/dm/src/run.rs index 4c7ab93..4e5dbe3 100644 --- a/dm/src/run.rs +++ b/dm/src/run.rs @@ -7,6 +7,7 @@ use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::Diagnostic; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; +use dmc_lib::types_table::TypesTable; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; use dvm_lib::vm::operand::Operand; @@ -14,45 +15,42 @@ use dvm_lib::vm::{CallStack, DvmContext, call}; use std::path::PathBuf; use std::rc::Rc; -pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usize) { +pub fn compile_and_run_script( + script: &PathBuf, + show_ir: bool, + show_asm: bool, + register_count: usize, +) { let input = std::fs::read_to_string(script).unwrap(); let mut files: SimpleFiles<&str, &str> = SimpleFiles::new(); let script_file_id = files.add(script.to_str().unwrap(), &input); - let parse_result = parse_compilation_unit(&input); - let mut compilation_unit = match parse_result { - Ok(compilation_unit) => compilation_unit, + match run(&input, show_ir, show_asm, register_count) { + Ok(_) => {} Err(diagnostics) => { check_and_report_diagnostics(&files, script_file_id, &diagnostics); - unreachable!(); } - }; + } +} + +fn run( + input: &str, + show_ir: bool, + show_asm: bool, + register_count: usize, +) -> Result<(), Vec> { + let mut compilation_unit = parse_compilation_unit(&input)?; let mut symbol_table = SymbolTable::new(); + let mut types_table = TypesTable::new(); - match compilation_unit.gather_declared_names(&mut symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - report_and_exit(&diagnostics, script_file_id, &files); - } - } + compilation_unit.init_scopes(&mut symbol_table); + compilation_unit.gather_symbols_into(&mut symbol_table)?; + compilation_unit.check_names(&mut symbol_table)?; + compilation_unit.type_check(&symbol_table, &mut types_table)?; - match compilation_unit.check_name_usages(&symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - report_and_exit(&diagnostics, script_file_id, &files); - } - } - - match compilation_unit.type_check(&symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - report_and_exit(&diagnostics, script_file_id, &files); - } - } - - let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table); + let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table, &types_table); if show_ir { for ir_function in &ir_functions { @@ -117,6 +115,8 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz if let Some(value) = result { println!("{}", value); } + + Ok(()) } fn check_and_report_diagnostics( diff --git a/dmc-lib/src/ast/assign_statement.rs b/dmc-lib/src/ast/assign_statement.rs index 189cf5f..5f04377 100644 --- a/dmc-lib/src/ast/assign_statement.rs +++ b/dmc-lib/src/ast/assign_statement.rs @@ -6,8 +6,10 @@ use crate::error_codes::{ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_N use crate::ir::ir_assign::IrAssign; use crate::ir::ir_set_field::IrSetField; use crate::ir::ir_statement::IrStatement; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol_table::SymbolTable; +use crate::types_table::TypesTable; pub struct AssignStatement { destination: Box, @@ -26,68 +28,68 @@ impl AssignStatement { &self.destination } - pub fn gather_declared_names( + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.destination.init_scopes(symbol_table, container_scope); + self.value.init_scopes(symbol_table, container_scope); + } + + pub fn check_constructor_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + [ + self.destination + .check_constructor_local_names(symbol_table, class_symbol), + self.value + .check_constructor_local_names(symbol_table, class_symbol), + ] + .into_iter() + .flatten() + .collect() + } + + pub fn check_method_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + [ + self.destination + .check_method_local_names(symbol_table, class_symbol), + self.value + .check_method_local_names(symbol_table, class_symbol), + ] + .into_iter() + .flatten() + .collect() + } + + pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec { + [ + self.destination.check_static_fn_local_names(symbol_table), + self.value.check_static_fn_local_names(symbol_table), + ] + .into_iter() + .flatten() + .collect() + } + + pub fn type_check( &mut self, - symbol_table: &mut SymbolTable, + symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; - match self.value.gather_declared_names(symbol_table) { + match self.value.type_check(symbol_table, types_table) { Ok(_) => {} Err(mut value_diagnostics) => { diagnostics.append(&mut value_diagnostics); } } - match self.destination.gather_declared_names(symbol_table) { - Ok(_) => {} - Err(mut destination_diagnostics) => { - diagnostics.append(&mut destination_diagnostics); - } - } - - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } - } - - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let mut diagnostics: Vec = vec![]; - - match self.value.check_name_usages(symbol_table) { - Ok(_) => {} - Err(mut value_diagnostics) => { - diagnostics.append(&mut value_diagnostics); - } - } - - match self.destination.check_name_usages(symbol_table) { - Ok(_) => {} - Err(mut destination_diagnostics) => { - diagnostics.append(&mut destination_diagnostics); - } - } - - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } - } - - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let mut diagnostics: Vec = vec![]; - - match self.value.type_check(symbol_table) { - Ok(_) => {} - Err(mut value_diagnostics) => { - diagnostics.append(&mut value_diagnostics); - } - } - - match self.destination.type_check(symbol_table) { + match self.destination.type_check(symbol_table, types_table) { Ok(_) => {} Err(mut destination_diagnostics) => { diagnostics.append(&mut destination_diagnostics); @@ -97,10 +99,18 @@ impl AssignStatement { // check destination is l value match &*self.destination { Expression::Identifier(identifier) => { - let expressible_symbol = identifier.expressible_symbol(); + let expressible_symbol = symbol_table + .find_expressible_symbol(identifier.scope_id(), identifier.name()) + .unwrap(); + + let is_mut = match &expressible_symbol { + ExpressibleSymbol::Field(field_symbol) => field_symbol.is_mut(), + ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.is_mut(), + _ => false, + }; // check mutable - if !expressible_symbol.is_mut() { + if !is_mut { let secondary_label = SecondaryLabel::new( expressible_symbol.source_range().start(), expressible_symbol.source_range().end(), @@ -119,8 +129,17 @@ impl AssignStatement { } // check assignable - let lhs_type = expressible_symbol.type_info(); - let rhs_type = self.value.type_info(); + let lhs_type = match &expressible_symbol { + ExpressibleSymbol::Field(field_symbol) => { + types_table.field_types().get(field_symbol).unwrap() + } + ExpressibleSymbol::Variable(variable_symbol) => { + types_table.variable_types().get(variable_symbol).unwrap() + } + _ => panic!(), + }; + + let rhs_type = self.value.type_info(symbol_table, types_table); if !lhs_type.is_assignable_from(rhs_type) { let secondary_label = SecondaryLabel::new( expressible_symbol.source_range().start(), @@ -168,29 +187,37 @@ impl AssignStatement { } } - pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { + pub fn to_ir( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) { let destination_symbol = match &*self.destination { - Expression::Identifier(identifier) => identifier.expressible_symbol(), + Expression::Identifier(identifier) => symbol_table + .find_expressible_symbol(identifier.scope_id(), identifier.name()) + .unwrap(), _ => unreachable!("Destination must be a mutable L value"), }; let ir_statement = match destination_symbol { ExpressibleSymbol::Field(field_symbol) => { let mut_field_pointer_variable = - get_or_init_mut_field_pointer_variable(builder, field_symbol).clone(); + get_or_init_mut_field_pointer_variable(builder, &field_symbol).clone(); let ir_set_field = IrSetField::new( &mut_field_pointer_variable, self.value - .to_ir_expression(builder, symbol_table) + .to_ir_expression(builder, symbol_table, types_table) .expect("Attempt to convert non-value to value"), ); IrStatement::SetField(ir_set_field) } ExpressibleSymbol::Variable(variable_symbol) => { - let vr_variable = variable_symbol.borrow().vr_variable().clone(); + let vr_variable = builder.local_variables().get(&variable_symbol).unwrap(); let ir_assign = IrAssign::new( - vr_variable, - self.value.to_ir_operation(builder, symbol_table), + vr_variable.clone(), + self.value + .to_ir_operation(builder, symbol_table, types_table), ); IrStatement::Assign(ir_assign) } @@ -203,12 +230,24 @@ impl AssignStatement { #[cfg(test)] mod tests { + use crate::ast::compilation_unit::CompilationUnit; + use crate::diagnostic::Diagnostic; use crate::error_codes::{ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_NO_L_VALUE}; use crate::parser::parse_compilation_unit; use crate::symbol_table::SymbolTable; + use crate::types_table::TypesTable; + + fn compile_up_to_type_check( + compilation_unit: &mut CompilationUnit, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { + compilation_unit.init_scopes(symbol_table); + compilation_unit.gather_symbols_into(symbol_table)?; + compilation_unit.check_names(symbol_table) + } #[test] - fn finds_mismatched_types() { + fn finds_mismatched_types() -> Result<(), Vec> { let mut compilation_unit = parse_compilation_unit( " fn main() @@ -219,52 +258,41 @@ mod tests { ) .unwrap(); let mut symbol_table = SymbolTable::new(); - compilation_unit - .gather_declared_names(&mut symbol_table) - .unwrap(); - compilation_unit.check_name_usages(&symbol_table).unwrap(); - match compilation_unit.type_check(&symbol_table) { - Ok(_) => { - panic!("Type check missed diagnostic."); - } - Err(diagnostics) => { - assert_eq!(diagnostics.len(), 1); - assert_eq!( - diagnostics[0].error_code().unwrap(), - ASSIGN_MISMATCHED_TYPES - ); - } - } + compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?; + let mut types_table = TypesTable::new(); + let diagnostics = compilation_unit + .type_check(&symbol_table, &mut types_table) + .unwrap_err(); + assert_eq!(diagnostics.len(), 1); + assert_eq!( + diagnostics[0].error_code().unwrap(), + ASSIGN_MISMATCHED_TYPES + ); + Ok(()) } #[test] - fn finds_no_l_value() { + fn finds_no_l_value() -> Result<(), Vec> { let mut compilation_unit = parse_compilation_unit( " fn main() 42 = 42 end ", - ) - .unwrap(); + )?; let mut symbol_table = SymbolTable::new(); - compilation_unit - .gather_declared_names(&mut symbol_table) - .unwrap(); - compilation_unit.check_name_usages(&symbol_table).unwrap(); - match compilation_unit.type_check(&symbol_table) { - Ok(_) => { - panic!("Type check missed diagnostic."); - } - Err(diagnostics) => { - assert_eq!(diagnostics.len(), 1); - assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_NO_L_VALUE); - } - } + compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?; + let mut types_table = TypesTable::new(); + let diagnostics = compilation_unit + .type_check(&symbol_table, &mut types_table) + .unwrap_err(); + assert_eq!(diagnostics.len(), 1); + assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_NO_L_VALUE); + Ok(()) } #[test] - fn finds_immutable_destination() { + fn finds_immutable_destination() -> Result<(), Vec> { let mut compilation_unit = parse_compilation_unit( " fn main() @@ -272,21 +300,15 @@ mod tests { x = 43 end ", - ) - .unwrap(); + )?; let mut symbol_table = SymbolTable::new(); - compilation_unit - .gather_declared_names(&mut symbol_table) - .unwrap(); - compilation_unit.check_name_usages(&symbol_table).unwrap(); - match compilation_unit.type_check(&symbol_table) { - Ok(_) => { - panic!("Type check missed diagnostic."); - } - Err(diagnostics) => { - assert_eq!(diagnostics.len(), 1); - assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_LHS_IMMUTABLE); - } - } + compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?; + let mut types_table = TypesTable::new(); + let diagnostics = compilation_unit + .type_check(&symbol_table, &mut types_table) + .unwrap_err(); + assert_eq!(diagnostics.len(), 1); + assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_LHS_IMMUTABLE); + Ok(()) } } diff --git a/dmc-lib/src/ast/binary_expression.rs b/dmc-lib/src/ast/binary_expression.rs index 9619f02..cbad6b8 100644 --- a/dmc-lib/src/ast/binary_expression.rs +++ b/dmc-lib/src/ast/binary_expression.rs @@ -9,8 +9,10 @@ use crate::ir::ir_operation::IrOperation; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; use crate::{diagnostics_result, handle_diagnostic, handle_diagnostics, maybe_return_diagnostics}; use std::cell::RefCell; use std::rc::Rc; @@ -72,45 +74,79 @@ impl BinaryExpression { self.type_info.as_ref().unwrap() } - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result<(), Vec> { - let diagnostics = [&mut self.lhs, &mut self.rhs] - .iter_mut() - .map(|expression| expression.gather_declared_names(symbol_table)) - .filter_map(|result| result.err()) - .flatten() - .collect::>(); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.lhs.init_scopes(symbol_table, container_scope); + self.rhs.init_scopes(symbol_table, container_scope); } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let diagnostics: Vec = [&mut self.lhs, &mut self.rhs] - .iter_mut() - .map(|expression| expression.check_name_usages(symbol_table)) - .filter_map(Result::err) - .flatten() - .collect(); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + pub fn check_field_initializer_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + [ + self.lhs + .check_field_initializer_names(symbol_table, class_symbol), + self.rhs + .check_field_initializer_names(symbol_table, class_symbol), + ] + .into_iter() + .flatten() + .collect() + } + + pub fn check_constructor_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + [ + self.lhs + .check_constructor_local_names(symbol_table, class_symbol), + self.rhs + .check_constructor_local_names(symbol_table, class_symbol), + ] + .into_iter() + .flatten() + .collect() + } + + pub fn check_method_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + [ + self.lhs + .check_method_local_names(symbol_table, class_symbol), + self.rhs + .check_method_local_names(symbol_table, class_symbol), + ] + .into_iter() + .flatten() + .collect() + } + + pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec { + [ + self.lhs.check_static_fn_local_names(symbol_table), + self.rhs.check_static_fn_local_names(symbol_table), + ] + .into_iter() + .flatten() + .collect() } fn check_op( &mut self, + symbol_table: &SymbolTable, + types_table: &TypesTable, check: impl Fn(&TypeInfo, &TypeInfo) -> bool, op_result: impl Fn(&TypeInfo, &TypeInfo) -> TypeInfo, lazy_diagnostic_message: impl Fn(&TypeInfo, &TypeInfo) -> String, ) -> Result<(), Diagnostic> { - let lhs_type_info = self.lhs.type_info(); - let rhs_type_info = self.rhs.type_info(); + let lhs_type_info = self.lhs.type_info(symbol_table, types_table); + let rhs_type_info = self.rhs.type_info(symbol_table, types_table); if check(lhs_type_info, rhs_type_info) { self.type_info = Some(op_result(lhs_type_info, rhs_type_info)); @@ -128,11 +164,15 @@ impl BinaryExpression { } } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + pub fn type_check( + &mut self, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; - handle_diagnostics!(self.lhs.type_check(symbol_table), diagnostics); - handle_diagnostics!(self.rhs.type_check(symbol_table), diagnostics); + handle_diagnostics!(self.lhs.type_check(symbol_table, types_table), diagnostics); + handle_diagnostics!(self.rhs.type_check(symbol_table, types_table), diagnostics); maybe_return_diagnostics!(diagnostics); @@ -140,6 +180,8 @@ impl BinaryExpression { BinaryOperation::Multiply => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_multiply(rhs), |lhs, rhs| lhs.multiply_result(rhs), |lhs, rhs| format!( @@ -153,6 +195,8 @@ impl BinaryExpression { BinaryOperation::Divide => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_divide(rhs), |lhs, rhs| lhs.divide_result(rhs), |lhs, rhs| format!("Incompatible types: cannot divide {} by {}", lhs, rhs) @@ -163,6 +207,8 @@ impl BinaryExpression { BinaryOperation::Modulo => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_modulo(rhs), |lhs, rhs| lhs.modulo_result(rhs), |lhs, rhs| format!("Incompatible types: cannot modulo {} by {}", lhs, rhs) @@ -173,6 +219,8 @@ impl BinaryExpression { BinaryOperation::Add => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_add(rhs), |lhs, rhs| lhs.add_result(&rhs), |lhs, rhs| format!("Incompatible types: cannot add {} to {}.", rhs, lhs) @@ -183,6 +231,8 @@ impl BinaryExpression { BinaryOperation::Subtract => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_subtract(rhs), |lhs, rhs| lhs.subtract_result(rhs), |lhs, rhs| format!( @@ -196,6 +246,8 @@ impl BinaryExpression { BinaryOperation::LeftShift => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_left_shift(rhs), |lhs, rhs| lhs.left_shift_result(rhs), |lhs, rhs| format!( @@ -209,6 +261,8 @@ impl BinaryExpression { BinaryOperation::RightShift => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_right_shift(rhs), |lhs, rhs| lhs.right_shift_result(rhs), |lhs, rhs| format!( @@ -222,6 +276,8 @@ impl BinaryExpression { BinaryOperation::BitwiseAnd => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_bitwise_and(rhs), |lhs, rhs| lhs.bitwise_and_result(rhs), |lhs, rhs| format!( @@ -234,6 +290,8 @@ impl BinaryExpression { } BinaryOperation::BitwiseXor => handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_bitwise_xor(rhs), |lhs, rhs| lhs.bitwise_xor_result(rhs), |lhs, rhs| format!("Incompatible types: cannot bitwise xor {} by {}", lhs, rhs) @@ -243,6 +301,8 @@ impl BinaryExpression { BinaryOperation::BitwiseOr => { handle_diagnostic!( self.check_op( + symbol_table, + types_table, |lhs, rhs| lhs.can_bitwise_or(rhs), |lhs, rhs| lhs.bitwise_or_result(rhs), |lhs, rhs| format!( @@ -262,14 +322,15 @@ impl BinaryExpression { &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> IrOperation { let lhs = self .lhs - .to_ir_expression(builder, symbol_table) + .to_ir_expression(builder, symbol_table, types_table) .expect("Attempt to use a non-value expression in binary expression."); let rhs = self .rhs - .to_ir_expression(builder, symbol_table) + .to_ir_expression(builder, symbol_table, types_table) .expect("Attempt to use a non-value expression in binary expression."); let ir_binary_operation = match self.op { BinaryOperation::Multiply => { @@ -304,8 +365,9 @@ impl BinaryExpression { &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> IrExpression { - let ir_operation = self.to_ir_operation(builder, symbol_table); + let ir_operation = self.to_ir_operation(builder, symbol_table, types_table); let t_var = IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index 7324775..d9d1986 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -6,15 +6,16 @@ use crate::ir::ir_call::IrCall; use crate::ir::ir_expression::IrExpression; use crate::source_range::SourceRange; use crate::symbol::callable_symbol::CallableSymbol; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; pub struct Call { callee: Box, arguments: Vec, source_range: SourceRange, - return_type_info: Option, } impl Call { @@ -23,7 +24,6 @@ impl Call { callee: callee.into(), arguments, source_range, - return_type_info: None, } } @@ -35,57 +35,92 @@ impl Call { self.arguments.iter().collect() } - pub fn gather_declared_names( + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.callee.init_scopes(symbol_table, container_scope); + for argument in &mut self.arguments { + argument.init_scopes(symbol_table, container_scope); + } + } + + pub fn check_field_initializer_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + let mut diagnostics: Vec = Vec::new(); + diagnostics.append( + &mut self + .callee + .check_field_initializer_names(symbol_table, class_symbol), + ); + for argument in &self.arguments { + diagnostics + .append(&mut argument.check_field_initializer_names(symbol_table, class_symbol)) + } + diagnostics + } + + pub fn check_constructor_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + let mut diagnostics = Vec::new(); + for argument in &self.arguments { + diagnostics + .append(&mut argument.check_constructor_local_names(symbol_table, class_symbol)) + } + diagnostics.append( + &mut self + .callee + .check_constructor_local_names(symbol_table, class_symbol), + ); + diagnostics + } + + pub fn check_method_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + let mut diagnostics = Vec::new(); + for argument in &self.arguments { + diagnostics.append(&mut argument.check_method_local_names(symbol_table, class_symbol)); + } + diagnostics.append( + &mut self + .callee + .check_method_local_names(symbol_table, class_symbol), + ); + diagnostics + } + + pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec { + let mut diagnostics: Vec = Vec::new(); + for argument in &self.arguments { + diagnostics.append(&mut argument.check_static_fn_local_names(symbol_table)); + } + diagnostics.append(&mut self.callee.check_static_fn_local_names(symbol_table)); + diagnostics + } + + pub fn type_check( &mut self, - symbol_table: &mut SymbolTable, + symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> Result<(), Vec> { - let mut to_gather = vec![]; - to_gather.push(self.callee.as_mut()); - to_gather.extend(&mut self.arguments); - - let diagnostics: Vec = to_gather - .iter_mut() - .map(|expression| expression.gather_declared_names(symbol_table)) - .filter_map(Result::err) - .flatten() - .collect(); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } - } - - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let mut to_check = vec![]; - to_check.push(self.callee.as_mut()); - to_check.extend(&mut self.arguments); - let diagnostics: Vec = to_check - .iter_mut() - .map(|expression| expression.check_name_usages(symbol_table)) - .filter_map(Result::err) - .flatten() - .collect(); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } - } - - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.callee.as_mut().type_check(symbol_table)?; + self.callee.as_mut().type_check(symbol_table, types_table)?; let mut diagnostics: Vec = self .arguments .iter_mut() - .map(|argument| argument.type_check(symbol_table)) + .map(|argument| argument.type_check(symbol_table, types_table)) .filter_map(Result::err) .flatten() .collect(); // check that callee is callable - let callable_symbol = match self.callee.type_info() { + let callable_symbol = match self.callee.type_info(symbol_table, types_table) { TypeInfo::Function(function_symbol) => { CallableSymbol::Function(function_symbol.clone()) } @@ -94,7 +129,7 @@ impl Call { diagnostics.push(Diagnostic::new( &format!( "Receiver of type {} is not callable.", - self.callee.type_info() + self.callee.type_info(symbol_table, types_table) ), self.callee.source_range().start(), self.callee.source_range().end(), @@ -103,9 +138,6 @@ impl Call { } }; - // set return type - self.return_type_info = Some(callable_symbol.return_type_info()); - // check arguments length let parameters = callable_symbol.parameters(); if parameters.len() != self.arguments.len() { @@ -126,10 +158,10 @@ impl Call { // check argument types for i in 0..parameters.len() { - let parameter = ¶meters[i].borrow(); + let parameter = ¶meters[i]; let argument = &self.arguments[i]; - let parameter_type_info = parameter.type_info(); - let argument_type_info = argument.type_info(); + let parameter_type_info = types_table.parameter_types().get(parameter).unwrap(); + let argument_type_info = argument.type_info(symbol_table, types_table); if !parameter_type_info.is_assignable_from(argument_type_info) { diagnostics.push(Diagnostic::new( &format!( @@ -149,14 +181,30 @@ impl Call { } } - pub fn return_type_info(&self) -> &TypeInfo { - self.return_type_info.as_ref().unwrap() + pub fn return_type_info<'a>( + &self, + symbol_table: &SymbolTable, + types_table: &'a TypesTable, + ) -> &'a TypeInfo { + let callable_symbol = self.get_callee_symbol(symbol_table); + match callable_symbol { + CallableSymbol::Function(function_symbol) => types_table + .function_return_types() + .get(&function_symbol) + .unwrap(), + CallableSymbol::Class(class_symbol) => types_table + .class_instance_types() + .get(&class_symbol) + .unwrap(), + } } - fn get_callee_symbol(&self) -> CallableSymbol { + fn get_callee_symbol(&self, symbol_table: &SymbolTable) -> CallableSymbol { match self.callee() { Expression::Identifier(identifier) => { - let expressible_symbol = identifier.expressible_symbol(); + let expressible_symbol = symbol_table + .find_expressible_symbol(identifier.scope_id(), identifier.name()) + .unwrap(); match expressible_symbol { ExpressibleSymbol::Function(function_symbol) => { CallableSymbol::Function(function_symbol.clone()) @@ -171,11 +219,16 @@ impl Call { } } - pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrCall { + pub fn to_ir( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) -> IrCall { let arguments: Vec = self .arguments .iter() - .map(|argument| argument.to_ir_expression(builder, symbol_table)) + .map(|argument| argument.to_ir_expression(builder, symbol_table, types_table)) .inspect(|expression| { if expression.is_none() { panic!("Attempt to pass non-expression") @@ -183,21 +236,17 @@ impl Call { }) .map(Option::unwrap) .collect(); - let callable_symbol = self.get_callee_symbol(); + let callable_symbol = self.get_callee_symbol(symbol_table); match callable_symbol { CallableSymbol::Function(function_symbol) => IrCall::new( - fqn_parts_to_string(function_symbol.borrow().fqn_parts()), + fqn_parts_to_string(function_symbol.fqn_parts()), arguments, - function_symbol.borrow().is_extern(), + function_symbol.is_extern(), ), CallableSymbol::Class(class_symbol) => { - let constructor_symbol = class_symbol - .borrow() - .constructor_symbol() - .cloned() - .expect("Default constructors not supported yet."); + let constructor_symbol = class_symbol.constructor_symbol(); IrCall::new( - fqn_parts_to_string(constructor_symbol.borrow().fqn_parts()), + fqn_parts_to_string(constructor_symbol.fqn_parts()), arguments, false, ) diff --git a/dmc-lib/src/ast/class.rs b/dmc-lib/src/ast/class.rs index 1a5b846..12b4a87 100644 --- a/dmc-lib/src/ast/class.rs +++ b/dmc-lib/src/ast/class.rs @@ -6,24 +6,21 @@ use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::function::Function; use crate::ast::generic_parameter::GenericParameter; -use crate::ast::helpers::{ - collect_diagnostics_mut, collect_diagnostics_single, map_symbol_insert_result, - resolve_ctor_name, -}; +use crate::ast::helpers::{collect_diagnostics_mut, collect_diagnostics_single, resolve_ctor_name}; use crate::ast::statement::Statement; use crate::diagnostic::Diagnostic; use crate::error_codes::{FIELD_MULTIPLE_INIT, FIELD_UNINIT}; use crate::ir::ir_class::{IrClass, IrField}; use crate::ir::ir_function::IrFunction; +use crate::ok_or_err_diagnostics; use crate::source_range::SourceRange; use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol; -use crate::symbol::field_symbol::FieldSymbol; -use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; +use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol_table::SymbolTable; -use crate::{ok_or_err_diagnostics, push_ok_or_push_errs}; -use std::cell::{Ref, RefCell, RefMut}; +use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; use std::collections::{HashMap, HashSet}; use std::rc::Rc; @@ -34,7 +31,7 @@ pub struct Class { constructor: Option, fields: Vec, functions: Vec, - class_symbol: Option>>, + scope_id: Option, } impl Class { @@ -53,258 +50,146 @@ impl Class { constructor, fields, functions, - class_symbol: None, + scope_id: None, } } - /// Returns a [ClassSymbol] representing this class. - fn make_class_symbol(&self, fqn_context: &FqnContext) -> ClassSymbol { - ClassSymbol::new( - &self.declared_name, - self.declared_name_source_range.clone(), - fqn_context.resolve(&self.declared_name), - false, - ) - } + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); - /// Inserts the given [ClassSymbol] into the `symbol_table`, returning `Ok` if the insert was - /// successful, else `Err` with diagnostics. - fn insert_class_symbol( - &mut self, - symbol_table: &mut SymbolTable, - class_symbol: ClassSymbol, - ) -> Result>, Vec> { - map_symbol_insert_result( - symbol_table.insert_class_symbol(class_symbol), - &self.declared_name_source_range, - ) - } - - /// Sets this class' class symbol to the given symbol. - fn set_class_symbol(&mut self, symbol: Rc>) { - self.class_symbol = Some(symbol); - } - - /// Gathers names of this class' generic parameters, returning `Ok` if all - /// [GenericParameter]s produced an `Ok(GenericParameterSymbol)`, else `Err`. - fn gather_generic_parameters_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result>>, Vec> { - let mut symbols: Vec>> = vec![]; - let mut diagnostics: Vec = vec![]; + let class_scope = + symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name)); for generic_parameter in &mut self.generic_parameters { - push_ok_or_push_errs!( - generic_parameter.gather_declared_names(symbol_table), - symbols, - diagnostics - ); + generic_parameter.init_scopes(symbol_table, class_scope); } - ok_or_err_diagnostics!(symbols, diagnostics) - } + let class_body_scope = + symbol_table.push_class_scope(&format!("class_body_scope({})", self.declared_name)); - /// Returns a [Ref] to this class' [ClassSymbol]. Only safe to call if the class symbol has been - /// set. - fn use_class_symbol(&self) -> Ref { - self.class_symbol.as_ref().unwrap().borrow() - } - - /// Returns a [RefMut] to this class' [ClassSymbol]. Only safe to call if the class symbol has - /// been set. - fn use_class_symbol_mut(&mut self) -> RefMut { - self.class_symbol.as_mut().unwrap().borrow_mut() - } - - /// Sets the given [GenericParameterSymbol]s on this class' [ClassSymbol]. - fn set_generic_parameters_on_class_symbol( - &mut self, - generic_parameter_symbols: Vec>>, - ) { - self.use_class_symbol_mut() - .set_generic_parameters(generic_parameter_symbols); - } - - /// Returns `Ok` of [FieldSymbol]s if all of this class' fields [Field::gather_declared_names] - /// methods return `Ok`, else `Err` of [Diagnostic]s. - fn gather_fields_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result>>, Vec> { - let mut symbols: Vec>> = vec![]; - let mut diagnostics: Vec = vec![]; - - for (field_index, field) in self.fields.iter_mut().enumerate() { - push_ok_or_push_errs!( - field.gather_declared_names(symbol_table, field_index), - symbols, - diagnostics - ); + for field in &mut self.fields { + field.init_scopes(symbol_table, class_body_scope); } - ok_or_err_diagnostics!(symbols, diagnostics) - } - - /// Converts the given `field_symbols` [Vec] into a [HashMap] of field names to [FieldSymbol]s. - fn make_fields_hash_map( - field_symbols: Vec>>, - ) -> HashMap, Rc>> { - HashMap::from_iter( - field_symbols - .into_iter() - .map(|field| { - let name = field.borrow().declared_name_owned(); - (name, field) - }) - .collect::>(), - ) - } - - /// Sets the field symbols property on the class' [ClassSymbol]. - fn set_field_symbols_on_class_symbol(&mut self, field_symbols: Vec>>) { - self.use_class_symbol_mut() - .set_fields(Self::make_fields_hash_map(field_symbols)) - } - - /// Returns a [ConstructorSymbol] representing a default constructor appropriate for this class. - fn make_default_constructor(&self, fqn_context: &FqnContext) -> ConstructorSymbol { - ConstructorSymbol::new( - self.declared_name_source_range.clone(), - resolve_ctor_name(fqn_context), - false, - true, - ) - } - - /// Gathers names in the class' [Constructor] and returns a [ConstructorSymbol], if the - /// constructor is present; else returns a symbol defining a default constructor. - fn gather_constructor_names( - &mut self, - symbol_table: &mut SymbolTable, - fqn_context: &FqnContext, - ) -> Result>, Vec> { if let Some(constructor) = &mut self.constructor { - constructor.gather_declared_names(symbol_table, fqn_context) - } else { - Ok(Rc::new(RefCell::new( - self.make_default_constructor(fqn_context), - ))) + constructor.init_scopes(symbol_table, class_body_scope); + } + for function in &mut self.functions { + function.init_scopes(symbol_table, class_body_scope); } - } - /// Sets the constructor symbol on this class' [ClassSymbol]. - fn set_constructor_symbol_on_class_symbol( - &mut self, - constructor_symbol: Rc>, - ) { - self.use_class_symbol_mut() - .set_constructor_symbol(constructor_symbol); - } - - /// Gathers function names and returns `Ok` if no diagnostics, else `Err`. - fn gather_functions_names( - &mut self, - symbol_table: &mut SymbolTable, - fqn_context: &FqnContext, - ) -> Result<(), Vec> { - collect_diagnostics_mut(&mut self.functions, |f| { - f.gather_declared_names(symbol_table, fqn_context, self.class_symbol.as_ref()) - }) - } - - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - fqn_context: &mut FqnContext, - ) -> Result<(), Vec> { - // 1. Make and insert class symbol - let to_insert = self.make_class_symbol(fqn_context); - let class_symbol = self.insert_class_symbol(symbol_table, to_insert)?; - self.set_class_symbol(class_symbol); - - // 2. Push class name on fqn context - fqn_context.push(self.declared_name.clone()); - - // 3. push scope - symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name)); - - // 4. gather generic parameters - let generic_parameter_symbols = self - .gather_generic_parameters_names(symbol_table) - .inspect_err(|_| { - symbol_table.pop_scope(); - fqn_context.pop(); - })?; - self.set_generic_parameters_on_class_symbol(generic_parameter_symbols); - - // 5. gather fields - let field_symbols = self.gather_fields_names(symbol_table).inspect_err(|_| { - symbol_table.pop_scope(); - fqn_context.pop(); - })?; - self.set_field_symbols_on_class_symbol(field_symbols); - - // 6. gather constructor - let constructor_symbol = self - .gather_constructor_names(symbol_table, fqn_context) - .inspect_err(|_| { - symbol_table.pop_scope(); - fqn_context.pop(); - })?; - self.set_constructor_symbol_on_class_symbol(constructor_symbol); - - // 7. gather functions - self.gather_functions_names(symbol_table, fqn_context) - .inspect_err(|_| { - symbol_table.pop_scope(); - fqn_context.pop(); - })?; - - // 8. pop scope symbol_table.pop_scope(); - - // 9. pop fqn part - fqn_context.pop(); - - Ok(()) + symbol_table.pop_scope(); } - fn check_generics_names(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - collect_diagnostics_mut(&mut self.generic_parameters, |gp| { - gp.check_name_usages(symbol_table, self.class_symbol.as_ref()) - }) - } + pub fn make_symbols(&self, fqn_context: &mut FqnContext) -> Vec { + let mut all_symbols: Vec = Vec::new(); - /// Checks the [Constructor]'s name usages if present. - fn check_constructor_names( - &mut self, - symbol_table: &SymbolTable, - ) -> Result<(), Vec> { - if let Some(constructor) = &mut self.constructor { - constructor.check_name_usages(symbol_table, self.class_symbol.as_ref())? + let mut generic_parameter_symbols = Vec::new(); + for generic_parameter in &self.generic_parameters { + let symbol = Rc::new(generic_parameter.make_symbol()); + all_symbols.push(Symbol::GenericParameter(symbol.clone())); + generic_parameter_symbols.push(symbol); } - Ok(()) + + let mut field_symbols = Vec::new(); + for field in &self.fields { + let symbol = Rc::new(field.make_symbol()); + all_symbols.push(Symbol::Field(symbol.clone())); + field_symbols.push(symbol); + } + + fqn_context.push(self.declared_name.clone()); // namespace this class' members + + let constructor_symbol = if let Some(constructor) = &self.constructor { + let (constructor_symbol, mut symbols) = constructor.make_symbols(fqn_context); + all_symbols.append(&mut symbols); + constructor_symbol + } else { + Rc::new(ConstructorSymbol::new( + &self.declared_name_source_range, + resolve_ctor_name(fqn_context), + false, + true, + self.scope_id.unwrap(), + vec![], + )) + }; + all_symbols.push(Symbol::Constructor(constructor_symbol.clone())); + + let mut function_symbols = Vec::new(); + for function in &self.functions { + let (function_symbol, mut symbols) = function.make_symbols(fqn_context, true); + all_symbols.append(&mut symbols); + function_symbols.push(function_symbol); + } + + fqn_context.pop(); // un-namespace + + let class_symbol = Rc::new(ClassSymbol::new( + &self.declared_name, + &self.declared_name_source_range, + fqn_context.resolve(&self.declared_name), + false, + self.scope_id.unwrap(), + generic_parameter_symbols, + constructor_symbol, + field_symbols, + function_symbols, + )); + all_symbols.push(Symbol::Class(class_symbol.clone())); + + all_symbols } - fn check_fields_names(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - collect_diagnostics_mut(&mut self.fields, |f| { - f.check_name_usages(symbol_table, self.class_symbol.as_ref()) - }) + pub fn check_names(&self, symbol_table: &mut SymbolTable) -> Vec { + let mut diagnostics: Vec = Vec::new(); + + for generic_parameter in &self.generic_parameters { + diagnostics.append(&mut generic_parameter.check_names(symbol_table)); + } + + for field in &self.fields { + diagnostics.append(&mut field.check_names(symbol_table)); + } + + if let Some(constructor) = &self.constructor { + diagnostics.append(&mut constructor.check_names(symbol_table)); + } + + for function in &self.functions { + diagnostics.append(&mut function.check_names(symbol_table)); + } + + diagnostics } - fn check_functions_names(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - collect_diagnostics_mut(&mut self.functions, |f| { - f.check_name_usages(symbol_table, self.class_symbol.as_ref()) - }) + pub fn check_field_initializer_names(&self, symbol_table: &SymbolTable) -> Vec { + let class_symbol = symbol_table + .get_class_symbol(self.scope_id.unwrap(), &self.declared_name) + .cloned() + .unwrap(); + self.fields + .iter() + .flat_map(|field| field.check_field_initializer_names(symbol_table, &class_symbol)) + .collect() } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.check_generics_names(symbol_table)?; - self.check_constructor_names(symbol_table)?; - self.check_fields_names(symbol_table)?; - self.check_functions_names(symbol_table)?; - Ok(()) + pub fn analyze_local_names(&self, symbol_table: &mut SymbolTable) -> Vec { + let class_symbol = symbol_table + .get_class_symbol(self.scope_id.unwrap(), &self.declared_name) + .cloned() + .unwrap(); + + let mut diagnostics: Vec = Vec::new(); + if let Some(constructor) = &self.constructor { + diagnostics.append(&mut constructor.analyze_local_names(symbol_table, &class_symbol)); + } + for function in &self.functions { + diagnostics + .append(&mut function.analyze_method_local_names(symbol_table, &class_symbol)); + } + diagnostics } fn type_check_generics(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { @@ -313,22 +198,35 @@ impl Class { }) } - fn type_check_fields(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - collect_diagnostics_mut(&mut self.fields, |f| f.type_check(symbol_table)) + fn type_check_fields( + &mut self, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) -> Result<(), Vec> { + collect_diagnostics_mut(&mut self.fields, |f| { + f.type_check(symbol_table, types_table) + }) } fn type_check_constructor( &mut self, symbol_table: &SymbolTable, + types_table: &mut TypesTable, ) -> Result<(), Vec> { if let Some(constructor) = &mut self.constructor { - constructor.type_check(symbol_table)?; + constructor.type_check(symbol_table, types_table)?; } Ok(()) } - fn type_check_functions(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - collect_diagnostics_mut(&mut self.functions, |f| f.type_check(symbol_table)) + fn type_check_functions( + &mut self, + symbol_table: &SymbolTable, + types_table: &mut TypesTable, + ) -> Result<(), Vec> { + collect_diagnostics_mut(&mut self.functions, |f| { + f.type_check(symbol_table, types_table) + }) } /// Returns all field names with declared initializers. @@ -350,16 +248,14 @@ impl Class { &self, assign_statement: &'a AssignStatement, fields_already_init: &HashSet<&&str>, + class_symbol: &ClassSymbol, ) -> Result, Diagnostic> { match assign_statement.destination() { Expression::Identifier(identifier) => { // find matching field symbol, if there is one - if let Some(field_symbol) = self.use_class_symbol().fields().get(identifier.name()) - { + if let Some(field_symbol) = class_symbol.fields().get(identifier.name()) { // check that we don't init more than once IF field is immutable - if fields_already_init.contains(&identifier.name()) - && !field_symbol.borrow().is_mut() - { + if fields_already_init.contains(&identifier.name()) && !field_symbol.is_mut() { let diagnostic = Diagnostic::new( &format!("Immutable field {} cannot be initialized more than once in constructor.", identifier.name()), identifier.source_range().start(), @@ -388,19 +284,25 @@ impl Class { fn get_fields_init_in_ctor<'a>( &self, fields_with_declared_initializers: &HashSet<&str>, + symbol_table: &SymbolTable, ) -> Result, Vec> { let mut constructor_inits: HashSet<&str> = HashSet::new(); let mut diagnostics: Vec = vec![]; if let Some(constructor) = &self.constructor { + let class_symbol = symbol_table + .get_class_symbol(self.scope_id.unwrap(), &self.declared_name) + .unwrap(); for statement in constructor.statements() { match statement { Statement::Assign(assign_statement) => { let fields_init_so_far = constructor_inits .union(fields_with_declared_initializers) .collect::>(); - match self - .check_ctor_assign_statement(assign_statement, &fields_init_so_far) - { + match self.check_ctor_assign_statement( + assign_statement, + &fields_init_so_far, + &class_symbol, + ) { Ok(maybe_init_field) => match maybe_init_field { None => {} Some(init_field) => { @@ -445,14 +347,17 @@ impl Class { /// Immutable fields may be only initialized once, either at their declaration or once in the /// constructor. Mutable fields may be initialized either at their declaration, or at least once /// in the constructor. - fn check_field_initialization(&self) -> Result<(), Vec> { + fn check_field_initialization( + &self, + symbol_table: &SymbolTable, + ) -> Result<(), Vec> { // We need to determine if fields are initialized or not (the latter is an error). // First phase: check all fields, then check constructor, leaving pending those things that // are fields <- initialized by constructor. Then circle back to fields and check all are // initialized let field_names_with_initializers = self.field_names_with_initializers(); let field_names_init_in_constructor = - self.get_fields_init_in_ctor(&field_names_with_initializers)?; + self.get_fields_init_in_ctor(&field_names_with_initializers, symbol_table)?; let combined = field_names_with_initializers .union(&field_names_init_in_constructor) .collect::>(); @@ -463,35 +368,45 @@ impl Class { Ok(()) } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + pub fn type_check( + &mut self, + symbol_table: &SymbolTable, + types_table: &mut TypesTable, + ) -> Result<(), Vec> { self.type_check_generics(symbol_table)?; - self.type_check_fields(symbol_table)?; - self.type_check_constructor(symbol_table)?; - self.type_check_functions(symbol_table)?; - self.check_field_initialization()?; + self.type_check_fields(symbol_table, types_table)?; + self.type_check_constructor(symbol_table, types_table)?; + self.type_check_functions(symbol_table, types_table)?; + self.check_field_initialization(symbol_table)?; Ok(()) } - pub fn to_ir(&self, symbol_table: &SymbolTable) -> (IrClass, Vec) { + pub fn to_ir( + &self, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) -> (IrClass, Vec) { + let self_class_symbol = symbol_table + .get_class_symbol(self.scope_id.unwrap(), &self.declared_name) + .unwrap(); + let mut ir_functions: Vec = vec![]; if let Some(constructor) = &self.constructor { ir_functions.push(constructor.to_ir( - self.class_symbol.as_ref().unwrap(), + self_class_symbol, &self.fields, symbol_table, + types_table, )) } for function in &self.functions { - ir_functions - .push(function.to_ir(symbol_table, Some(self.class_symbol.as_ref().unwrap()))); + ir_functions.push(function.to_ir(symbol_table, types_table, Some(self_class_symbol))); } - let class_symbol = self.class_symbol.as_ref().unwrap().borrow(); - let ir_class = IrClass::new( - class_symbol.declared_name_owned(), - fqn_parts_to_string(class_symbol.fqn_parts()).into(), + self_class_symbol.declared_name_owned(), + fqn_parts_to_string(self_class_symbol.fqn_parts()).into(), self.fields .iter() .map(|field| { diff --git a/dmc-lib/src/ast/compilation_unit.rs b/dmc-lib/src/ast/compilation_unit.rs index bcef248..a3f481c 100644 --- a/dmc-lib/src/ast/compilation_unit.rs +++ b/dmc-lib/src/ast/compilation_unit.rs @@ -2,12 +2,13 @@ use crate::ast::class::Class; use crate::ast::extern_function::ExternFunction; use crate::ast::fqn_context::FqnContext; use crate::ast::function::Function; -use crate::ast::helpers::collect_diagnostics_into_mut; +use crate::ast::helpers::{collect_diagnostics_into_mut, try_insert_symbols_into}; use crate::diagnostic::Diagnostic; -use crate::diagnostics_result; use crate::ir::ir_class::IrClass; use crate::ir::ir_function::IrFunction; use crate::symbol_table::SymbolTable; +use crate::types_table::TypesTable; +use crate::{diagnostics_result, handle_diagnostics}; pub struct CompilationUnit { functions: Vec, @@ -40,68 +41,70 @@ impl CompilationUnit { &self.classes } - pub fn gather_declared_names( - &mut self, + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable) { + let compilation_unit_scope = symbol_table.push_module_scope("compilation_unit_scope"); + for class in &mut self.classes { + class.init_scopes(symbol_table, compilation_unit_scope); + } + for function in &mut self.functions { + function.init_scopes(symbol_table, compilation_unit_scope); + } + for extern_function in &mut self.extern_functions { + extern_function.init_scopes(symbol_table, compilation_unit_scope); + } + symbol_table.pop_scope(); + } + + pub fn gather_symbols_into( + &self, symbol_table: &mut SymbolTable, ) -> Result<(), Vec> { - symbol_table.push_module_scope("compilation_unit_scope"); - - let mut fqn_context = FqnContext::new(); // in the future, we'll push the pkg/ns on here - let mut diagnostics: Vec = vec![]; - - collect_diagnostics_into_mut( - &mut self.functions, - |f| f.gather_declared_names(symbol_table, &fqn_context, None), - &mut diagnostics, - ); - - collect_diagnostics_into_mut( - &mut self.extern_functions, - |ef| ef.gather_declared_names(symbol_table, &fqn_context), - &mut diagnostics, - ); - - collect_diagnostics_into_mut( - &mut self.classes, - |c| c.gather_declared_names(symbol_table, &mut fqn_context), - &mut diagnostics, - ); - - symbol_table.pop_scope(); - + let mut diagnostics = vec![]; + let mut fqn_context = FqnContext::new(); + for class in &self.classes { + handle_diagnostics!( + try_insert_symbols_into(class.make_symbols(&mut fqn_context), symbol_table), + diagnostics + ); + } + for function in &self.functions { + let (_, symbols) = function.make_symbols(&mut fqn_context, false); + handle_diagnostics!(try_insert_symbols_into(symbols, symbol_table), diagnostics); + } + for extern_function in &self.extern_functions { + let (_, symbols) = extern_function.make_symbols(&mut fqn_context); + handle_diagnostics!(try_insert_symbols_into(symbols, symbol_table), diagnostics); + } diagnostics_result!(diagnostics) } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let mut diagnostics: Vec = vec![]; - - collect_diagnostics_into_mut( - &mut self.functions, - |f| f.check_name_usages(symbol_table, None), - &mut diagnostics, - ); - - collect_diagnostics_into_mut( - &mut self.extern_functions, - |ef| ef.check_name_usages(symbol_table, None), - &mut diagnostics, - ); - - collect_diagnostics_into_mut( - &mut self.classes, - |c| c.check_name_usages(symbol_table), - &mut diagnostics, - ); - + pub fn check_names(&self, symbol_table: &mut SymbolTable) -> Result<(), Vec> { + let mut diagnostics = vec![]; + for class in &self.classes { + diagnostics.append(&mut class.check_names(symbol_table)); + diagnostics.append(&mut class.check_field_initializer_names(symbol_table)); + diagnostics.append(&mut class.analyze_local_names(symbol_table)); + } + for function in &self.functions { + diagnostics.append(&mut function.check_names(symbol_table)); + diagnostics.append(&mut function.analyze_static_fn_local_names(symbol_table)); + } + for extern_function in &self.extern_functions { + diagnostics.append(&mut extern_function.check_names(symbol_table)); + } diagnostics_result!(diagnostics) } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + pub fn type_check( + &mut self, + symbol_table: &SymbolTable, + types_table: &mut TypesTable, + ) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; collect_diagnostics_into_mut( &mut self.functions, - |f| f.type_check(symbol_table), + |f| f.type_check(symbol_table, types_table), &mut diagnostics, ); @@ -113,24 +116,28 @@ impl CompilationUnit { collect_diagnostics_into_mut( &mut self.classes, - |c| c.type_check(symbol_table), + |c| c.type_check(symbol_table, types_table), &mut diagnostics, ); diagnostics_result!(diagnostics) } - pub fn to_ir(&self, symbol_table: &SymbolTable) -> (Vec, Vec) { + pub fn to_ir( + &self, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) -> (Vec, Vec) { let mut functions: Vec = vec![]; let mut classes: Vec = vec![]; self.functions .iter() - .map(|f| f.to_ir(symbol_table, None)) + .map(|f| f.to_ir(symbol_table, types_table, None)) .for_each(|f| functions.push(f)); for class in &self.classes { - let (class, mut class_functions) = class.to_ir(symbol_table); + let (class, mut class_functions) = class.to_ir(symbol_table, types_table); functions.append(&mut class_functions); classes.push(class); } diff --git a/dmc-lib/src/ast/constructor.rs b/dmc-lib/src/ast/constructor.rs index 1ef789f..4e28307 100644 --- a/dmc-lib/src/ast/constructor.rs +++ b/dmc-lib/src/ast/constructor.rs @@ -1,7 +1,7 @@ use crate::ast::field::Field; use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_util::fqn_parts_to_string; -use crate::ast::helpers::resolve_ctor_name; +use crate::ast::helpers::{collect_parameter_symbols_into, resolve_ctor_name}; use crate::ast::ir_builder::IrBuilder; use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; @@ -22,9 +22,9 @@ use crate::source_range::SourceRange; use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol; -use crate::symbol::parameter_symbol::ParameterSymbol; -use crate::symbol_table::{SymbolInsertError, SymbolTable}; +use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; use std::cell::RefCell; use std::ops::Neg; use std::rc::Rc; @@ -34,6 +34,7 @@ pub struct Constructor { ctor_keyword_source_range: SourceRange, parameters: Vec, statements: Vec, + scope_id: Option, } impl Constructor { @@ -48,6 +49,7 @@ impl Constructor { ctor_keyword_source_range, parameters, statements, + scope_id: None, } } @@ -55,112 +57,67 @@ impl Constructor { &self.statements } - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - fqn_context: &FqnContext, - ) -> Result>, Vec> { - // insert constructor symbol - let to_insert = ConstructorSymbol::new( - self.ctor_keyword_source_range.clone(), + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); + + let function_scope = symbol_table.push_function_scope("constructor_scope"); + + for parameter in &mut self.parameters { + parameter.init_scopes(symbol_table, function_scope); + } + + let body_scope = symbol_table.push_block_scope("body_scope"); + for statement in &mut self.statements { + statement.init_scopes(symbol_table, body_scope); + } + + symbol_table.pop_scope(); + symbol_table.pop_scope(); + } + + pub fn make_symbols(&self, fqn_context: &FqnContext) -> (Rc, Vec) { + let mut all_symbols: Vec = Vec::new(); + + let mut parameter_symbols = Vec::new(); + collect_parameter_symbols_into(&self.parameters, &mut all_symbols, &mut parameter_symbols); + + let constructor_symbol = Rc::new(ConstructorSymbol::new( + &self.ctor_keyword_source_range, resolve_ctor_name(fqn_context), false, false, - ); - let constructor_symbol = - symbol_table - .insert_constructor_symbol(to_insert) - .map_err(|symbol_insert_error| match symbol_insert_error { - SymbolInsertError::AlreadyDeclared(_) => { - vec![ - Diagnostic::new( - "Cannot declare more than one constructor.", - self.ctor_keyword_source_range.start(), - self.ctor_keyword_source_range.end(), - ) - .with_reporter(file!(), line!()), - ] - } - })?; + self.scope_id.unwrap(), + parameter_symbols, + )); + all_symbols.push(Symbol::Constructor(constructor_symbol.clone())); - symbol_table.push_function_scope("ctor_scope"); - - let mut parameter_symbols: Vec>> = vec![]; - let mut parameters_diagnostics = vec![]; - - for parameter in &mut self.parameters { - match parameter.gather_declared_names(symbol_table) { - Ok(parameter_symbol) => { - parameter_symbols.push(parameter_symbol); - } - Err(mut ds) => { - parameters_diagnostics.append(&mut ds); - } - } - } - - if !parameters_diagnostics.is_empty() { - symbol_table.pop_scope(); - return Err(parameters_diagnostics); - } else { - constructor_symbol - .borrow_mut() - .set_parameters(parameter_symbols); - } - - symbol_table.push_block_scope("ctor_main_block"); - - let statements_diagnostics = self - .statements - .iter_mut() - .map(|stmt| stmt.gather_declared_names(symbol_table)) - .filter_map(Result::err) - .flatten() - .collect::>(); - - symbol_table.pop_scope(); // block - symbol_table.pop_scope(); // function - - if statements_diagnostics.is_empty() { - Ok(constructor_symbol) - } else { - Err(statements_diagnostics) - } + (constructor_symbol, all_symbols) } - pub fn check_name_usages( + pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { + let mut diagnostics: Vec = Vec::new(); + for parameter in &self.parameters { + diagnostics.append(&mut parameter.check_names(symbol_table)); + } + diagnostics + } + + pub fn analyze_local_names( + &self, + symbol_table: &mut SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + self.statements + .iter() + .flat_map(|s| s.analyze_constructor_local_names(symbol_table, class_symbol)) + .collect() + } + + pub fn type_check( &mut self, symbol_table: &SymbolTable, - class_context: Option<&Rc>>, + types_table: &mut TypesTable, ) -> Result<(), Vec> { - let parameters_diagnostics: Vec = self - .parameters - .iter_mut() - .map(|param| param.check_name_usages(symbol_table, class_context)) - .filter_map(Result::err) - .flatten() - .collect(); - - if !parameters_diagnostics.is_empty() { - return Err(parameters_diagnostics); - } - - let statements_diagnostics: Vec = self - .statements - .iter_mut() - .map(|statement| statement.check_name_usages(symbol_table)) - .filter_map(Result::err) - .flatten() - .collect(); - - if statements_diagnostics.is_empty() { - Ok(()) - } else { - Err(statements_diagnostics) - } - } - - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let parameters_diagnostics: Vec = self .parameters .iter_mut() @@ -176,7 +133,7 @@ impl Constructor { let statements_diagnostics: Vec = self .statements .iter_mut() - .map(|statement| statement.type_check(symbol_table, None)) + .map(|statement| statement.type_check(symbol_table, types_table, None)) .filter_map(Result::err) .flatten() .collect(); @@ -190,9 +147,10 @@ impl Constructor { pub fn to_ir( &self, - class_symbol: &Rc>, + class_symbol: &Rc, fields: &[Field], symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> IrFunction { let mut ir_builder = IrBuilder::new(); @@ -202,17 +160,22 @@ impl Constructor { .iter() .enumerate() .map(|(i, parameter)| { - let mut parameter_symbol = parameter.parameter_symbol().borrow_mut(); + let parameter_symbol = symbol_table + .get_parameter_symbol_owned(parameter.scope_id(), parameter.declared_name()) + .unwrap(); + let parameter_type = types_table + .parameter_types() + .get(¶meter_symbol) + .unwrap(); let offset = (parameters_count as isize).neg() + i as isize; let ir_parameter = Rc::new(IrParameter::new( parameter_symbol.declared_name(), - parameter_symbol.type_info().clone(), + parameter_type.clone(), offset, )); // make sure to save ir_parameter to symbol so others can access it - let to_save = ir_parameter.clone(); - parameter_symbol.set_ir_parameter(to_save); + ir_builder.push_parameter(¶meter_symbol, ir_parameter.clone()); ir_parameter }) @@ -234,7 +197,7 @@ impl Constructor { let alloc_assign = IrAssign::new( self_variable.clone(), - IrOperation::Allocate(IrAllocate::new(class_symbol.borrow().declared_name_owned())), + IrOperation::Allocate(IrAllocate::new(class_symbol.declared_name_owned())), ); ir_builder .current_block_mut() @@ -275,7 +238,7 @@ impl Constructor { .clone(); let ir_expression = initializer - .to_ir_expression(&mut ir_builder, symbol_table) + .to_ir_expression(&mut ir_builder, symbol_table, types_table) .unwrap(); let ir_set_field = IrSetField::new( &field_mut_ref_variable, // dumb that we clone it and then ref it @@ -289,7 +252,7 @@ impl Constructor { // do "declared" statements of constructor for statement in &self.statements { - statement.to_ir(&mut ir_builder, symbol_table, false); + statement.to_ir(&mut ir_builder, symbol_table, types_table, false); } // return complete self object @@ -303,16 +266,12 @@ impl Constructor { ir_builder.finish_block(); let entry_block = ir_builder.get_block(entry_block_id); + let constructor_symbol = symbol_table + .get_constructor_symbol(self.scope_id.unwrap()) + .unwrap(); IrFunction::new( - fqn_parts_to_string( - class_symbol - .borrow() - .constructor_symbol() - .unwrap() - .borrow() - .fqn_parts(), - ), // fake function symbol - &ir_parameters, // make params + fqn_parts_to_string(constructor_symbol.fqn_parts()), + ir_parameters, &TypeInfo::ClassInstance(class_symbol.clone()), entry_block.clone(), ) diff --git a/dmc-lib/src/ast/diagnostic_factories.rs b/dmc-lib/src/ast/diagnostic_factories.rs new file mode 100644 index 0000000..479d952 --- /dev/null +++ b/dmc-lib/src/ast/diagnostic_factories.rs @@ -0,0 +1,88 @@ +use crate::diagnostic::{Diagnostic, SecondaryLabel}; +use crate::error_codes::{ + OUTER_CLASS_FIELD_USED_IN_INIT, OUTER_CLASS_METHOD_USED_IN_INIT, SELF_CONSTRUCTOR_USED_IN_INIT, + SELF_FIELD_USED_IN_INIT, SELF_METHOD_USED_IN_INIT, SYMBOL_ALREADY_DECLARED, SYMBOL_NOT_FOUND, +}; +use crate::source_range::SourceRange; +use crate::symbol::Symbol; + +pub fn symbol_not_found(name: &str, source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + &format!("Symbol {} not found in scope.", name), + source_range.start(), + source_range.end(), + ) + .with_error_code(SYMBOL_NOT_FOUND) +} + +pub fn cannot_reference_field_in_init(name: &str, source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + &format!("Cannot reference field {} during initialization.", name), + source_range.start(), + source_range.end(), + ) + .with_error_code(SELF_FIELD_USED_IN_INIT) +} + +pub fn symbol_already_declared(already_inserted: &Symbol, would_insert: &Symbol) -> Diagnostic { + let secondary_label = SecondaryLabel::new( + already_inserted.declared_name_source_range().start(), + already_inserted.declared_name_source_range().end(), + Some("Symbol already declared here.".to_string()), + ); + Diagnostic::new( + &format!( + "Symbol {} already declared in current scope.", + would_insert.declared_name() + ), + would_insert.declared_name_source_range().start(), + would_insert.declared_name_source_range().end(), + ) + .with_error_code(SYMBOL_ALREADY_DECLARED) + .with_secondary_labels(&[secondary_label]) +} + +pub fn self_constructor_used_in_init(source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + "Cannot call Self constructor during initialization.", + source_range.start(), + source_range.end(), + ) + .with_error_code(SELF_CONSTRUCTOR_USED_IN_INIT) +} + +pub fn self_field_used_in_init(source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + "Cannot reference Self field during initialization.", + source_range.start(), + source_range.end(), + ) + .with_error_code(SELF_FIELD_USED_IN_INIT) +} + +pub fn self_method_used_in_init(source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + "Cannot call Self method during initialization.", + source_range.start(), + source_range.end(), + ) + .with_error_code(SELF_METHOD_USED_IN_INIT) +} + +pub fn outer_class_field_usage(source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + "Cannot reference an outer class member.", + source_range.start(), + source_range.end(), + ) + .with_error_code(OUTER_CLASS_FIELD_USED_IN_INIT) +} + +pub fn outer_class_method_usage(source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + "Cannot call an outer class method.", + source_range.start(), + source_range.end(), + ) + .with_error_code(OUTER_CLASS_METHOD_USED_IN_INIT) +} diff --git a/dmc-lib/src/ast/expression.rs b/dmc-lib/src/ast/expression.rs index 0ba3264..5f21cef 100644 --- a/dmc-lib/src/ast/expression.rs +++ b/dmc-lib/src/ast/expression.rs @@ -13,8 +13,10 @@ use crate::ir::ir_operation::IrOperation; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; use std::cell::RefCell; use std::rc::Rc; @@ -29,61 +31,164 @@ pub enum Expression { } impl Expression { - pub fn gather_declared_names( + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + match self { + Expression::Binary(binary_expression) => { + binary_expression.init_scopes(symbol_table, container_scope); + } + Expression::Negative(negative_expression) => { + negative_expression.init_scopes(symbol_table, container_scope); + } + Expression::Call(call) => { + call.init_scopes(symbol_table, container_scope); + } + Expression::Identifier(identifier) => { + identifier.init_scope_id(container_scope); + } + _ => {} + } + } + + pub fn check_field_initializer_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + match self { + Expression::Binary(binary_expression) => { + binary_expression.check_field_initializer_names(symbol_table, class_symbol) + } + Expression::Negative(negative_expression) => { + negative_expression.check_field_initializer_names(symbol_table, class_symbol) + } + Expression::Call(call) => { + call.check_field_initializer_names(symbol_table, class_symbol) + } + Expression::Identifier(identifier) => { + if let Some(diagnostic) = + identifier.check_name_as_field_initializer(symbol_table, class_symbol) + { + vec![diagnostic] + } else { + vec![] + } + } + _ => vec![], + } + } + + pub fn check_constructor_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + match self { + Expression::Binary(binary_expression) => { + binary_expression.check_constructor_local_names(symbol_table, class_symbol) + } + Expression::Negative(negative_expression) => { + negative_expression.check_constructor_local_names(symbol_table, class_symbol) + } + Expression::Call(call) => { + call.check_constructor_local_names(symbol_table, class_symbol) + } + Expression::Identifier(identifier) => { + if let Some(diagnostic) = + identifier.check_constructor_local_name(symbol_table, class_symbol) + { + vec![diagnostic] + } else { + vec![] + } + } + _ => vec![], + } + } + + pub fn check_method_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + match self { + Expression::Binary(binary_expression) => { + binary_expression.check_method_local_names(symbol_table, class_symbol) + } + Expression::Negative(negative_expression) => { + negative_expression.check_method_local_names(symbol_table, class_symbol) + } + Expression::Call(call) => call.check_method_local_names(symbol_table, class_symbol), + Expression::Identifier(identifier) => { + if let Some(diagnostic) = + identifier.check_method_local_name(symbol_table, class_symbol) + { + vec![diagnostic] + } else { + vec![] + } + } + _ => vec![], + } + } + + pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec { + match self { + Expression::Binary(binary_expression) => { + binary_expression.check_static_fn_local_names(symbol_table) + } + Expression::Negative(negative_expression) => { + negative_expression.check_static_fn_local_names(symbol_table) + } + Expression::Call(call) => call.check_static_fn_local_names(symbol_table), + Expression::Identifier(identifier) => { + if let Some(diagnostic) = identifier.check_static_fn_local_name(symbol_table) { + vec![diagnostic] + } else { + vec![] + } + } + Expression::Integer(_) => { + vec![] + } + Expression::Double(_) => { + vec![] + } + Expression::String(_) => { + vec![] + } + } + } + + pub fn type_check( &mut self, - symbol_table: &mut SymbolTable, + symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> Result<(), Vec> { match self { Expression::Binary(binary_expression) => { - binary_expression.gather_declared_names(symbol_table) + binary_expression.type_check(symbol_table, types_table) } Expression::Negative(negative_expression) => { - negative_expression.gather_declared_names(symbol_table) + negative_expression.type_check(symbol_table, types_table) } - Expression::Call(call) => call.gather_declared_names(symbol_table), - Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table), + Expression::Call(call) => call.type_check(symbol_table, types_table), + Expression::Identifier(_) => Ok(()), Expression::Integer(_) => Ok(()), Expression::Double(_) => Ok(()), Expression::String(_) => Ok(()), } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - match self { - Expression::Binary(binary_expression) => { - binary_expression.check_name_usages(symbol_table) - } - Expression::Negative(negative_expression) => { - negative_expression.check_name_usages(symbol_table) - } - Expression::Call(call) => call.check_name_usages(symbol_table), - Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table), - Expression::Integer(_) => Ok(()), - Expression::Double(_) => Ok(()), - Expression::String(_) => Ok(()), - } - } - - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - match self { - Expression::Binary(binary_expression) => binary_expression.type_check(symbol_table), - Expression::Negative(negative_expression) => { - negative_expression.type_check(symbol_table) - } - Expression::Call(call) => call.type_check(symbol_table), - Expression::Identifier(identifier) => identifier.type_check(symbol_table), - Expression::Integer(_) => Ok(()), - Expression::Double(_) => Ok(()), - Expression::String(_) => Ok(()), - } - } - - pub fn type_info(&self) -> &TypeInfo { + pub fn type_info<'a>( + &'a self, + symbol_table: &SymbolTable, + types_table: &'a TypesTable, + ) -> &'a TypeInfo { match self { Expression::Binary(binary_expression) => binary_expression.type_info(), Expression::Negative(negative_expression) => negative_expression.type_info(), - Expression::Call(call) => call.return_type_info(), - Expression::Identifier(identifier) => identifier.type_info(), + Expression::Call(call) => call.return_type_info(symbol_table, types_table), + Expression::Identifier(identifier) => identifier.type_info(symbol_table, types_table), Expression::Integer(integer_literal) => integer_literal.type_info(), Expression::Double(double_literal) => double_literal.type_info(), Expression::String(string_literal) => string_literal.type_info(), @@ -106,12 +211,15 @@ impl Expression { &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> IrOperation { match self { Expression::Binary(binary_expression) => { - binary_expression.to_ir_operation(builder, symbol_table) + binary_expression.to_ir_operation(builder, symbol_table, types_table) + } + Expression::Call(call) => { + IrOperation::Call(call.to_ir(builder, symbol_table, types_table)) } - Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)), Expression::Integer(integer_literal) => { IrOperation::Load(IrExpression::Int(integer_literal.value())) } @@ -122,10 +230,10 @@ impl Expression { IrOperation::Load(IrExpression::String(string_literal.content().into())) } Expression::Identifier(identifier) => { - IrOperation::Load(identifier.expressible_symbol().ir_expression(builder)) + IrOperation::Load(identifier.ir_expression(builder, symbol_table)) } Expression::Negative(negative_expression) => { - IrOperation::Load(negative_expression.to_ir(builder, symbol_table)) + IrOperation::Load(negative_expression.to_ir(builder, symbol_table, types_table)) } } } @@ -134,14 +242,21 @@ impl Expression { &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> Option { match self { Expression::Binary(binary_expression) => { - Some(binary_expression.to_ir_expression(builder, symbol_table)) + Some(binary_expression.to_ir_expression(builder, symbol_table, types_table)) + } + Expression::Negative(negative_expression) => { + Some(negative_expression.to_ir(builder, symbol_table, types_table)) } Expression::Call(call) => { - let ir_call = call.to_ir(builder, symbol_table); - if matches!(call.return_type_info(), TypeInfo::Void) { + let ir_call = call.to_ir(builder, symbol_table, types_table); + if matches!( + call.return_type_info(symbol_table, types_table), + TypeInfo::Void + ) { builder .current_block_mut() .add_statement(IrStatement::Call(ir_call)); @@ -150,7 +265,7 @@ impl Expression { let t_var = IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), - call.return_type_info(), + call.return_type_info(symbol_table, types_table), ); let as_rc = Rc::new(RefCell::new(t_var)); let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call)); @@ -160,6 +275,9 @@ impl Expression { Some(IrExpression::Variable(as_rc)) } } + Expression::Identifier(identifier) => { + Some(identifier.ir_expression(builder, symbol_table)) + } Expression::Integer(integer_literal) => { Some(IrExpression::Int(integer_literal.value())) } @@ -169,13 +287,6 @@ impl Expression { Expression::String(string_literal) => { Some(IrExpression::String(string_literal.content().into())) } - Expression::Identifier(identifier) => { - let expressible_symbol = identifier.expressible_symbol(); - Some(expressible_symbol.ir_expression(builder)) - } - Expression::Negative(negative_expression) => { - Some(negative_expression.to_ir(builder, symbol_table)) - } } } } diff --git a/dmc-lib/src/ast/expression_statement.rs b/dmc-lib/src/ast/expression_statement.rs index 58d951b..4122df2 100644 --- a/dmc-lib/src/ast/expression_statement.rs +++ b/dmc-lib/src/ast/expression_statement.rs @@ -3,8 +3,10 @@ use crate::ast::ir_builder::IrBuilder; use crate::diagnostic::Diagnostic; use crate::ir::ir_return::IrReturn; use crate::ir::ir_statement::IrStatement; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; pub struct ExpressionStatement { expression: Box, @@ -21,26 +23,42 @@ impl ExpressionStatement { &self.expression } - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result<(), Vec> { - self.expression.gather_declared_names(symbol_table) + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.expression.init_scopes(symbol_table, container_scope); } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.expression.check_name_usages(symbol_table) + pub fn check_constructor_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + self.expression + .check_constructor_local_names(symbol_table, class_symbol) + } + + pub fn check_method_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + self.expression + .check_method_local_names(symbol_table, class_symbol) + } + + pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec { + self.expression.check_static_fn_local_names(symbol_table) } pub fn type_check( &mut self, symbol_table: &SymbolTable, + types_table: &TypesTable, must_return_type_info: Option<&TypeInfo>, ) -> Result<(), Vec> { - self.expression.type_check(symbol_table)?; + self.expression.type_check(symbol_table, types_table)?; if must_return_type_info.is_some() { - let expression_type = self.expression.type_info(); + let expression_type = self.expression.type_info(symbol_table, types_table); let return_type = must_return_type_info.unwrap(); if !return_type.is_assignable_from(expression_type) { return Err(vec![Diagnostic::new( @@ -60,9 +78,12 @@ impl ExpressionStatement { &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, + types_table: &TypesTable, should_return_value: bool, ) { - let ir_expression = self.expression.to_ir_expression(builder, symbol_table); + let ir_expression = self + .expression + .to_ir_expression(builder, symbol_table, types_table); if ir_expression.is_some() && should_return_value { builder .current_block_mut() diff --git a/dmc-lib/src/ast/extern_function.rs b/dmc-lib/src/ast/extern_function.rs index d6538b8..9e0f451 100644 --- a/dmc-lib/src/ast/extern_function.rs +++ b/dmc-lib/src/ast/extern_function.rs @@ -1,21 +1,22 @@ use crate::ast::fqn_context::FqnContext; -use crate::ast::helpers::{collect_diagnostics_into_mut, gather_oks, map_symbol_insert_result}; +use crate::ast::helpers::{collect_diagnostics_into_mut, collect_parameter_symbols_into}; use crate::ast::parameter::Parameter; use crate::ast::type_use::TypeUse; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; -use crate::symbol::class_symbol::ClassSymbol; +use crate::symbol::Symbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol_table::SymbolTable; -use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics, ok_or_return}; +use crate::{diagnostics_result, handle_diagnostics}; use std::cell::RefCell; use std::rc::Rc; pub struct ExternFunction { - declared_name: String, + declared_name: Rc, declared_name_source_range: SourceRange, parameters: Vec, return_type: TypeUse, + scope_id: Option, function_symbol: Option>>, } @@ -31,6 +32,7 @@ impl ExternFunction { declared_name_source_range, parameters, return_type, + scope_id: None, function_symbol: None, } } @@ -39,140 +41,46 @@ impl ExternFunction { &self.declared_name } - /// Inserts and saves a `FunctionSymbol`. - fn insert_save_function_symbol( - &mut self, - symbol_table: &mut SymbolTable, - fqn_context: &FqnContext, - ) -> Result<(), Vec> { - let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); + + symbol_table.push_function_scope(&format!("extern_function_scope({})", self.declared_name)); + + for parameter in &mut self.parameters { + parameter.init_scopes(symbol_table, container_scope); + } + self.return_type.init_scopes(symbol_table, container_scope); + + symbol_table.pop_scope(); + } + + pub fn make_symbols(&self, fqn_context: &FqnContext) -> (Rc, Vec) { + let mut all_symbols: Vec = Vec::new(); + + let mut parameter_symbols = Vec::new(); + collect_parameter_symbols_into(&self.parameters, &mut all_symbols, &mut parameter_symbols); + + let function_symbol = Rc::new(FunctionSymbol::new( &self.declared_name, self.declared_name_source_range.clone(), fqn_context.resolve(self.declared_name()), true, + false, + self.scope_id.unwrap(), + parameter_symbols, )); + all_symbols.push(Symbol::Function(function_symbol.clone())); - let function_symbol = ok_or_return!(map_symbol_insert_result( - insert_result, - &self.declared_name_source_range - )); - - self.function_symbol = Some(function_symbol); - - Ok(()) + (function_symbol, all_symbols) } - /// Gathers parameters' declared names, and sets `ParameterSymbols` on the `FunctionSymbol`. - fn gather_parameters_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result<(), Vec> { - let mut diagnostics: Vec = vec![]; - - let parameter_symbols = gather_oks( - &mut self.parameters, - |p| p.gather_declared_names(symbol_table), - &mut diagnostics, - ); - - // Only continue if we have all the parameters and no diagnostics. - maybe_return_diagnostics!(diagnostics); - - self.function_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_parameters(parameter_symbols); - - Ok(()) - } - - /// Gathers return type names - fn gather_return_type_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result<(), Vec> { - let mut diagnostics: Vec = vec![]; - handle_diagnostics!( - self.return_type.gather_declared_names(symbol_table), - diagnostics - ); - diagnostics_result!(diagnostics) - } - - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - fqn_context: &FqnContext, - ) -> Result<(), Vec> { - self.insert_save_function_symbol(symbol_table, fqn_context)?; - - symbol_table - .push_function_scope(&format!("extern_function_scope({})", &self.declared_name)); - - self.gather_parameters_names(symbol_table) - .inspect_err(|_| symbol_table.pop_scope())?; // pop scope if we had diagnostics! - - // handle return type - self.gather_return_type_names(symbol_table) - .inspect_err(|_| symbol_table.pop_scope())?; - - symbol_table.pop_scope(); // function scope - Ok(()) - } - - fn check_parameters_names( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - diagnostics: &mut Vec, - ) { - collect_diagnostics_into_mut( - &mut self.parameters, - |p| p.check_name_usages(symbol_table, class_context), - diagnostics, - ); - } - - fn check_return_type_names( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - diagnostics: &mut Vec, - ) { - handle_diagnostics!( - self.return_type - .check_name_usages(symbol_table, class_context), - diagnostics - ); - } - - /// Sets the `FunctionSymbol`'s return type info. - fn set_function_symbol_return_type(&mut self) { - self.function_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_return_type_info(self.return_type.type_info().clone()); - } - - pub fn check_name_usages( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - ) -> Result<(), Vec> { - let mut diagnostics: Vec = vec![]; - - self.check_parameters_names(symbol_table, class_context, &mut diagnostics); - - self.check_return_type_names(symbol_table, class_context, &mut diagnostics); - - maybe_return_diagnostics!(diagnostics); - - // set return type info on symbol now that its available - self.set_function_symbol_return_type(); - - Ok(()) + pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { + let mut diagnostics: Vec = Vec::new(); + for parameter in &self.parameters { + diagnostics.append(&mut parameter.check_names(symbol_table)); + } + diagnostics.append(&mut self.return_type.check_names(symbol_table)); + diagnostics } fn type_check_parameters( diff --git a/dmc-lib/src/ast/field.rs b/dmc-lib/src/ast/field.rs index 307040c..9a29c94 100644 --- a/dmc-lib/src/ast/field.rs +++ b/dmc-lib/src/ast/field.rs @@ -4,7 +4,8 @@ use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::field_symbol::FieldSymbol; -use crate::symbol_table::{SymbolInsertError, SymbolTable}; +use crate::symbol_table::SymbolTable; +use crate::types_table::TypesTable; use std::cell::RefCell; use std::rc::Rc; @@ -15,6 +16,7 @@ pub struct Field { is_mut: bool, declared_type: Option>, initializer: Option>, + scope_id: Option, field_symbol: Option>>, } @@ -34,6 +36,7 @@ impl Field { is_mut, declared_type: declared_type.map(Box::new), initializer: initializer.map(Box::new), + scope_id: None, field_symbol: None, } } @@ -54,77 +57,50 @@ impl Field { self.initializer.as_ref().map(Box::as_ref) } - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - field_index: usize, - ) -> Result>, Vec> { - // 1. insert field symbol - let to_insert = FieldSymbol::new( + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); + if let Some(type_use) = &mut self.declared_type { + type_use.init_scopes(symbol_table, container_scope); + } + if let Some(expression) = &mut self.initializer { + expression.init_scopes(symbol_table, container_scope); + } + } + + pub fn make_symbol(&self) -> FieldSymbol { + FieldSymbol::new( &self.declared_name, self.declared_name_source_range.clone(), self.is_mut, - ); - - let field_symbol = symbol_table - .insert_field_symbol(to_insert) - .map_err(|e| match e { - SymbolInsertError::AlreadyDeclared(already_declared) => { - vec![Diagnostic::new( - &format!( - "Symbol {} already declared in current scope.", - already_declared.symbol().borrow().declared_name() - ), - self.declared_name_source_range.start(), - self.declared_name_source_range.end(), - )] - } - })?; - - // save for later - let to_return = field_symbol.clone(); - self.field_symbol = Some(field_symbol); - - // set field index on symbol - self.field_symbol - .as_ref() - .unwrap() - .borrow_mut() - .set_field_index(field_index); - - // 2. gather type_use and initializer, if present - if let Some(type_use) = &mut self.declared_type { - type_use.gather_declared_names(symbol_table)?; - } - - if let Some(initializer) = &mut self.initializer { - initializer.gather_declared_names(symbol_table)?; - } - - Ok(to_return) + self.scope_id.unwrap(), + ) } - pub fn check_name_usages( + pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { + let mut diagnostics: Vec = Vec::new(); + if let Some(type_use) = &self.declared_type { + diagnostics.append(&mut type_use.check_names(symbol_table)); + } + diagnostics + } + + pub fn check_field_initializer_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + if let Some(initializer) = &self.initializer { + initializer.check_field_initializer_names(symbol_table, class_symbol) + } else { + vec![] + } + } + + pub fn type_check( &mut self, symbol_table: &SymbolTable, - class_context: Option<&Rc>>, + types_table: &TypesTable, ) -> Result<(), Vec> { - if let Some(type_use) = &mut self.declared_type { - type_use.check_name_usages(symbol_table, class_context)?; - } - - // This is going to get hairy, because users might attempt to use a field in an initializer - // (for either this field, or another one) before it's actually initialized. As such, we - // need a way to prevent lookup of current class' fields in the initializer. - // For now, the following is okay so long as we don't start referencing things in the - // initializers. - if let Some(initializer) = self.initializer.as_mut() { - initializer.check_name_usages(symbol_table)?; - } - Ok(()) - } - - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; if let Some(type_use) = &mut self.declared_type { @@ -134,7 +110,9 @@ impl Field { } if let Some(initializer) = &mut self.initializer { - if let Some(mut initializer_diagnostics) = initializer.type_check(symbol_table).err() { + if let Some(mut initializer_diagnostics) = + initializer.type_check(symbol_table, types_table).err() + { diagnostics.append(&mut initializer_diagnostics); } } @@ -149,7 +127,8 @@ impl Field { Some(type_use) => { match self.initializer.as_ref() { Some(initializer) => { - let initializer_type_info = initializer.type_info(); + let initializer_type_info = + initializer.type_info(symbol_table, types_table); let declared_type_info = type_use.type_info(); if declared_type_info.is_assignable_from(initializer_type_info) { declared_type_info @@ -176,7 +155,7 @@ impl Field { None => { // type is the initializer match self.initializer.as_ref() { - Some(initializer) => initializer.type_info(), + Some(initializer) => initializer.type_info(symbol_table, types_table), None => { // this is an error return Err(vec![Diagnostic::new( diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index 3fb78be..caec2f5 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -1,8 +1,8 @@ use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::helpers::{ - collect_diagnostics_into_enumerated_mut, collect_diagnostics_into_mut, gather_oks, - map_symbol_insert_result, + collect_diagnostics_into_enumerated_mut, collect_diagnostics_into_mut, + collect_parameter_symbols_into, }; use crate::ast::ir_builder::IrBuilder; use crate::ast::parameter::Parameter; @@ -18,19 +18,19 @@ use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; -use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics, ok_or_return}; -use std::cell::RefCell; +use crate::types_table::TypesTable; +use crate::{diagnostics_result, handle_diagnostics}; use std::ops::Neg; use std::rc::Rc; pub struct Function { - declared_name: String, + declared_name: Rc, declared_name_source_range: SourceRange, is_public: bool, parameters: Vec, return_type: Option, statements: Vec, - function_symbol: Option>>, + scope_id: Option, } impl Function { @@ -43,13 +43,13 @@ impl Function { statements: Vec, ) -> Self { Self { - declared_name: declared_name.to_string(), + declared_name: declared_name.into(), declared_name_source_range, is_public, parameters, return_type, statements, - function_symbol: None, + scope_id: None, } } @@ -61,220 +61,91 @@ impl Function { self.statements.iter().collect() } - /// Inserts a "self" parameter at index 0. - fn insert_self_parameter(&mut self) { - self.parameters.insert( - 0, - Parameter::new( - "self", - SourceRange::new(0, 0), - TypeUse::new("Self", SourceRange::new(0, 0), vec![]), - ), - ); - } + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); + let function_scope = + symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name)); - /// If `class_context` is `Some`, calls `insert_self_parameter()`. - fn maybe_insert_self_parameter(&mut self, class_context: Option<&Rc>>) { - if class_context.is_some() { - self.insert_self_parameter(); + for parameter in &mut self.parameters { + parameter.init_scopes(symbol_table, function_scope); } + if let Some(type_use) = &mut self.return_type { + type_use.init_scopes(symbol_table, function_scope); + } + + let body_scope = + symbol_table.push_block_scope(&format!("body_scope({})", self.declared_name)); + + for statement in &mut self.statements { + statement.init_scopes(symbol_table, body_scope); + } + + symbol_table.pop_scope(); // body + symbol_table.pop_scope(); // function } - /// Inserts and saves to self a `FunctionSymbol` - fn insert_save_function_symbol( - &mut self, - symbol_table: &mut SymbolTable, + pub fn make_symbols( + &self, fqn_context: &FqnContext, - ) -> Result<(), Vec> { - let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( - self.declared_name(), + is_method: bool, + ) -> (Rc, Vec) { + let mut all_symbols: Vec = vec![]; + + let mut parameter_symbols = Vec::new(); + collect_parameter_symbols_into(&self.parameters, &mut all_symbols, &mut parameter_symbols); + + let function_symbol = Rc::new(FunctionSymbol::new( + &self.declared_name, self.declared_name_source_range.clone(), fqn_context.resolve(self.declared_name()), false, + is_method, + self.scope_id.unwrap(), + parameter_symbols, )); + all_symbols.push(Symbol::Function(function_symbol.clone())); - // get function symbol if successful - let function_symbol = ok_or_return!(map_symbol_insert_result( - insert_result, - &self.declared_name_source_range - )); - - // save function symbol for later - self.function_symbol = Some(function_symbol.clone()); - - Ok(()) + (function_symbol, all_symbols) } - /// Gathers all declared names in parameters - fn gather_parameters_names( - &mut self, - symbol_table: &mut SymbolTable, - class_context: Option<&Rc>>, - ) -> Result<(), Vec> { - // handle self case - self.maybe_insert_self_parameter(class_context); - - let mut diagnostics: Vec = vec![]; - - // now gather all names for the params - let parameter_symbols = gather_oks( - &mut self.parameters, - |p| p.gather_declared_names(symbol_table), - &mut diagnostics, - ); - - // important: if there were diagnostics we don't have all the param symbols - // n.b.: passing pop_scope callback because we don't want to mess up the scopes for others - maybe_return_diagnostics!(diagnostics); - - // set params on function symbol - self.function_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_parameters(parameter_symbols); - Ok(()) - } - - /// Gathers all return type names - fn gather_return_type_names( - &mut self, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, - ) { - collect_diagnostics_into_mut( - self.return_type.as_mut_slice(), - |type_use| type_use.gather_declared_names(symbol_table), - diagnostics, - ); - } - - /// Gathers all statements' names - fn gather_statements_names( - &mut self, - symbol_table: &mut SymbolTable, - diagnostics: &mut Vec, - ) { - collect_diagnostics_into_mut( - &mut self.statements, - |s| s.gather_declared_names(symbol_table), - diagnostics, - ); - } - - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - fqn_context: &FqnContext, - class_context: Option<&Rc>>, - ) -> Result<(), Vec> { - // function symbol - self.insert_save_function_symbol(symbol_table, fqn_context)?; - - // parameters - symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name)); - self.gather_parameters_names(symbol_table, class_context) - .inspect_err(|_| symbol_table.pop_scope())?; // if we errored, we need to fix scopes - - // return type - let mut diagnostics = vec![]; - self.gather_return_type_names(symbol_table, &mut diagnostics); - - // enter main block - symbol_table.push_block_scope(&format!("main_block_scope({})", self.declared_name)); - - // statements - self.gather_statements_names(symbol_table, &mut diagnostics); - - symbol_table.pop_scope(); // main block scope - symbol_table.pop_scope(); // function scope - - diagnostics_result!(diagnostics) - } - - /// Checks parameter name usages - fn check_parameter_names( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - diagnostics: &mut Vec, - ) { - collect_diagnostics_into_mut( - &mut self.parameters, - |p| p.check_name_usages(symbol_table, class_context), - diagnostics, - ); - } - - /// Checks return type name usages, if there is a `TypeUse` present. If `None`, sets the - /// `FunctionSymbol`'s return type to `TypeInfo::Void`. - fn check_return_type_names( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - diagnostics: &mut Vec, - ) { - if let Some(type_use) = &mut self.return_type { - match type_use.check_name_usages(symbol_table, class_context) { - Ok(_) => { - // set return type info on function symbol - self.function_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_return_type_info(type_use.type_info().clone()); - } - Err(mut type_use_diagnostics) => { - diagnostics.append(&mut type_use_diagnostics); - } - } - } else { - // we don't have a given return type, so it's void - self.function_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_return_type_info(TypeInfo::Void); + pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { + let mut diagnostics = Vec::new(); + for parameter in &self.parameters { + diagnostics.append(&mut parameter.check_names(symbol_table)); } + if let Some(type_use) = &self.return_type { + diagnostics.append(&mut type_use.check_names(symbol_table)); + } + diagnostics } - /// Checks all statement name usages. - fn check_statement_names( - &mut self, - symbol_table: &SymbolTable, - diagnostics: &mut Vec, - ) { - collect_diagnostics_into_mut( - &mut self.statements, - |s| s.check_name_usages(symbol_table), - diagnostics, - ); + pub fn analyze_method_local_names( + &self, + symbol_table: &mut SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + self.statements + .iter() + .flat_map(|statement| statement.analyze_method_local_names(symbol_table, class_symbol)) + .collect() } - pub fn check_name_usages( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - ) -> Result<(), Vec> { - let mut diagnostics = vec![]; - - // parameters - self.check_parameter_names(symbol_table, class_context, &mut diagnostics); - - // return type - self.check_return_type_names(symbol_table, class_context, &mut diagnostics); - - // statements - self.check_statement_names(symbol_table, &mut diagnostics); - - diagnostics_result!(diagnostics) + pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec { + self.statements + .iter() + .flat_map(|statement| statement.analyze_static_fn_local_names(symbol_table)) + .collect() } - /// Gets the return type `TypeInfo`. This function is only safe to call after an `Ok` call to - /// `check_return_type_names()`. - fn get_return_type_info(&self) -> TypeInfo { - let function_symbol = self.function_symbol.as_ref().unwrap().borrow(); - function_symbol.return_type_info().clone() + fn get_return_type_info( + types_table: &TypesTable, + function_symbol: &FunctionSymbol, + ) -> TypeInfo { + types_table + .function_return_types() + .get(function_symbol) + .cloned() + .unwrap() } /// Type checks parameters. @@ -305,9 +176,11 @@ impl Function { fn type_check_statements( &mut self, symbol_table: &SymbolTable, + types_table: &mut TypesTable, diagnostics: &mut Vec, + function_symbol: &FunctionSymbol, ) { - let return_type_info = self.get_return_type_info(); + let return_type_info = Self::get_return_type_info(types_table, function_symbol); let statements_len = self.statements.len(); collect_diagnostics_into_enumerated_mut( @@ -315,17 +188,24 @@ impl Function { |i, s| { let is_last = i == statements_len - 1; if is_last { - s.type_check(symbol_table, Some(&return_type_info)) + s.type_check(symbol_table, types_table, Some(&return_type_info)) } else { - s.type_check(symbol_table, None) + s.type_check(symbol_table, types_table, None) } }, diagnostics, ); } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + pub fn type_check( + &mut self, + symbol_table: &SymbolTable, + types_table: &mut TypesTable, + ) -> Result<(), Vec> { let mut diagnostics = vec![]; + let function_symbol = symbol_table + .get_function_symbol(self.scope_id.unwrap(), self.declared_name()) + .unwrap(); // parameters self.type_check_parameters(symbol_table, &mut diagnostics); @@ -334,33 +214,39 @@ impl Function { self.type_check_return_type(symbol_table, &mut diagnostics); // statements - self.type_check_statements(symbol_table, &mut diagnostics); + self.type_check_statements(symbol_table, types_table, &mut diagnostics, function_symbol); diagnostics_result!(diagnostics) } /// Converts all parameters to ir. Saves the IrParameter to the associated parameter symbol. - fn parameters_to_ir(&self, builder: &mut IrBuilder) { + fn parameters_to_ir( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) { for (i, parameter) in self.parameters.iter().enumerate() { - let parameter_symbol = parameter.parameter_symbol(); + let parameter_symbol = symbol_table + .get_parameter_symbol_owned(parameter.scope_id(), parameter.declared_name()) + .unwrap(); + let parameter_type_info = types_table + .parameter_types() + .get(¶meter_symbol) + .unwrap(); let stack_offset = (self.parameters.len() as isize).neg() + (i as isize); let ir_parameter = IrParameter::new( - parameter_symbol.borrow().declared_name(), - parameter_symbol.borrow().type_info().clone(), + parameter_symbol.declared_name(), + parameter_type_info.clone(), stack_offset, ); let as_rc = Rc::new(ir_parameter); - builder.parameters_mut().push(as_rc.clone()); - parameter_symbol.borrow_mut().set_ir_parameter(as_rc); + builder.push_parameter(¶meter_symbol, as_rc.clone()); } } /// If `class_context.is_some()`, set parameter 0 to the self parameter/variable on the builder. - fn handle_method_case( - &self, - builder: &mut IrBuilder, - class_context: Option<&Rc>>, - ) { + fn handle_method_case(&self, builder: &mut IrBuilder, class_context: Option<&ClassSymbol>) { // if we are a method, we need to set the self parameter on the builder if class_context.is_some() { let parameter_0 = builder.parameters()[0].clone(); @@ -370,24 +256,39 @@ impl Function { } /// Convert all statements to ir. - fn statements_to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { - let return_type_info = self.get_return_type_info(); + fn statements_to_ir( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + types_table: &TypesTable, + function_symbol: &FunctionSymbol, + ) { + let return_type_info = Self::get_return_type_info(types_table, function_symbol); let should_return_value = !matches!(return_type_info, TypeInfo::Void); for (i, statement) in self.statements.iter().enumerate() { let is_last = i == self.statements.len() - 1; - statement.to_ir(builder, symbol_table, should_return_value && is_last); + statement.to_ir( + builder, + symbol_table, + types_table, + should_return_value && is_last, + ); } } pub fn to_ir( &self, symbol_table: &SymbolTable, - class_context: Option<&Rc>>, + types_table: &TypesTable, + class_context: Option<&ClassSymbol>, ) -> IrFunction { let mut builder = IrBuilder::new(); + let function_symbol = symbol_table + .get_function_symbol(self.scope_id.unwrap(), self.declared_name()) + .unwrap(); // parameters - self.parameters_to_ir(&mut builder); + self.parameters_to_ir(&mut builder, symbol_table, types_table); let entry_block_id = builder.new_block(); @@ -395,15 +296,15 @@ impl Function { self.handle_method_case(&mut builder, class_context); // body - self.statements_to_ir(&mut builder, symbol_table); + self.statements_to_ir(&mut builder, symbol_table, types_table, function_symbol); builder.finish_block(); let entry_block = builder.get_block(entry_block_id).clone(); IrFunction::new( - fqn_parts_to_string(self.function_symbol.as_ref().unwrap().borrow().fqn_parts()), - builder.parameters(), - &self.get_return_type_info(), + fqn_parts_to_string(function_symbol.fqn_parts()), + builder.parameters().iter().map(|p| (*p).clone()).collect(), + &Self::get_return_type_info(types_table, function_symbol), entry_block, ) } diff --git a/dmc-lib/src/ast/generic_parameter.rs b/dmc-lib/src/ast/generic_parameter.rs index bcd318e..a346c16 100644 --- a/dmc-lib/src/ast/generic_parameter.rs +++ b/dmc-lib/src/ast/generic_parameter.rs @@ -1,11 +1,9 @@ use crate::ast::type_use::TypeUse; -use crate::diagnostic::{Diagnostic, SecondaryLabel}; -use crate::error_codes::SYMBOL_ALREADY_DECLARED; +use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; -use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; -use crate::symbol_table::{SymbolInsertError, SymbolTable}; -use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics}; +use crate::symbol_table::SymbolTable; +use crate::{diagnostics_result, handle_diagnostics}; use std::cell::RefCell; use std::rc::Rc; @@ -13,6 +11,7 @@ pub struct GenericParameter { declared_name: Rc, declared_name_source_range: SourceRange, extends: Vec, + scope_id: Option, generic_parameter_symbol: Option>>, } @@ -26,87 +25,31 @@ impl GenericParameter { declared_name: declared_name.into(), declared_name_source_range, extends, + scope_id: None, generic_parameter_symbol: None, } } - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result>, Vec> { - // insert symbol - let to_insert = - GenericParameterSymbol::new(&self.declared_name, &self.declared_name_source_range); - let to_return = match symbol_table.insert_generic_parameter_symbol(to_insert) { - Ok(generic_parameter_symbol) => generic_parameter_symbol, - Err(symbol_insert_error) => { - return match symbol_insert_error { - SymbolInsertError::AlreadyDeclared(already_declared) => { - let already_declared_symbol = already_declared.symbol().borrow(); - let already_declared_source_range = - already_declared_symbol.declared_name_source_range(); - let diagnostic = Diagnostic::new( - &format!("Symbol {} already declared in scope.", self.declared_name), - self.declared_name_source_range.start(), - self.declared_name_source_range.end(), - ) - .with_reporter(file!(), line!()) - .with_error_code(SYMBOL_ALREADY_DECLARED) - .with_secondary_labels(&[SecondaryLabel::new( - already_declared_source_range.start(), - already_declared_source_range.end(), - Some("Symbol already declared here.".to_string()), - )]); - Err(vec![diagnostic]) - } - }; - } - }; - - // save param symbol - self.generic_parameter_symbol = Some(to_return.clone()); - - let mut diagnostics: Vec = vec![]; - + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); for type_use in &mut self.extends { - handle_diagnostics!(type_use.gather_declared_names(symbol_table), diagnostics); - } - - if diagnostics.is_empty() { - Ok(to_return) - } else { - Err(diagnostics) + type_use.init_scopes(symbol_table, container_scope); } } - pub fn check_name_usages( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - ) -> Result<(), Vec> { - let mut diagnostics: Vec = vec![]; - // check the extends type uses - for type_use in &mut self.extends { - handle_diagnostics!( - type_use.check_name_usages(symbol_table, class_context), - diagnostics - ); - } - maybe_return_diagnostics!(diagnostics); + pub fn make_symbol(&self) -> GenericParameterSymbol { + GenericParameterSymbol::new( + &self.declared_name, + &self.declared_name_source_range, + self.scope_id.unwrap(), + ) + } - // now that each extends type use has type info, set the type infos on the generic parameter - let extends_type_infos = self - .extends + pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { + self.extends .iter() - .map(|type_use| type_use.type_info().clone()) - .collect::>(); - self.generic_parameter_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_extends(extends_type_infos); - - Ok(()) + .flat_map(|type_use| type_use.check_names(symbol_table)) + .collect() } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { diff --git a/dmc-lib/src/ast/helpers.rs b/dmc-lib/src/ast/helpers.rs index b4c3510..661062e 100644 --- a/dmc-lib/src/ast/helpers.rs +++ b/dmc-lib/src/ast/helpers.rs @@ -1,9 +1,11 @@ +use crate::ast::diagnostic_factories::symbol_already_declared; use crate::ast::fqn_context::FqnContext; -use crate::diagnostic::{Diagnostic, SecondaryLabel}; +use crate::ast::parameter::Parameter; +use crate::diagnostic::Diagnostic; use crate::diagnostics_result; -use crate::error_codes::SYMBOL_ALREADY_DECLARED; -use crate::source_range::SourceRange; -use crate::symbol_table::SymbolInsertError; +use crate::symbol::Symbol; +use crate::symbol::parameter_symbol::ParameterSymbol; +use crate::symbol_table::SymbolTable; use std::rc::Rc; /// Iterates through all `ts`, running the `f` function, and pushing returned `Diagnostic`s @@ -72,36 +74,6 @@ pub fn collect_diagnostics_single( diagnostics_result!(diagnostics) } -pub fn map_symbol_insert_result( - insert_result: Result, - declared_name_source_range: &SourceRange, -) -> Result> { - match insert_result { - Ok(symbol) => Ok(symbol), - Err(symbol_insert_error) => match symbol_insert_error { - SymbolInsertError::AlreadyDeclared(already_declared) => { - let already_declared_symbol = already_declared.symbol().borrow(); - let diagnostic = Diagnostic::new( - &format!( - "Symbol {} already declared in current scope.", - already_declared_symbol.declared_name() - ), - declared_name_source_range.start(), - declared_name_source_range.end(), - ) - .with_reporter(file!(), line!()) - .with_error_code(SYMBOL_ALREADY_DECLARED) - .with_secondary_labels(&[SecondaryLabel::new( - already_declared_symbol.declared_name_source_range().start(), - already_declared_symbol.declared_name_source_range().end(), - Some("Symbol already declared here.".to_string()), - )]); - Err(vec![diagnostic]) - } - }, - } -} - pub fn gather_oks( ts: &mut [T], mut f: impl FnMut(&mut T) -> Result>, @@ -122,3 +94,40 @@ pub fn gather_oks( pub fn resolve_ctor_name(fqn_context: &FqnContext) -> Vec> { fqn_context.resolve("ctor") // ctor is a keyword at the language level, should not be callable via normal means } + +pub fn try_insert_symbol_into( + symbol: Symbol, + symbol_table: &mut SymbolTable, +) -> Result<(), Diagnostic> { + let maybe_already_inserted = symbol_table.get_symbol(symbol.scope_id(), symbol.declared_name()); + if let Some(already_inserted) = maybe_already_inserted { + Err(symbol_already_declared(&already_inserted, &symbol)) + } else { + symbol_table.insert_symbol(symbol); + Ok(()) + } +} + +pub fn try_insert_symbols_into( + symbols: Vec, + symbol_table: &mut SymbolTable, +) -> Result<(), Vec> { + let diagnostics: Vec = symbols + .into_iter() + .map(|symbol| try_insert_symbol_into(symbol, symbol_table)) + .filter_map(Result::err) + .collect(); + diagnostics_result!(diagnostics) +} + +pub fn collect_parameter_symbols_into( + parameters: &[Parameter], + all_symbols: &mut Vec, + parameter_symbols: &mut Vec>, +) { + for parameter in parameters { + let symbol = Rc::new(parameter.make_symbol()); + all_symbols.push(Symbol::Parameter(symbol.clone())); + parameter_symbols.push(symbol); + } +} diff --git a/dmc-lib/src/ast/identifier.rs b/dmc-lib/src/ast/identifier.rs index 90bc1c2..e2a16b7 100644 --- a/dmc-lib/src/ast/identifier.rs +++ b/dmc-lib/src/ast/identifier.rs @@ -1,25 +1,39 @@ +use crate::ast::diagnostic_factories::{ + outer_class_field_usage, outer_class_method_usage, self_constructor_used_in_init, + self_field_used_in_init, self_method_used_in_init, symbol_not_found, +}; +use crate::ast::ir_builder::IrBuilder; +use crate::ast::ir_util::get_or_init_field_pointer_variable; use crate::diagnostic::Diagnostic; +use crate::ir::ir_assign::IrAssign; +use crate::ir::ir_expression::IrExpression; +use crate::ir::ir_operation::IrOperation; +use crate::ir::ir_read_field::IrReadField; +use crate::ir::ir_statement::IrStatement; +use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol; +use crate::symbol::field_symbol::FieldSymbol; +use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; +use std::cell::RefCell; +use std::rc::Rc; pub struct Identifier { name: String, - scope_id: Option, - expressible_symbol: Option, - type_info: Option, source_range: SourceRange, + scope_id: Option, } impl Identifier { pub fn new(name: &str, source_range: SourceRange) -> Self { Self { name: name.into(), - scope_id: None, - expressible_symbol: None, - type_info: None, source_range, + scope_id: None, } } @@ -27,45 +41,237 @@ impl Identifier { &self.name } - pub fn gather_declared_names( - &mut self, - symbol_table: &SymbolTable, - ) -> Result<(), Vec> { - self.scope_id = Some(symbol_table.current_scope_id()); - Ok(()) + pub fn init_scope_id(&mut self, container_scope: usize) { + self.scope_id = Some(container_scope); } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let maybe_expressible_symbol = - symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); - match maybe_expressible_symbol { - None => Err(vec![Diagnostic::new( - &format!("Unable to resolve symbol {}", self.name), - self.source_range.start(), - self.source_range.end(), - )]), - Some(expressible_symbol) => { - self.expressible_symbol = Some(expressible_symbol); - Ok(()) - } + pub fn scope_id(&self) -> usize { + self.scope_id.unwrap() + } + + /// Check against recursively constructing this class. + fn check_self_constructor_use( + &self, + context_class_symbol: &ClassSymbol, + class_symbol: &ClassSymbol, + ) -> Option { + // this is not future-proof, as we will eventually allow reference to the self class, which + // would (theoretically) be assigned to an instance field + if context_class_symbol == class_symbol { + Some(self_constructor_used_in_init(&self.source_range)) + } else { + None } } - pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec> { - let type_info = self.expressible_symbol.as_ref().unwrap().type_info(); - self.type_info = Some(type_info); - Ok(()) + /// Check against using this or outer class' bare fields. + fn check_self_or_outer_field_use( + &self, + context_class_symbol: &ClassSymbol, + field_symbol: &FieldSymbol, + ) -> Option { + // Usage of a bare field will always be an error, whether in this class or an outer class + if context_class_symbol + .fields() + .contains_key(field_symbol.declared_name()) + { + Some(self_field_used_in_init(&self.source_range)) + } else { + Some(outer_class_field_usage(&self.source_range)) + } } - pub fn type_info(&self) -> &TypeInfo { - self.type_info.as_ref().unwrap() + /// Check against using self or outer class methods. + fn check_self_or_outer_method_use( + &self, + context_class_symbol: &ClassSymbol, + function_symbol: &FunctionSymbol, + ) -> Option { + if context_class_symbol + .functions() + .contains_key(function_symbol.declared_name()) + { + // Can only use Self static functions, which we don't have yet + Some(self_method_used_in_init(&self.source_range)) + } else if function_symbol.is_method() { + // Can only use outer class static functions, which we don't have yet + Some(outer_class_method_usage(&self.source_range)) + } else { + None + } } - pub fn expressible_symbol(&self) -> &ExpressibleSymbol { - self.expressible_symbol.as_ref().unwrap() + pub fn check_name_as_field_initializer( + &self, + symbol_table: &SymbolTable, + context_class_symbol: &ClassSymbol, + ) -> Option { + let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); + if let Some(symbol) = symbol { + match symbol { + ExpressibleSymbol::Class(class_symbol) => { + self.check_self_constructor_use(context_class_symbol, &class_symbol) + } + ExpressibleSymbol::Field(field_symbol) => { + self.check_self_or_outer_field_use(context_class_symbol, &field_symbol) + } + ExpressibleSymbol::Function(function_symbol) => { + self.check_self_or_outer_method_use(context_class_symbol, &function_symbol) + } + ExpressibleSymbol::Parameter(_) => { + // Cannot get here, because classes cannot currently be declared in functions + unreachable!() + } + ExpressibleSymbol::Variable(_) => { + // Cannot get here, as classes cannot currently be declared in functions + unreachable!() + } + } + } else { + Some(symbol_not_found(&self.name, &self.source_range)) + } + } + + pub fn check_constructor_local_name( + &self, + symbol_table: &SymbolTable, + context_class_symbol: &ClassSymbol, + ) -> Option { + let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); + if let Some(symbol) = symbol { + match symbol { + ExpressibleSymbol::Class(class_symbol) => { + self.check_self_constructor_use(context_class_symbol, &class_symbol) + } + ExpressibleSymbol::Field(field_symbol) => { + self.check_self_or_outer_field_use(context_class_symbol, &field_symbol) + } + ExpressibleSymbol::Function(function_symbol) => { + self.check_self_or_outer_method_use(context_class_symbol, &function_symbol) + } + ExpressibleSymbol::Parameter(_) => None, + ExpressibleSymbol::Variable(_) => None, + } + } else { + Some(symbol_not_found(&self.name, &self.source_range)) + } + } + + pub fn check_method_local_name( + &self, + symbol_table: &SymbolTable, + context_class_symbol: &ClassSymbol, + ) -> Option { + let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); + if let Some(symbol) = symbol { + match symbol { + ExpressibleSymbol::Class(_) => { + None // all class usages should be ok + } + ExpressibleSymbol::Field(field_symbol) => { + // Must be a reference to a field in this class + if context_class_symbol + .fields() + .contains_key(field_symbol.declared_name()) + { + None + } else { + Some(outer_class_field_usage(&self.source_range)) + } + } + ExpressibleSymbol::Function(function_symbol) => { + // Must be a method in this class + if function_symbol.is_method() + && !context_class_symbol + .functions() + .contains_key(function_symbol.declared_name()) + { + Some(outer_class_method_usage(&self.source_range)) + } else { + None + } + } + ExpressibleSymbol::Parameter(_) => { + None // ok + } + ExpressibleSymbol::Variable(_) => { + None // ok + } + } + } else { + Some(symbol_not_found(&self.name, &self.source_range)) + } + } + + /// WARNING: this is not appropriate (yet) for class static functions. + pub fn check_static_fn_local_name(&self, symbol_table: &SymbolTable) -> Option { + if symbol_table + .find_expressible_symbol(self.scope_id.unwrap(), &self.name) + .is_some() + { + None + } else { + Some(symbol_not_found(&self.name, &self.source_range)) + } } pub fn source_range(&self) -> &SourceRange { &self.source_range } + + pub fn type_info<'a>( + &self, + symbol_table: &SymbolTable, + types_table: &'a TypesTable, + ) -> &'a TypeInfo { + let variable_symbol = symbol_table + .get_variable_symbol(self.scope_id.unwrap(), &self.name) + .unwrap(); + types_table.variable_types().get(variable_symbol).unwrap() + } + + pub fn ir_expression( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + ) -> IrExpression { + let expressible_symbol = symbol_table + .find_expressible_symbol(self.scope_id.unwrap(), &self.name) + .unwrap(); + match expressible_symbol { + ExpressibleSymbol::Class(class_symbol) => { + todo!() + } + ExpressibleSymbol::Field(field_symbol) => { + let read_destination = IrVariable::new_vr( + builder.new_t_var().into(), + builder.current_block().id(), + field_symbol.type_info(), + ); + let read_destination_as_rc = Rc::new(RefCell::new(read_destination)); + let ir_read_field = IrReadField::new( + get_or_init_field_pointer_variable(builder, &field_symbol).clone(), + ); + builder + .current_block_mut() + .add_statement(IrStatement::Assign(IrAssign::new( + read_destination_as_rc.clone(), + IrOperation::ReadField(ir_read_field), + ))); + IrExpression::Variable(read_destination_as_rc) + } + ExpressibleSymbol::Function(_) => { + panic!("Cannot yet get ir-variable for FunctionSymbol") + } + ExpressibleSymbol::Parameter(parameter_symbol) => { + let parameters_map = builder.parameters_map(); + let ir_parameter = parameters_map.get(¶meter_symbol).unwrap(); + IrExpression::Parameter(ir_parameter.clone()) + } + ExpressibleSymbol::Variable(variable_symbol) => { + let ir_variable = builder.local_variables().get(&variable_symbol).unwrap(); + IrExpression::Variable(ir_variable.clone()) + } + } + } } diff --git a/dmc-lib/src/ast/ir_builder.rs b/dmc-lib/src/ast/ir_builder.rs index 176970e..1de6770 100644 --- a/dmc-lib/src/ast/ir_builder.rs +++ b/dmc-lib/src/ast/ir_builder.rs @@ -3,12 +3,15 @@ use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; +use crate::symbol::parameter_symbol::ParameterSymbol; +use crate::symbol::variable_symbol::VariableSymbol; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; pub struct IrBuilder { - parameters: Vec>, + parameters: Vec<(Rc, Rc)>, + local_variables: HashMap, Rc>>, block_counter: usize, t_var_counter: usize, blocks: HashMap>>, @@ -22,6 +25,7 @@ impl IrBuilder { pub fn new() -> Self { Self { parameters: vec![], + local_variables: HashMap::new(), block_counter: 0, t_var_counter: 0, blocks: HashMap::new(), @@ -32,12 +36,37 @@ impl IrBuilder { } } - pub fn parameters(&self) -> &[Rc] { - &self.parameters + pub fn local_variables(&self) -> &HashMap, Rc>> { + &self.local_variables } - pub fn parameters_mut(&mut self) -> &mut Vec> { - &mut self.parameters + pub fn local_variables_mut( + &mut self, + ) -> &mut HashMap, Rc>> { + &mut self.local_variables + } + + pub fn parameters(&self) -> Vec<&Rc> { + self.parameters + .iter() + .map(|(_, ir_parameter)| ir_parameter) + .collect() + } + + pub fn parameters_map(&self) -> HashMap, Rc> { + let mut map = HashMap::new(); + for (name, ir_parameter) in &self.parameters { + map.insert(name.clone(), ir_parameter.clone()); + } + map + } + + pub fn push_parameter( + &mut self, + parameter_symbol: &Rc, + parameter: Rc, + ) { + self.parameters.push((parameter_symbol.clone(), parameter)); } pub fn new_block(&mut self) -> usize { diff --git a/dmc-lib/src/ast/ir_util.rs b/dmc-lib/src/ast/ir_util.rs index 6212265..80eaa49 100644 --- a/dmc-lib/src/ast/ir_util.rs +++ b/dmc-lib/src/ast/ir_util.rs @@ -5,25 +5,24 @@ use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut; use crate::ir::ir_operation::IrOperation; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; -use crate::symbol::Symbol; use crate::symbol::field_symbol::FieldSymbol; use std::cell::RefCell; use std::rc::Rc; pub fn get_or_init_field_pointer_variable<'a>( builder: &'a mut IrBuilder, - field_symbol: &Rc>, + field_symbol: &Rc, ) -> &'a Rc> { // This following should work because blocks are flat in the ir; if a variable is defined in the // ir block from this point forward, it's available to all subsequent blocks. if !builder .field_pointer_variables() - .contains_key(field_symbol.borrow().declared_name()) + .contains_key(field_symbol.declared_name()) { let field_ref_variable = IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), - field_symbol.borrow().type_info(), + field_symbol.type_info(), ); let as_rc = Rc::new(RefCell::new(field_ref_variable)); let to_insert = as_rc.clone(); @@ -34,31 +33,31 @@ pub fn get_or_init_field_pointer_variable<'a>( as_rc, IrOperation::GetFieldRef(IrGetFieldRef::new( self_parameter_or_variable.clone(), - field_symbol.borrow().field_index(), + field_symbol.field_index(), )), ))); builder .field_pointer_variables_mut() - .insert(field_symbol.borrow().declared_name_owned(), to_insert); + .insert(field_symbol.declared_name_owned(), to_insert); } builder .field_pointer_variables() - .get(field_symbol.borrow().declared_name()) + .get(field_symbol.declared_name()) .unwrap() } pub fn get_or_init_mut_field_pointer_variable<'a>( builder: &'a mut IrBuilder, - field_symbol: &Rc>, + field_symbol: &Rc, ) -> &'a Rc> { if !builder .field_mut_pointer_variables() - .contains_key(field_symbol.borrow().declared_name()) + .contains_key(field_symbol.declared_name()) { let mut_field_pointer_variable = IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), - field_symbol.borrow().type_info(), + field_symbol.type_info(), ); let as_rc = Rc::new(RefCell::new(mut_field_pointer_variable)); let to_insert = as_rc.clone(); @@ -69,15 +68,15 @@ pub fn get_or_init_mut_field_pointer_variable<'a>( as_rc, IrOperation::GetFieldRefMut(IrGetFieldRefMut::new( self_parameter_or_variable.clone(), - field_symbol.borrow().field_index(), + field_symbol.field_index(), )), ))); builder .field_mut_pointer_variables_mut() - .insert(field_symbol.borrow().declared_name_owned(), to_insert); + .insert(field_symbol.declared_name_owned(), to_insert); } builder .field_mut_pointer_variables() - .get(field_symbol.borrow().declared_name()) + .get(field_symbol.declared_name()) .unwrap() } diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index 4e3a5f0..c93db3d 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -1,12 +1,16 @@ use crate::ast::expression::Expression; +use crate::ast::helpers::try_insert_symbol_into; use crate::ast::ir_builder::IrBuilder; use crate::diagnostic::Diagnostic; use crate::ir::ir_assign::IrAssign; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; +use crate::symbol::Symbol; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::variable_symbol::VariableSymbol; -use crate::symbol_table::{SymbolInsertError, SymbolTable}; +use crate::symbol_table::SymbolTable; +use crate::types_table::TypesTable; use std::cell::RefCell; use std::rc::Rc; @@ -46,76 +50,125 @@ impl LetStatement { &mut self.initializer } - pub fn gather_declared_names( - &mut self, + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); + self.initializer.init_scopes(symbol_table, container_scope); + } + + fn make_and_insert_variable_symbol( + &self, symbol_table: &mut SymbolTable, - ) -> Result<(), Vec> { - let mut diagnostics = vec![]; - - match self.initializer_mut().gather_declared_names(symbol_table) { - Ok(_) => {} - Err(mut initializer_diagnostics) => { - diagnostics.append(&mut initializer_diagnostics); - } - } - - let insert_result = symbol_table.insert_variable_symbol(VariableSymbol::new( + ) -> Option { + let variable_symbol = Rc::new(VariableSymbol::new( &self.declared_name, - self.declared_name_source_range.clone(), + &self.declared_name_source_range, self.is_mut, + self.scope_id.unwrap(), )); - if let Err(symbol_insert_error) = insert_result { - match symbol_insert_error { - SymbolInsertError::AlreadyDeclared(already_declared) => { - diagnostics.push(Diagnostic::new( - &format!( - "Symbol {} already declared in current scope", - already_declared.symbol().borrow().declared_name() - ), - self.declared_name_source_range.start(), - self.declared_name_source_range.end(), - )) - } - } - } - - self.scope_id = Some(symbol_table.current_scope_id()); - - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + try_insert_symbol_into(Symbol::Variable(variable_symbol), symbol_table).err() } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.initializer.check_name_usages(symbol_table) + pub fn analyze_constructor_local_names( + &self, + symbol_table: &mut SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + let mut diagnostics = Vec::new(); + diagnostics.append( + &mut self + .initializer + .check_constructor_local_names(symbol_table, class_symbol), + ); + if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) { + diagnostics.push(diagnostic); + } + + diagnostics } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.initializer.type_check(symbol_table)?; - let initializer_type_info = self.initializer.type_info(); - let variable_symbol = - symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name); - variable_symbol - .borrow_mut() - .set_type_info(initializer_type_info.clone()); + pub fn analyze_method_local_names( + &self, + symbol_table: &mut SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + let mut diagnostics = Vec::new(); + diagnostics.append( + &mut self + .initializer + .check_method_local_names(symbol_table, class_symbol), + ); + if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) { + diagnostics.push(diagnostic); + } + diagnostics + } + + pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec { + let mut diagnostics = Vec::new(); + diagnostics.append(&mut self.initializer.check_static_fn_local_names(symbol_table)); + if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) { + diagnostics.push(diagnostic); + } + diagnostics + } + + pub fn gather_local_type(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) { + let initializer_type_info = self + .initializer + .type_info(symbol_table, types_table) + .clone(); + let variable_symbol = symbol_table + .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) + .unwrap(); + types_table + .variable_types_mut() + .insert(variable_symbol, initializer_type_info); + } + + pub fn type_check( + &mut self, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) -> Result<(), Vec> { + self.initializer.type_check(symbol_table, types_table)?; + let initializer_type_info = self.initializer.type_info(symbol_table, types_table); + let variable_symbol = symbol_table + .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) + .unwrap(); Ok(()) } - pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { - let init_operation = self.initializer.to_ir_operation(builder, symbol_table); + pub fn to_ir( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) { + let init_operation = self + .initializer + .to_ir_operation(builder, symbol_table, types_table); + + let destination_symbol = symbol_table + .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) + .unwrap(); + + let destination_type = types_table + .variable_types() + .get(&destination_symbol) + .unwrap(); - let destination_symbol = - symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name); let destination_vr_variable = IrVariable::new_vr( self.declared_name().into(), builder.current_block().id(), - self.initializer.type_info(), + destination_type, ); + let as_rc = Rc::new(RefCell::new(destination_vr_variable)); let ir_assign = IrAssign::new(as_rc.clone(), init_operation); - destination_symbol.borrow_mut().set_vr_variable(as_rc); + + builder + .local_variables_mut() + .insert(destination_symbol, as_rc.clone()); builder .current_block_mut() diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index c7327cc..6f2a304 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -4,6 +4,7 @@ pub mod call; pub mod class; pub mod compilation_unit; pub mod constructor; +mod diagnostic_factories; pub mod double_literal; pub mod expression; pub mod expression_statement; diff --git a/dmc-lib/src/ast/negative_expression.rs b/dmc-lib/src/ast/negative_expression.rs index d3de26a..46dcdcf 100644 --- a/dmc-lib/src/ast/negative_expression.rs +++ b/dmc-lib/src/ast/negative_expression.rs @@ -8,8 +8,10 @@ use crate::ir::ir_operation::IrOperation; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; use std::cell::RefCell; use std::rc::Rc; @@ -40,21 +42,49 @@ impl NegativeExpression { &mut self.operand } - pub fn gather_declared_names( + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.operand.init_scopes(symbol_table, container_scope); + } + + pub fn check_field_initializer_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + self.operand + .check_field_initializer_names(symbol_table, class_symbol) + } + + pub fn check_constructor_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + self.operand + .check_constructor_local_names(symbol_table, class_symbol) + } + + pub fn check_method_local_names( + &self, + symbol_table: &SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + self.operand + .check_method_local_names(symbol_table, class_symbol) + } + + pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec { + self.operand.check_static_fn_local_names(symbol_table) + } + + pub fn type_check( &mut self, - symbol_table: &mut SymbolTable, + symbol_table: &SymbolTable, + types_table: &TypesTable, ) -> Result<(), Vec> { - self.operand.gather_declared_names(symbol_table) - } + self.operand.type_check(symbol_table, types_table)?; - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.operand.check_name_usages(symbol_table) - } - - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.operand.type_check(symbol_table)?; - - let type_info = self.operand.type_info(); + let type_info = self.operand.type_info(symbol_table, types_table); if type_info.can_negate() { self.type_info = Some(type_info.negate_result()); Ok(()) @@ -67,10 +97,15 @@ impl NegativeExpression { } } - pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrExpression { + pub fn to_ir( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + types_table: &TypesTable, + ) -> IrExpression { let operand_as_ir = self .operand - .to_ir_expression(builder, symbol_table) + .to_ir_expression(builder, symbol_table, types_table) .expect("Attempt to negate non-value expression"); match operand_as_ir { diff --git a/dmc-lib/src/ast/parameter.rs b/dmc-lib/src/ast/parameter.rs index 1c7e535..9fcb18b 100644 --- a/dmc-lib/src/ast/parameter.rs +++ b/dmc-lib/src/ast/parameter.rs @@ -1,17 +1,15 @@ use crate::ast::type_use::TypeUse; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; -use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::parameter_symbol::ParameterSymbol; -use crate::symbol_table::{SymbolInsertError, SymbolTable}; -use std::cell::RefCell; +use crate::symbol_table::SymbolTable; use std::rc::Rc; pub struct Parameter { declared_name: Rc, declared_name_source_range: SourceRange, type_use: TypeUse, - parameter_symbol: Option>>, + scope_id: Option, } impl Parameter { @@ -24,71 +22,37 @@ impl Parameter { declared_name: declared_name.into(), declared_name_source_range, type_use, - parameter_symbol: None, + scope_id: None, } } - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result>, Vec> { - // insert parameter symbol - let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new( + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn scope_id(&self) -> usize { + self.scope_id.unwrap() + } + + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); + self.type_use.init_scopes(symbol_table, container_scope); + } + + pub fn make_symbol(&self) -> ParameterSymbol { + ParameterSymbol::new( &self.declared_name, self.declared_name_source_range.clone(), - )); - match insert_result { - Ok(parameter_symbol) => { - self.parameter_symbol = Some(parameter_symbol.clone()); - } - Err(symbol_insert_error) => { - return match symbol_insert_error { - SymbolInsertError::AlreadyDeclared(already_declared) => { - Err(vec![Diagnostic::new( - &format!( - "Parameter {} already declared.", - already_declared.symbol().borrow().declared_name() - ), - self.declared_name_source_range.start(), - self.declared_name_source_range.end(), - )]) - } - }; - } - } - - // type use - self.type_use.gather_declared_names(symbol_table)?; - - Ok(self.parameter_symbol.clone().unwrap()) + self.scope_id.unwrap(), + ) } - pub fn check_name_usages( - &mut self, - symbol_table: &SymbolTable, - class_context: Option<&Rc>>, - ) -> Result<(), Vec> { - // check the type use - self.type_use - .check_name_usages(symbol_table, class_context)?; - - // set type on parameter symbol - self.parameter_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_type_info(self.type_use.type_info().clone()); - Ok(()) + pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { + self.type_use.check_names(symbol_table) } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { self.type_use.type_check(symbol_table)?; Ok(()) } - - pub fn parameter_symbol(&self) -> &Rc> { - self.parameter_symbol - .as_ref() - .expect("parameter symbol not initialized") - } } diff --git a/dmc-lib/src/ast/statement.rs b/dmc-lib/src/ast/statement.rs index 59ebafc..7c65a2c 100644 --- a/dmc-lib/src/ast/statement.rs +++ b/dmc-lib/src/ast/statement.rs @@ -3,8 +3,10 @@ use crate::ast::expression_statement::ExpressionStatement; use crate::ast::ir_builder::IrBuilder; use crate::ast::let_statement::LetStatement; use crate::diagnostic::Diagnostic; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::types_table::TypesTable; pub enum Statement { Let(LetStatement), @@ -13,42 +15,84 @@ pub enum Statement { } impl Statement { - pub fn gather_declared_names( - &mut self, - symbol_table: &mut SymbolTable, - ) -> Result<(), Vec> { + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { match self { - Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table), + Statement::Let(let_statement) => { + let_statement.init_scopes(symbol_table, container_scope); + } Statement::Expression(expression_statement) => { - expression_statement.gather_declared_names(symbol_table) + expression_statement.init_scopes(symbol_table, container_scope); } Statement::Assign(assign_statement) => { - assign_statement.gather_declared_names(symbol_table) + assign_statement.init_scopes(symbol_table, container_scope); } } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + pub fn analyze_constructor_local_names( + &self, + symbol_table: &mut SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { match self { - Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table), - Statement::Expression(expression_statement) => { - expression_statement.check_name_usages(symbol_table) + Statement::Let(let_statement) => { + let_statement.analyze_constructor_local_names(symbol_table, class_symbol) + } + Statement::Expression(expression_statement) => { + expression_statement.check_constructor_local_names(symbol_table, class_symbol) + } + Statement::Assign(assign_statement) => { + assign_statement.check_constructor_local_names(symbol_table, class_symbol) + } + } + } + + pub fn analyze_method_local_names( + &self, + symbol_table: &mut SymbolTable, + class_symbol: &ClassSymbol, + ) -> Vec { + match self { + Statement::Let(let_statement) => { + let_statement.analyze_method_local_names(symbol_table, class_symbol) + } + Statement::Expression(expression_statement) => { + expression_statement.check_method_local_names(symbol_table, class_symbol) + } + Statement::Assign(assign_statement) => { + assign_statement.check_method_local_names(symbol_table, class_symbol) + } + } + } + + pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec { + match self { + Statement::Let(let_statement) => { + let_statement.analyze_static_fn_local_names(symbol_table) + } + Statement::Expression(expression_statement) => { + expression_statement.check_static_fn_local_names(symbol_table) + } + Statement::Assign(assign_statement) => { + assign_statement.check_static_fn_local_names(symbol_table) } - Statement::Assign(assign_statement) => assign_statement.check_name_usages(symbol_table), } } pub fn type_check( &mut self, symbol_table: &SymbolTable, + types_table: &mut TypesTable, must_return_type_info: Option<&TypeInfo>, ) -> Result<(), Vec> { match self { - Statement::Let(let_statement) => let_statement.type_check(symbol_table), + Statement::Let(let_statement) => let_statement.type_check(symbol_table, types_table), Statement::Expression(expression_statement) => { - expression_statement.type_check(symbol_table, must_return_type_info) + expression_statement.type_check(symbol_table, types_table, must_return_type_info) + } + Statement::Assign(assign_statement) => { + assign_statement.type_check(symbol_table, types_table) } - Statement::Assign(assign_statement) => assign_statement.type_check(symbol_table), } } @@ -56,17 +100,18 @@ impl Statement { &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, + types_table: &TypesTable, should_return_value: bool, ) { match self { Statement::Let(let_statement) => { - let_statement.to_ir(builder, symbol_table); + let_statement.to_ir(builder, symbol_table, types_table); } Statement::Expression(expression_statement) => { - expression_statement.to_ir(builder, symbol_table, should_return_value); + expression_statement.to_ir(builder, symbol_table, types_table, should_return_value); } Statement::Assign(assign_statement) => { - assign_statement.to_ir(builder, symbol_table); + assign_statement.to_ir(builder, symbol_table, types_table); } } } diff --git a/dmc-lib/src/ast/type_use.rs b/dmc-lib/src/ast/type_use.rs index 58b0655..266bea2 100644 --- a/dmc-lib/src/ast/type_use.rs +++ b/dmc-lib/src/ast/type_use.rs @@ -1,3 +1,4 @@ +use crate::ast::diagnostic_factories::symbol_not_found; use crate::diagnostic::Diagnostic; use crate::error_codes::INCORRECT_GENERIC_ARGUMENTS; use crate::source_range::SourceRange; @@ -9,7 +10,7 @@ use std::cell::RefCell; use std::rc::Rc; pub struct TypeUse { - declared_name: String, + declared_name: Rc, declared_name_source_range: SourceRange, generic_arguments: Vec, scope_id: Option, @@ -35,6 +36,34 @@ impl TypeUse { &self.declared_name } + pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { + self.scope_id = Some(container_scope); + for type_use in &mut self.generic_arguments { + type_use.init_scopes(symbol_table, container_scope); + } + } + + pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { + let mut diagnostics: Vec = Vec::new(); + + // find this name + let maybe_type_symbol = + symbol_table.find_type_symbol(self.scope_id.unwrap(), &self.declared_name); + if maybe_type_symbol.is_none() { + diagnostics.push(symbol_not_found( + self.declared_name(), + &self.declared_name_source_range, + )); + } + + // check generic args + for type_use in &self.generic_arguments { + type_use.check_names(symbol_table); + } + + diagnostics + } + pub fn gather_declared_names( &mut self, symbol_table: &mut SymbolTable, @@ -53,7 +82,7 @@ impl TypeUse { pub fn check_name_usages( &mut self, symbol_table: &SymbolTable, - class_context: Option<&Rc>>, + class_context: Option<&Rc>, ) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; @@ -90,8 +119,7 @@ impl TypeUse { match self.type_info() { TypeInfo::ClassInstance(class_symbol) => { // check number of params/args match - let borrowed_class_symbol = class_symbol.borrow(); - let generic_parameters = borrowed_class_symbol.generic_parameters(); + let generic_parameters = class_symbol.generic_parameters(); if generic_parameters.len() != self.generic_arguments.len() { let diagnostic = Diagnostic::new( &format!( @@ -111,7 +139,7 @@ impl TypeUse { // check that each arg is assignable to the param's extends for i in 0..self.generic_arguments.len() { - let generic_parameter_symbol = generic_parameters[i].borrow(); + let generic_parameter_symbol = &generic_parameters[i]; if generic_parameter_symbol.extends().len() > 0 { unimplemented!("Generic extends not implemented yet.") } @@ -153,6 +181,7 @@ mod tests { use crate::diagnostic::Diagnostic; use crate::parser::parse_compilation_unit; use crate::symbol_table::SymbolTable; + use crate::types_table::TypesTable; #[test] fn type_check_generics() -> Result<(), Vec> { @@ -167,10 +196,12 @@ mod tests { )?; let mut symbol_table = SymbolTable::new(); + let mut types_table = TypesTable::new(); - compilation_unit.gather_declared_names(&mut symbol_table)?; - compilation_unit.check_name_usages(&mut symbol_table)?; - compilation_unit.type_check(&mut symbol_table)?; + compilation_unit.init_scopes(&mut symbol_table); + compilation_unit.gather_symbols_into(&mut symbol_table)?; + compilation_unit.check_names(&mut symbol_table)?; + compilation_unit.type_check(&mut symbol_table, &mut types_table)?; Ok(()) } diff --git a/dmc-lib/src/error_codes.rs b/dmc-lib/src/error_codes.rs index 65c7dd1..9ccd8f4 100644 --- a/dmc-lib/src/error_codes.rs +++ b/dmc-lib/src/error_codes.rs @@ -1,5 +1,6 @@ pub type ErrorCode = usize; +pub const SYMBOL_NOT_FOUND: ErrorCode = 13; pub const SYMBOL_ALREADY_DECLARED: ErrorCode = 14; pub const BINARY_INCOMPATIBLE_TYPES: ErrorCode = 15; pub const ASSIGN_MISMATCHED_TYPES: ErrorCode = 16; @@ -9,3 +10,8 @@ pub const INCORRECT_GENERIC_ARGUMENTS: ErrorCode = 19; pub const GENERIC_ARGUMENT_TYPE_MISMATCH: ErrorCode = 20; pub const FIELD_MULTIPLE_INIT: ErrorCode = 21; pub const FIELD_UNINIT: ErrorCode = 22; +pub const SELF_FIELD_USED_IN_INIT: ErrorCode = 23; +pub const SELF_METHOD_USED_IN_INIT: ErrorCode = 24; +pub const SELF_CONSTRUCTOR_USED_IN_INIT: ErrorCode = 25; +pub const OUTER_CLASS_FIELD_USED_IN_INIT: ErrorCode = 26; +pub const OUTER_CLASS_METHOD_USED_IN_INIT: ErrorCode = 27; diff --git a/dmc-lib/src/ir/ir_block.rs b/dmc-lib/src/ir/ir_block.rs index 937cb52..eb6bbf7 100644 --- a/dmc-lib/src/ir/ir_block.rs +++ b/dmc-lib/src/ir/ir_block.rs @@ -121,11 +121,13 @@ impl Display for IrBlock { #[cfg(test)] mod tests { + use crate::diagnostic::Diagnostic; use crate::parser::parse_compilation_unit; use crate::symbol_table::SymbolTable; + use crate::types_table::TypesTable; #[test] - fn overlapping_assignments_bug_when_k_2() { + fn overlapping_assignments_bug_when_k_2() -> Result<(), Vec> { let mut compilation_unit = parse_compilation_unit( " fn main() @@ -138,15 +140,12 @@ mod tests { ) .unwrap(); let mut symbol_table = SymbolTable::new(); - compilation_unit - .gather_declared_names(&mut symbol_table) - .expect("gather failed"); - compilation_unit - .check_name_usages(&mut symbol_table) - .expect("name check failed"); - compilation_unit - .type_check(&mut symbol_table) - .expect("type check failed"); + let mut types_table = TypesTable::new(); + + compilation_unit.init_scopes(&mut symbol_table); + compilation_unit.gather_symbols_into(&mut symbol_table)?; + compilation_unit.check_names(&mut symbol_table)?; + compilation_unit.type_check(&mut symbol_table, &mut types_table)?; let main = compilation_unit .functions() @@ -154,8 +153,10 @@ mod tests { .find(|f| f.declared_name() == "main") .unwrap(); - let mut main_ir = main.to_ir(&symbol_table, None); + let mut main_ir = main.to_ir(&symbol_table, &types_table, None); let (register_assignments, _) = main_ir.assign_registers(2); assert_eq!(register_assignments.len(), 4); + + Ok(()) } } diff --git a/dmc-lib/src/ir/ir_class.rs b/dmc-lib/src/ir/ir_class.rs index 3c4b286..bedba4c 100644 --- a/dmc-lib/src/ir/ir_class.rs +++ b/dmc-lib/src/ir/ir_class.rs @@ -51,9 +51,9 @@ impl IrField { TypeInfo::Integer => VmTypeInfo::Int, TypeInfo::Double => VmTypeInfo::Double, TypeInfo::String => VmTypeInfo::String, - TypeInfo::ClassInstance(class_symbol) => VmTypeInfo::ClassInstance( - fqn_parts_to_string(class_symbol.borrow().fqn_parts()).into(), - ), + TypeInfo::ClassInstance(class_symbol) => { + VmTypeInfo::ClassInstance(fqn_parts_to_string(class_symbol.fqn_parts()).into()) + } TypeInfo::GenericType(_) => VmTypeInfo::Any, _ => panic!(), }, diff --git a/dmc-lib/src/ir/ir_function.rs b/dmc-lib/src/ir/ir_function.rs index abb5d77..3482809 100644 --- a/dmc-lib/src/ir/ir_function.rs +++ b/dmc-lib/src/ir/ir_function.rs @@ -21,7 +21,7 @@ pub struct IrFunction { impl IrFunction { pub fn new( fqn: Rc, - parameters: &[Rc], + parameters: Vec>, return_type_info: &TypeInfo, entry: Rc>, ) -> Self { diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index 32662e0..0137943 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -5,9 +5,10 @@ pub mod error_codes; pub mod ir; pub mod lexer; pub mod parser; -mod scope; +pub mod scope; pub mod source_range; pub mod symbol; pub mod symbol_table; pub mod token; pub mod type_info; +pub mod types_table; diff --git a/dmc-lib/src/scope/block_scope.rs b/dmc-lib/src/scope/block_scope.rs index 4c0a40f..c2fce2c 100644 --- a/dmc-lib/src/scope/block_scope.rs +++ b/dmc-lib/src/scope/block_scope.rs @@ -1,12 +1,11 @@ use crate::symbol::variable_symbol::VariableSymbol; -use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; pub struct BlockScope { debug_name: String, parent_id: usize, - variable_symbols: HashMap, Rc>>, + variable_symbols: HashMap, Rc>, } impl BlockScope { @@ -18,11 +17,11 @@ impl BlockScope { } } - pub fn variable_symbols(&self) -> &HashMap, Rc>> { + pub fn variable_symbols(&self) -> &HashMap, Rc> { &self.variable_symbols } - pub fn variable_symbols_mut(&mut self) -> &mut HashMap, Rc>> { + pub fn variable_symbols_mut(&mut self) -> &mut HashMap, Rc> { &mut self.variable_symbols } diff --git a/dmc-lib/src/scope/class_body_scope.rs b/dmc-lib/src/scope/class_body_scope.rs new file mode 100644 index 0000000..8a0d509 --- /dev/null +++ b/dmc-lib/src/scope/class_body_scope.rs @@ -0,0 +1,64 @@ +use crate::symbol::class_symbol::ClassSymbol; +use crate::symbol::constructor_symbol::ConstructorSymbol; +use crate::symbol::field_symbol::FieldSymbol; +use crate::symbol::function_symbol::FunctionSymbol; +use std::collections::HashMap; +use std::rc::Rc; + +pub struct ClassBodyScope { + debug_name: String, + parent_id: usize, + class_symbols: HashMap, Rc>, + field_symbols: HashMap, Rc>, + function_symbols: HashMap, Rc>, + constructor_symbol: Option>, +} + +impl ClassBodyScope { + pub fn new(debug_name: &str, parent_id: usize) -> Self { + Self { + debug_name: debug_name.into(), + parent_id, + class_symbols: HashMap::new(), + field_symbols: HashMap::new(), + function_symbols: HashMap::new(), + constructor_symbol: None, + } + } + + pub fn class_symbols(&self) -> &HashMap, Rc> { + &self.class_symbols + } + + pub fn class_symbols_mut(&mut self) -> &mut HashMap, Rc> { + &mut self.class_symbols + } + + pub fn field_symbols(&self) -> &HashMap, Rc> { + &self.field_symbols + } + + pub fn field_symbols_mut(&mut self) -> &mut HashMap, Rc> { + &mut self.field_symbols + } + + pub fn function_symbols(&self) -> &HashMap, Rc> { + &self.function_symbols + } + + pub fn function_symbols_mut(&mut self) -> &mut HashMap, Rc> { + &mut self.function_symbols + } + + pub fn constructor_symbol(&self) -> Option<&Rc> { + self.constructor_symbol.as_ref() + } + + pub fn constructor_symbol_mut(&mut self) -> &mut Option> { + &mut self.constructor_symbol + } + + pub fn parent_id(&self) -> usize { + self.parent_id + } +} diff --git a/dmc-lib/src/scope/class_scope.rs b/dmc-lib/src/scope/class_scope.rs index f83552c..75ae003 100644 --- a/dmc-lib/src/scope/class_scope.rs +++ b/dmc-lib/src/scope/class_scope.rs @@ -1,20 +1,11 @@ -use crate::symbol::class_symbol::ClassSymbol; -use crate::symbol::constructor_symbol::ConstructorSymbol; -use crate::symbol::field_symbol::FieldSymbol; -use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; -use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; pub struct ClassScope { debug_name: String, parent_id: usize, - generic_parameter_symbols: HashMap, Rc>>, - class_symbols: HashMap, Rc>>, - field_symbols: HashMap, Rc>>, - function_symbols: HashMap, Rc>>, - constructor_symbol: Option>>, + generic_parameter_symbols: HashMap, Rc>, } impl ClassScope { @@ -23,58 +14,20 @@ impl ClassScope { debug_name: debug_name.into(), parent_id, generic_parameter_symbols: HashMap::new(), - class_symbols: HashMap::new(), - field_symbols: HashMap::new(), - function_symbols: HashMap::new(), - constructor_symbol: None, } } - pub fn generic_parameter_symbols( - &self, - ) -> &HashMap, Rc>> { - &self.generic_parameter_symbols - } - - pub fn generic_parameter_symbols_mut( - &mut self, - ) -> &mut HashMap, Rc>> { - &mut self.generic_parameter_symbols - } - - pub fn class_symbols(&self) -> &HashMap, Rc>> { - &self.class_symbols - } - - pub fn class_symbols_mut(&mut self) -> &mut HashMap, Rc>> { - &mut self.class_symbols - } - - pub fn field_symbols(&self) -> &HashMap, Rc>> { - &self.field_symbols - } - - pub fn field_symbols_mut(&mut self) -> &mut HashMap, Rc>> { - &mut self.field_symbols - } - - pub fn function_symbols(&self) -> &HashMap, Rc>> { - &self.function_symbols - } - - pub fn function_symbols_mut(&mut self) -> &mut HashMap, Rc>> { - &mut self.function_symbols - } - - pub fn constructor_symbol(&self) -> Option<&Rc>> { - self.constructor_symbol.as_ref() - } - - pub fn constructor_symbol_mut(&mut self) -> &mut Option>> { - &mut self.constructor_symbol - } - pub fn parent_id(&self) -> usize { self.parent_id } + + pub fn generic_parameter_symbols(&self) -> &HashMap, Rc> { + &self.generic_parameter_symbols + } + + pub fn generic_parameter_symbols_mut( + &mut self, + ) -> &mut HashMap, Rc> { + &mut self.generic_parameter_symbols + } } diff --git a/dmc-lib/src/scope/function_scope.rs b/dmc-lib/src/scope/function_scope.rs index 0090041..1ea73b1 100644 --- a/dmc-lib/src/scope/function_scope.rs +++ b/dmc-lib/src/scope/function_scope.rs @@ -1,12 +1,11 @@ use crate::symbol::parameter_symbol::ParameterSymbol; -use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; pub struct FunctionScope { debug_name: String, parent_id: usize, - parameter_symbols: HashMap, Rc>>, + parameter_symbols: HashMap, Rc>, } impl FunctionScope { @@ -18,11 +17,11 @@ impl FunctionScope { } } - pub fn parameter_symbols(&self) -> &HashMap, Rc>> { + pub fn parameter_symbols(&self) -> &HashMap, Rc> { &self.parameter_symbols } - pub fn parameter_symbols_mut(&mut self) -> &mut HashMap, Rc>> { + pub fn parameter_symbols_mut(&mut self) -> &mut HashMap, Rc> { &mut self.parameter_symbols } diff --git a/dmc-lib/src/scope/mod.rs b/dmc-lib/src/scope/mod.rs index d0fa25d..390c98a 100644 --- a/dmc-lib/src/scope/mod.rs +++ b/dmc-lib/src/scope/mod.rs @@ -1,9 +1,11 @@ use crate::scope::block_scope::BlockScope; +use crate::scope::class_body_scope::ClassBodyScope; use crate::scope::class_scope::ClassScope; use crate::scope::function_scope::FunctionScope; use crate::scope::module_scope::ModuleScope; pub mod block_scope; +pub mod class_body_scope; pub mod class_scope; pub mod function_scope; pub mod module_scope; @@ -11,6 +13,7 @@ pub mod module_scope; pub enum Scope { Module(ModuleScope), Class(ClassScope), + ClassBody(ClassBodyScope), Function(FunctionScope), Block(BlockScope), } @@ -20,6 +23,7 @@ impl Scope { match self { Scope::Module(module_scope) => module_scope.parent_id(), Scope::Class(class_scope) => Some(class_scope.parent_id()), + Scope::ClassBody(class_body_scope) => Some(class_body_scope.parent_id()), Scope::Function(function_scope) => Some(function_scope.parent_id()), Scope::Block(block_scope) => Some(block_scope.parent_id()), } diff --git a/dmc-lib/src/scope/module_scope.rs b/dmc-lib/src/scope/module_scope.rs index 0d9644f..72df133 100644 --- a/dmc-lib/src/scope/module_scope.rs +++ b/dmc-lib/src/scope/module_scope.rs @@ -1,14 +1,13 @@ use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::function_symbol::FunctionSymbol; -use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; pub struct ModuleScope { debug_name: String, parent_id: Option, - class_symbols: HashMap, Rc>>, - function_symbols: HashMap, Rc>>, + class_symbols: HashMap, Rc>, + function_symbols: HashMap, Rc>, } impl ModuleScope { @@ -21,19 +20,19 @@ impl ModuleScope { } } - pub fn class_symbols(&self) -> &HashMap, Rc>> { + pub fn class_symbols(&self) -> &HashMap, Rc> { &self.class_symbols } - pub fn class_symbols_mut(&mut self) -> &mut HashMap, Rc>> { + pub fn class_symbols_mut(&mut self) -> &mut HashMap, Rc> { &mut self.class_symbols } - pub fn function_symbols(&self) -> &HashMap, Rc>> { + pub fn function_symbols(&self) -> &HashMap, Rc> { &self.function_symbols } - pub fn function_symbols_mut(&mut self) -> &mut HashMap, Rc>> { + pub fn function_symbols_mut(&mut self) -> &mut HashMap, Rc> { &mut self.function_symbols } diff --git a/dmc-lib/src/symbol/callable_symbol.rs b/dmc-lib/src/symbol/callable_symbol.rs index 9fa0bfd..799c69a 100644 --- a/dmc-lib/src/symbol/callable_symbol.rs +++ b/dmc-lib/src/symbol/callable_symbol.rs @@ -1,34 +1,19 @@ use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::parameter_symbol::ParameterSymbol; -use crate::type_info::TypeInfo; -use std::cell::RefCell; use std::rc::Rc; pub enum CallableSymbol { - Function(Rc>), - Class(Rc>), + Function(Rc), + Class(Rc), } impl CallableSymbol { - pub fn return_type_info(&self) -> TypeInfo { + pub fn parameters(&self) -> Vec> { match self { - CallableSymbol::Function(function) => function.borrow().return_type_info().clone(), - CallableSymbol::Class(class_symbol) => TypeInfo::ClassInstance(class_symbol.clone()), - } - } - - pub fn parameters(&self) -> Vec>> { - match self { - CallableSymbol::Function(function_symbol) => { - function_symbol.borrow().parameters().to_vec() - } + CallableSymbol::Function(function_symbol) => function_symbol.parameters().to_vec(), CallableSymbol::Class(class_symbol) => { - if let Some(constructor_symbol) = class_symbol.borrow().constructor_symbol() { - constructor_symbol.borrow().parameters().to_vec() - } else { - vec![] - } + class_symbol.constructor_symbol().parameters().to_vec() } } } diff --git a/dmc-lib/src/symbol/class_symbol.rs b/dmc-lib/src/symbol/class_symbol.rs index c41a989..bef2bb0 100644 --- a/dmc-lib/src/symbol/class_symbol.rs +++ b/dmc-lib/src/symbol/class_symbol.rs @@ -1,11 +1,10 @@ use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::symbol::constructor_symbol::ConstructorSymbol; use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; -use std::cell::RefCell; use std::collections::HashMap; +use std::hash::{Hash, Hasher}; use std::rc::Rc; pub struct ClassSymbol { @@ -13,74 +12,92 @@ pub struct ClassSymbol { declared_name_source_range: SourceRange, fqn_parts: Vec>, is_extern: bool, - generic_parameters: Vec>>, - constructor_symbol: Option>>, - fields: HashMap, Rc>>, - functions: HashMap, Rc>>, + scope_id: usize, + generic_parameters: Vec>, + constructor_symbol: Rc, + fields: HashMap, Rc>, + functions: HashMap, Rc>, } impl ClassSymbol { pub fn new( declared_name: &Rc, - declared_name_source_range: SourceRange, + declared_name_source_range: &SourceRange, fqn_parts: Vec>, is_extern: bool, + scope_id: usize, + generic_parameters: Vec>, + constructor_symbol: Rc, + fields: Vec>, + functions: Vec>, ) -> Self { Self { declared_name: declared_name.clone(), - declared_name_source_range, + declared_name_source_range: declared_name_source_range.clone(), fqn_parts, is_extern, - generic_parameters: vec![], - constructor_symbol: None, - fields: HashMap::new(), - functions: HashMap::new(), + scope_id, + generic_parameters, + constructor_symbol, + fields: fields + .into_iter() + .map(|fs| (fs.declared_name_owned(), fs)) + .collect(), + functions: functions + .into_iter() + .map(|fs| (fs.declared_name_owned(), fs)) + .collect(), } } + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + + pub fn declared_name_source_range(&self) -> &SourceRange { + &self.declared_name_source_range + } + pub fn fqn_parts(&self) -> &[Rc] { &self.fqn_parts } - pub fn set_generic_parameters( - &mut self, - generic_parameters: Vec>>, - ) { - self.generic_parameters = generic_parameters; + pub fn scope_id(&self) -> usize { + self.scope_id } - pub fn generic_parameters(&self) -> &[Rc>] { + pub fn generic_parameters(&self) -> &[Rc] { &self.generic_parameters } - pub fn set_constructor_symbol(&mut self, constructor_symbol: Rc>) { - self.constructor_symbol = Some(constructor_symbol); + pub fn constructor_symbol(&self) -> &ConstructorSymbol { + &self.constructor_symbol } - pub fn set_fields(&mut self, fields: HashMap, Rc>>) { - self.fields = fields; - } - - pub fn fields(&self) -> &HashMap, Rc>> { + pub fn fields(&self) -> &HashMap, Rc> { &self.fields } - /// A value of `None` indicates that there is no declared constructor for this class. - pub fn constructor_symbol(&self) -> Option<&Rc>> { - self.constructor_symbol.as_ref() + pub fn functions(&self) -> &HashMap, Rc> { + &self.functions } } -impl Symbol for ClassSymbol { - fn declared_name(&self) -> &str { - &self.declared_name - } +impl Eq for ClassSymbol {} - fn declared_name_owned(&self) -> Rc { - self.declared_name.clone() - } - - fn declared_name_source_range(&self) -> &SourceRange { - &self.declared_name_source_range +impl PartialEq for ClassSymbol { + fn eq(&self, other: &Self) -> bool { + self.declared_name == other.declared_name && self.scope_id == other.scope_id + } +} + +impl Hash for ClassSymbol { + fn hash(&self, state: &mut H) { + self.declared_name.hash(state); + self.scope_id.hash(state); } } diff --git a/dmc-lib/src/symbol/constructor_symbol.rs b/dmc-lib/src/symbol/constructor_symbol.rs index ec4e353..33b5129 100644 --- a/dmc-lib/src/symbol/constructor_symbol.rs +++ b/dmc-lib/src/symbol/constructor_symbol.rs @@ -1,7 +1,6 @@ use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::symbol::parameter_symbol::ParameterSymbol; -use std::cell::RefCell; +use std::hash::{Hash, Hasher}; use std::rc::Rc; pub struct ConstructorSymbol { @@ -9,35 +8,51 @@ pub struct ConstructorSymbol { fqn_parts: Vec>, is_extern: bool, is_default: bool, - parameters: Option>>>, + scope_id: usize, + parameters: Vec>, } impl ConstructorSymbol { pub fn new( - keyword_source_range: SourceRange, + keyword_source_range: &SourceRange, fqn_parts: Vec>, is_extern: bool, is_default: bool, + scope_id: usize, + parameters: Vec>, ) -> Self { Self { - keyword_source_range, + keyword_source_range: keyword_source_range.clone(), fqn_parts, is_extern, is_default, - parameters: None, + scope_id, + parameters, } } + pub fn declared_name(&self) -> &str { + "ctor" + } + + pub fn declared_name_owned(&self) -> Rc { + Rc::from(self.declared_name()) + } + + pub fn declared_name_source_range(&self) -> &SourceRange { + &self.keyword_source_range + } + pub fn fqn_parts(&self) -> &[Rc] { &self.fqn_parts } - pub fn set_parameters(&mut self, parameters: Vec>>) { - self.parameters = Some(parameters); + pub fn scope_id(&self) -> usize { + self.scope_id } - pub fn parameters(&self) -> &[Rc>] { - self.parameters.as_ref().unwrap() + pub fn parameters(&self) -> &[Rc] { + &self.parameters } pub fn is_extern(&self) -> bool { @@ -45,16 +60,16 @@ impl ConstructorSymbol { } } -impl Symbol for ConstructorSymbol { - fn declared_name(&self) -> &str { - "ctor" - } +impl Eq for ConstructorSymbol {} - fn declared_name_owned(&self) -> Rc { - Rc::from(self.declared_name()) - } - - fn declared_name_source_range(&self) -> &SourceRange { - &self.keyword_source_range +impl PartialEq for ConstructorSymbol { + fn eq(&self, other: &Self) -> bool { + self.scope_id == other.scope_id + } +} + +impl Hash for ConstructorSymbol { + fn hash(&self, state: &mut H) { + self.scope_id.hash(state); } } diff --git a/dmc-lib/src/symbol/expressible_symbol.rs b/dmc-lib/src/symbol/expressible_symbol.rs index c1c5aec..6c05412 100644 --- a/dmc-lib/src/symbol/expressible_symbol.rs +++ b/dmc-lib/src/symbol/expressible_symbol.rs @@ -1,111 +1,36 @@ -use crate::ast::ir_builder::IrBuilder; -use crate::ast::ir_util::get_or_init_field_pointer_variable; -use crate::ir::ir_assign::IrAssign; -use crate::ir::ir_expression::IrExpression; -use crate::ir::ir_operation::IrOperation; -use crate::ir::ir_read_field::IrReadField; -use crate::ir::ir_statement::IrStatement; -use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::variable_symbol::VariableSymbol; -use crate::type_info::TypeInfo; -use std::cell::RefCell; use std::rc::Rc; pub enum ExpressibleSymbol { - Class(Rc>), - Field(Rc>), - Function(Rc>), - Parameter(Rc>), - Variable(Rc>), + Class(Rc), + Field(Rc), + Function(Rc), + Parameter(Rc), + Variable(Rc), } impl ExpressibleSymbol { - pub fn type_info(&self) -> TypeInfo { - match self { - ExpressibleSymbol::Class(class_symbol) => TypeInfo::ClassInstance(class_symbol.clone()), - ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().type_info().clone(), - ExpressibleSymbol::Function(function_symbol) => { - TypeInfo::Function(function_symbol.clone()) // n.b. not the return type! - } - ExpressibleSymbol::Parameter(parameter_symbol) => { - parameter_symbol.borrow().type_info().clone() - } - ExpressibleSymbol::Variable(variable_symbol) => { - variable_symbol.borrow().type_info().clone() - } - } - } - - pub fn is_mut(&self) -> bool { - match self { - ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().is_mut(), - ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.borrow().is_mut(), - _ => false, - } - } - pub fn source_range(&self) -> SourceRange { match self { ExpressibleSymbol::Class(class_symbol) => { - class_symbol.borrow().declared_name_source_range().clone() + class_symbol.declared_name_source_range().clone() } ExpressibleSymbol::Field(field_symbol) => { - field_symbol.borrow().declared_name_source_range().clone() + field_symbol.declared_name_source_range().clone() } - ExpressibleSymbol::Function(function_symbol) => function_symbol - .borrow() - .declared_name_source_range() - .clone(), - ExpressibleSymbol::Parameter(parameter_symbol) => parameter_symbol - .borrow() - .declared_name_source_range() - .clone(), - ExpressibleSymbol::Variable(variable_symbol) => variable_symbol - .borrow() - .declared_name_source_range() - .clone(), - } - } - - pub fn ir_expression(&self, builder: &mut IrBuilder) -> IrExpression { - match self { - ExpressibleSymbol::Class(class_symbol) => { - todo!() - } - ExpressibleSymbol::Field(field_symbol) => { - // now we need to read the field into a variable and return an expression pointing - // to that variable - let read_destination = IrVariable::new_vr( - builder.new_t_var().into(), - builder.current_block().id(), - field_symbol.borrow().type_info(), - ); - let read_destination_as_rc = Rc::new(RefCell::new(read_destination)); - let ir_read_field = IrReadField::new( - get_or_init_field_pointer_variable(builder, field_symbol).clone(), - ); - builder - .current_block_mut() - .add_statement(IrStatement::Assign(IrAssign::new( - read_destination_as_rc.clone(), - IrOperation::ReadField(ir_read_field), - ))); - IrExpression::Variable(read_destination_as_rc) - } - ExpressibleSymbol::Function(_) => { - panic!("Cannot get ir_variable for FunctionSymbol"); + ExpressibleSymbol::Function(function_symbol) => { + function_symbol.declared_name_source_range().clone() } ExpressibleSymbol::Parameter(parameter_symbol) => { - IrExpression::Parameter(parameter_symbol.borrow().ir_parameter().clone()) + parameter_symbol.declared_name_source_range().clone() } ExpressibleSymbol::Variable(variable_symbol) => { - IrExpression::Variable(variable_symbol.borrow().vr_variable().clone()) + variable_symbol.declared_name_source_range().clone() } } } diff --git a/dmc-lib/src/symbol/field_symbol.rs b/dmc-lib/src/symbol/field_symbol.rs index cca7080..fd11ded 100644 --- a/dmc-lib/src/symbol/field_symbol.rs +++ b/dmc-lib/src/symbol/field_symbol.rs @@ -1,12 +1,13 @@ use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::type_info::TypeInfo; +use std::hash::{Hash, Hasher}; use std::rc::Rc; pub struct FieldSymbol { declared_name: Rc, declared_name_source_range: SourceRange, is_mut: bool, + scope_id: usize, type_info: Option, field_index: Option, } @@ -16,16 +17,34 @@ impl FieldSymbol { declared_name: &Rc, declared_name_source_range: SourceRange, is_mut: bool, + scope_id: usize, ) -> Self { Self { declared_name: declared_name.clone(), declared_name_source_range, is_mut, + scope_id, type_info: None, field_index: None, } } + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + + pub fn declared_name_source_range(&self) -> &SourceRange { + &self.declared_name_source_range + } + + pub fn scope_id(&self) -> usize { + self.scope_id + } + pub fn is_mut(&self) -> bool { self.is_mut } @@ -47,16 +66,17 @@ impl FieldSymbol { } } -impl Symbol for FieldSymbol { - fn declared_name(&self) -> &str { - &self.declared_name - } +impl Eq for FieldSymbol {} - fn declared_name_owned(&self) -> Rc { - self.declared_name.clone() - } - - fn declared_name_source_range(&self) -> &SourceRange { - &self.declared_name_source_range +impl PartialEq for FieldSymbol { + fn eq(&self, other: &Self) -> bool { + self.declared_name == other.declared_name && self.scope_id == other.scope_id + } +} + +impl Hash for FieldSymbol { + fn hash(&self, state: &mut H) { + self.declared_name.hash(state); + self.scope_id.hash(state); } } diff --git a/dmc-lib/src/symbol/function_symbol.rs b/dmc-lib/src/symbol/function_symbol.rs index 44249f0..32f2815 100644 --- a/dmc-lib/src/symbol/function_symbol.rs +++ b/dmc-lib/src/symbol/function_symbol.rs @@ -1,8 +1,6 @@ use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::symbol::parameter_symbol::ParameterSymbol; -use crate::type_info::TypeInfo; -use std::cell::RefCell; +use std::hash::Hash; use std::rc::Rc; pub struct FunctionSymbol { @@ -10,45 +8,57 @@ pub struct FunctionSymbol { declared_name_source_range: SourceRange, fqn_parts: Vec>, is_extern: bool, - parameters: Option>>>, - return_type: Option, + is_method: bool, + scope_id: usize, + parameters: Vec>, } impl FunctionSymbol { pub fn new( - declared_name: &str, + declared_name: &Rc, declared_name_source_range: SourceRange, fqn_parts: Vec>, is_extern: bool, + is_method: bool, + scope_id: usize, + parameters: Vec>, ) -> Self { Self { - declared_name: declared_name.into(), + declared_name: declared_name.clone(), declared_name_source_range, fqn_parts, is_extern, - parameters: None, - return_type: None, + is_method, + scope_id, + parameters, } } + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + + pub fn declared_name_source_range(&self) -> &SourceRange { + &self.declared_name_source_range + } + pub fn fqn_parts(&self) -> &[Rc] { &self.fqn_parts } - pub fn set_parameters(&mut self, parameters: Vec>>) { - self.parameters = Some(parameters); + pub fn is_method(&self) -> bool { + self.is_method } - pub fn parameters(&self) -> &[Rc>] { - self.parameters.as_ref().unwrap() + pub fn scope_id(&self) -> usize { + self.scope_id } - - pub fn set_return_type_info(&mut self, return_type: TypeInfo) { - self.return_type = Some(return_type); - } - - pub fn return_type_info(&self) -> &TypeInfo { - self.return_type.as_ref().unwrap() + pub fn parameters(&self) -> &[Rc] { + &self.parameters } pub fn is_extern(&self) -> bool { @@ -56,16 +66,17 @@ impl FunctionSymbol { } } -impl Symbol for FunctionSymbol { - fn declared_name(&self) -> &str { - &self.declared_name - } +impl Eq for FunctionSymbol {} - fn declared_name_owned(&self) -> Rc { - self.declared_name.clone() - } - - fn declared_name_source_range(&self) -> &SourceRange { - &self.declared_name_source_range +impl PartialEq for FunctionSymbol { + fn eq(&self, other: &Self) -> bool { + self.declared_name == other.declared_name && self.scope_id == other.scope_id + } +} + +impl Hash for FunctionSymbol { + fn hash(&self, state: &mut H) { + self.declared_name.hash(state); + self.scope_id.hash(state); } } diff --git a/dmc-lib/src/symbol/generic_parameter_symbol.rs b/dmc-lib/src/symbol/generic_parameter_symbol.rs index b5bcd56..83aa7d6 100644 --- a/dmc-lib/src/symbol/generic_parameter_symbol.rs +++ b/dmc-lib/src/symbol/generic_parameter_symbol.rs @@ -1,23 +1,45 @@ use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::type_info::TypeInfo; +use std::hash::{Hash, Hasher}; use std::rc::Rc; pub struct GenericParameterSymbol { declared_name: Rc, declared_name_source_range: SourceRange, + scope_id: usize, extends: Option>, } impl GenericParameterSymbol { - pub fn new(declared_name: &Rc, declared_name_source_range: &SourceRange) -> Self { + pub fn new( + declared_name: &Rc, + declared_name_source_range: &SourceRange, + scope_id: usize, + ) -> Self { Self { declared_name: declared_name.clone(), declared_name_source_range: declared_name_source_range.clone(), + scope_id, extends: None, } } + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + + pub fn declared_name_source_range(&self) -> &SourceRange { + &self.declared_name_source_range + } + + pub fn scope_id(&self) -> usize { + self.scope_id + } + pub fn set_extends(&mut self, extends: Vec) { self.extends = Some(extends); } @@ -27,16 +49,17 @@ impl GenericParameterSymbol { } } -impl Symbol for GenericParameterSymbol { - fn declared_name(&self) -> &str { - &self.declared_name - } +impl Eq for GenericParameterSymbol {} - fn declared_name_owned(&self) -> Rc { - self.declared_name.clone() - } - - fn declared_name_source_range(&self) -> &SourceRange { - &self.declared_name_source_range +impl PartialEq for GenericParameterSymbol { + fn eq(&self, other: &Self) -> bool { + self.declared_name == other.declared_name && self.scope_id == other.scope_id + } +} + +impl Hash for GenericParameterSymbol { + fn hash(&self, state: &mut H) { + self.declared_name.hash(state); + self.scope_id.hash(state); } } diff --git a/dmc-lib/src/symbol/mod.rs b/dmc-lib/src/symbol/mod.rs index 4925e02..7fb073f 100644 --- a/dmc-lib/src/symbol/mod.rs +++ b/dmc-lib/src/symbol/mod.rs @@ -1,4 +1,11 @@ use crate::source_range::SourceRange; +use crate::symbol::class_symbol::ClassSymbol; +use crate::symbol::constructor_symbol::ConstructorSymbol; +use crate::symbol::field_symbol::FieldSymbol; +use crate::symbol::function_symbol::FunctionSymbol; +use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; +use crate::symbol::parameter_symbol::ParameterSymbol; +use crate::symbol::variable_symbol::VariableSymbol; use std::rc::Rc; pub mod callable_symbol; @@ -12,8 +19,59 @@ pub mod parameter_symbol; pub mod type_symbol; pub mod variable_symbol; -pub trait Symbol { - fn declared_name(&self) -> &str; - fn declared_name_owned(&self) -> Rc; - fn declared_name_source_range(&self) -> &SourceRange; +#[derive(Eq, PartialEq, Hash)] +pub enum Symbol { + Class(Rc), + GenericParameter(Rc), + Field(Rc), + Constructor(Rc), + Function(Rc), + Parameter(Rc), + Variable(Rc), +} + +impl Symbol { + pub fn scope_id(&self) -> usize { + match self { + Symbol::Class(class_symbol) => class_symbol.scope_id(), + Symbol::GenericParameter(generic_parameter_symbol) => { + generic_parameter_symbol.scope_id() + } + Symbol::Field(field_symbol) => field_symbol.scope_id(), + Symbol::Constructor(constructor_symbol) => constructor_symbol.scope_id(), + Symbol::Function(function_symbol) => function_symbol.scope_id(), + Symbol::Parameter(parameter_symbol) => parameter_symbol.scope_id(), + Symbol::Variable(variable_symbol) => variable_symbol.scope_id(), + } + } + + pub fn declared_name(&self) -> &str { + match self { + Symbol::Class(class_symbol) => class_symbol.declared_name(), + Symbol::GenericParameter(generic_parameter_symbol) => { + generic_parameter_symbol.declared_name() + } + Symbol::Field(field_symbol) => field_symbol.declared_name(), + Symbol::Constructor(constructor_symbol) => constructor_symbol.declared_name(), + Symbol::Function(function_symbol) => function_symbol.declared_name(), + Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name(), + Symbol::Variable(variable_symbol) => variable_symbol.declared_name(), + } + } + + pub fn declared_name_source_range(&self) -> &SourceRange { + match self { + Symbol::Class(class_symbol) => class_symbol.declared_name_source_range(), + Symbol::GenericParameter(generic_parameter_symbol) => { + generic_parameter_symbol.declared_name_source_range() + } + Symbol::Field(field_symbol) => field_symbol.declared_name_source_range(), + Symbol::Constructor(constructor_symbol) => { + constructor_symbol.declared_name_source_range() + } + Symbol::Function(function_symbol) => function_symbol.declared_name_source_range(), + Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name_source_range(), + Symbol::Variable(variable_symbol) => variable_symbol.declared_name_source_range(), + } + } } diff --git a/dmc-lib/src/symbol/parameter_symbol.rs b/dmc-lib/src/symbol/parameter_symbol.rs index 2259e4f..daadaa8 100644 --- a/dmc-lib/src/symbol/parameter_symbol.rs +++ b/dmc-lib/src/symbol/parameter_symbol.rs @@ -1,53 +1,54 @@ -use crate::ir::ir_parameter::IrParameter; use crate::source_range::SourceRange; -use crate::symbol::Symbol; -use crate::type_info::TypeInfo; +use std::hash::{Hash, Hasher}; use std::rc::Rc; pub struct ParameterSymbol { declared_name: Rc, declared_name_source_range: SourceRange, - type_info: Option, - ir_parameter: Option>, + scope_id: usize, } impl ParameterSymbol { - pub fn new(declared_name: &Rc, declared_name_source_range: SourceRange) -> Self { + pub fn new( + declared_name: &Rc, + declared_name_source_range: SourceRange, + scope_id: usize, + ) -> Self { Self { declared_name: declared_name.clone(), declared_name_source_range, - type_info: None, - ir_parameter: None, + scope_id, } } - pub fn set_type_info(&mut self, type_info: TypeInfo) { - self.type_info = Some(type_info); - } - - pub fn type_info(&self) -> &TypeInfo { - self.type_info.as_ref().unwrap() - } - - pub fn set_ir_parameter(&mut self, ir_parameter: Rc) { - self.ir_parameter = Some(ir_parameter); - } - - pub fn ir_parameter(&self) -> &Rc { - self.ir_parameter.as_ref().unwrap() - } -} - -impl Symbol for ParameterSymbol { - fn declared_name(&self) -> &str { + pub fn declared_name(&self) -> &str { &self.declared_name } - fn declared_name_owned(&self) -> Rc { + pub fn declared_name_owned(&self) -> Rc { self.declared_name.clone() } - fn declared_name_source_range(&self) -> &SourceRange { + pub fn declared_name_source_range(&self) -> &SourceRange { &self.declared_name_source_range } + + pub fn scope_id(&self) -> usize { + self.scope_id + } +} + +impl Eq for ParameterSymbol {} + +impl PartialEq for ParameterSymbol { + fn eq(&self, other: &Self) -> bool { + self.declared_name == other.declared_name && self.scope_id == other.scope_id + } +} + +impl Hash for ParameterSymbol { + fn hash(&self, state: &mut H) { + self.declared_name.hash(state); + self.scope_id.hash(state); + } } diff --git a/dmc-lib/src/symbol/type_symbol.rs b/dmc-lib/src/symbol/type_symbol.rs index 992dbb0..d718b03 100644 --- a/dmc-lib/src/symbol/type_symbol.rs +++ b/dmc-lib/src/symbol/type_symbol.rs @@ -1,9 +1,8 @@ use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; -use std::cell::RefCell; use std::rc::Rc; pub enum TypeSymbol { - Class(Rc>), - GenericParameter(Rc>), + Class(Rc), + GenericParameter(Rc), } diff --git a/dmc-lib/src/symbol/variable_symbol.rs b/dmc-lib/src/symbol/variable_symbol.rs index b7eaf08..0196ea1 100644 --- a/dmc-lib/src/symbol/variable_symbol.rs +++ b/dmc-lib/src/symbol/variable_symbol.rs @@ -1,64 +1,61 @@ -use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; -use crate::symbol::Symbol; -use crate::type_info::TypeInfo; -use std::cell::RefCell; +use std::hash::{Hash, Hasher}; use std::rc::Rc; pub struct VariableSymbol { declared_name: Rc, declared_name_source_range: SourceRange, is_mut: bool, - type_info: Option, - vr_variable: Option>>, + scope_id: usize, } impl VariableSymbol { - pub fn new(name: &Rc, declared_name_source_range: SourceRange, is_mut: bool) -> Self { + pub fn new( + name: &Rc, + declared_name_source_range: &SourceRange, + is_mut: bool, + scope_id: usize, + ) -> Self { Self { declared_name: name.clone(), - declared_name_source_range, + declared_name_source_range: declared_name_source_range.clone(), is_mut, - type_info: None, - vr_variable: None, + scope_id, } } + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + + pub fn declared_name_source_range(&self) -> &SourceRange { + &self.declared_name_source_range + } + pub fn is_mut(&self) -> bool { self.is_mut } - pub fn set_type_info(&mut self, type_info: TypeInfo) { - self.type_info = Some(type_info); - } - - pub fn type_info(&self) -> &TypeInfo { - self.type_info - .as_ref() - .expect("TypeInfo not initialized. Did you type check?") - } - - pub fn set_vr_variable(&mut self, ir_variable: Rc>) { - self.vr_variable = Some(ir_variable); - } - - pub fn vr_variable(&self) -> &Rc> { - self.vr_variable - .as_ref() - .expect("ir_variable not yet initialized") + pub fn scope_id(&self) -> usize { + self.scope_id } } -impl Symbol for VariableSymbol { - fn declared_name(&self) -> &str { - &self.declared_name - } +impl Eq for VariableSymbol {} - fn declared_name_owned(&self) -> Rc { - self.declared_name.clone() - } - - fn declared_name_source_range(&self) -> &SourceRange { - &self.declared_name_source_range +impl PartialEq for VariableSymbol { + fn eq(&self, other: &Self) -> bool { + self.declared_name == other.declared_name && self.scope_id == other.scope_id + } +} + +impl Hash for VariableSymbol { + fn hash(&self, state: &mut H) { + self.declared_name.hash(state); + self.scope_id.hash(state); } } diff --git a/dmc-lib/src/symbol_table/helpers.rs b/dmc-lib/src/symbol_table/helpers.rs index 540173a..68ca09e 100644 --- a/dmc-lib/src/symbol_table/helpers.rs +++ b/dmc-lib/src/symbol_table/helpers.rs @@ -1,134 +1,210 @@ use crate::scope::Scope; use crate::scope::block_scope::BlockScope; +use crate::scope::class_body_scope::ClassBodyScope; use crate::scope::class_scope::ClassScope; use crate::scope::function_scope::FunctionScope; use crate::scope::module_scope::ModuleScope; use crate::symbol::Symbol; +use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol; +use crate::symbol::field_symbol::FieldSymbol; +use crate::symbol::function_symbol::FunctionSymbol; +use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; +use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::type_symbol::TypeSymbol; -use std::cell::RefCell; +use crate::symbol::variable_symbol::VariableSymbol; +use std::collections::HashMap; use std::rc::Rc; -pub fn find_in_module_by_name( - module_scope: &ModuleScope, +fn find_class_symbol_ref( + symbols: &HashMap, Rc>, name: &str, -) -> Option>> { - module_scope - .function_symbols() +) -> Option { + symbols .get(name) - .map(|symbol| symbol.clone() as Rc>) - .or_else(|| { - module_scope - .class_symbols() - .get(name) - .map(|symbol| symbol.clone() as Rc>) - }) + .map(|symbol| Symbol::Class(symbol.clone())) } -pub fn find_in_class_by_name( - class_scope: &ClassScope, +fn find_generic_parameter_symbol_ref( + symbols: &HashMap, Rc>, name: &str, -) -> Option>> { - class_scope - .class_symbols() +) -> Option { + symbols .get(name) - .map(|symbol| symbol.clone() as Rc>) - .or_else(|| { - class_scope - .function_symbols() - .get(name) - .map(|symbol| symbol.clone() as Rc>) - }) - .or_else(|| { - class_scope - .field_symbols() - .get(name) - .map(|symbol| symbol.clone() as Rc>) - }) + .map(|symbol| Symbol::GenericParameter(symbol.clone())) } -pub fn find_in_function_by_name( - function_scope: &FunctionScope, +fn find_field_symbol_ref( + symbols: &HashMap, Rc>, name: &str, -) -> Option>> { - function_scope - .parameter_symbols() +) -> Option { + symbols .get(name) - .map(|symbol| symbol.clone() as Rc>) + .map(|symbol| Symbol::Field(symbol.clone())) } -pub fn find_in_block_by_name( - block_scope: &BlockScope, +fn find_function_symbol_ref( + symbols: &HashMap, Rc>, name: &str, -) -> Option>> { - block_scope - .variable_symbols() +) -> Option { + symbols .get(name) - .map(|symbol| symbol.clone() as Rc>) + .map(|symbol| Symbol::Function(symbol.clone())) } +fn find_parameter_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| Symbol::Parameter(symbol.clone())) +} + +fn find_variable_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| Symbol::Variable(symbol.clone())) +} + +/* Expressible symbol refs */ + +fn find_class_expressible_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| ExpressibleSymbol::Class(symbol.clone())) +} + +fn find_field_expressible_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| ExpressibleSymbol::Field(symbol.clone())) +} + +fn find_function_expressible_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| ExpressibleSymbol::Function(symbol.clone())) +} + +fn find_parameter_expressible_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| ExpressibleSymbol::Parameter(symbol.clone())) +} + +fn find_variable_expressible_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| ExpressibleSymbol::Variable(symbol.clone())) +} + +/* Find type symbols */ + +fn find_class_type_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| TypeSymbol::Class(symbol.clone())) +} + +fn find_generic_parameter_type_symbol_ref( + symbols: &HashMap, Rc>, + name: &str, +) -> Option { + symbols + .get(name) + .map(|symbol| TypeSymbol::GenericParameter(symbol.clone())) +} + +/* Public helper functions */ +/* Various find in functions */ + +pub fn find_in_module_by_name(module_scope: &ModuleScope, name: &str) -> Option { + find_function_symbol_ref(&module_scope.function_symbols(), name) + .or_else(|| find_class_symbol_ref(&module_scope.class_symbols(), name)) +} + +pub fn find_in_class_by_name(class_scope: &ClassScope, name: &str) -> Option { + find_generic_parameter_symbol_ref(&class_scope.generic_parameter_symbols(), name) +} + +pub fn find_in_class_body_by_name(class_body_scope: &ClassBodyScope, name: &str) -> Option { + find_class_symbol_ref(&class_body_scope.class_symbols(), name) + .or_else(|| find_function_symbol_ref(&class_body_scope.function_symbols(), name)) + .or_else(|| find_field_symbol_ref(&class_body_scope.field_symbols(), name)) +} + +pub fn find_in_function_by_name(function_scope: &FunctionScope, name: &str) -> Option { + find_parameter_symbol_ref(&function_scope.parameter_symbols(), name) +} + +pub fn find_in_block_by_name(block_scope: &BlockScope, name: &str) -> Option { + find_variable_symbol_ref(&block_scope.variable_symbols(), name) +} + +/* Find expressible */ + fn find_expressible_in_module_by_name( module_scope: &ModuleScope, name: &str, ) -> Option { - module_scope - .class_symbols() - .get(name) - .map(|class_symbol| ExpressibleSymbol::Class(class_symbol.clone())) - .or_else(|| { - module_scope - .function_symbols() - .get(name) - .map(|function_symbol| ExpressibleSymbol::Function(function_symbol.clone())) - }) + find_class_expressible_symbol_ref(&module_scope.class_symbols(), name) + .or_else(|| find_function_expressible_symbol_ref(&module_scope.function_symbols(), name)) } -fn find_expressible_in_class_by_name( - class_scope: &ClassScope, +fn find_expressible_in_class_body_by_name( + class_body_scope: &ClassBodyScope, name: &str, ) -> Option { - class_scope - .class_symbols() - .get(name) - .map(|symbol| ExpressibleSymbol::Class(symbol.clone())) + find_class_expressible_symbol_ref(&class_body_scope.class_symbols(), name) .or_else(|| { - class_scope - .function_symbols() - .get(name) - .map(|function_symbol| ExpressibleSymbol::Function(function_symbol.clone())) - }) - .or_else(|| { - class_scope - .field_symbols() - .get(name) - .map(|field_symbol| ExpressibleSymbol::Field(field_symbol.clone())) + find_function_expressible_symbol_ref(&class_body_scope.function_symbols(), name) }) + .or_else(|| find_field_expressible_symbol_ref(&class_body_scope.field_symbols(), name)) } fn find_expressible_in_function_by_name( function_scope: &FunctionScope, name: &str, ) -> Option { - function_scope - .parameter_symbols() - .get(name) - .map(|parameter_symbol| ExpressibleSymbol::Parameter(parameter_symbol.clone())) + find_parameter_expressible_symbol_ref(function_scope.parameter_symbols(), name) } fn find_expressible_in_block_by_name( block_scope: &BlockScope, name: &str, ) -> Option { - block_scope - .variable_symbols() - .get(name) - .map(|variable_symbol| ExpressibleSymbol::Variable(variable_symbol.clone())) + find_variable_expressible_symbol_ref(block_scope.variable_symbols(), name) } pub fn find_expressible_symbol(scope: &Scope, name: &str) -> Option { match scope { Scope::Module(module_scope) => find_expressible_in_module_by_name(module_scope, name), - Scope::Class(class_scope) => find_expressible_in_class_by_name(class_scope, name), + Scope::Class(_) => None, + Scope::ClassBody(class_body_scope) => { + find_expressible_in_class_body_by_name(class_body_scope, name) + } Scope::Function(function_scope) => { find_expressible_in_function_by_name(function_scope, name) } @@ -136,32 +212,30 @@ pub fn find_expressible_symbol(scope: &Scope, name: &str) -> Option Option { - module_scope - .class_symbols() - .get(name) - .map(|symbol| TypeSymbol::Class(symbol.clone())) + find_class_type_symbol_ref(&module_scope.class_symbols(), name) } -pub fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option { - class_scope - .class_symbols() - .get(name) - .map(|symbol| TypeSymbol::Class(symbol.clone())) - .or_else(|| { - class_scope - .generic_parameter_symbols() - .get(name) - .map(|generic_parameter_symbol| { - TypeSymbol::GenericParameter(generic_parameter_symbol.clone()) - }) - }) +fn find_type_symbol_in_class_body( + class_body_scope: &ClassBodyScope, + name: &str, +) -> Option { + find_class_type_symbol_ref(&class_body_scope.class_symbols(), name) +} + +fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option { + find_generic_parameter_type_symbol_ref(&class_scope.generic_parameter_symbols(), name) } pub fn find_type_symbol(scope: &Scope, name: &str) -> Option { match scope { Scope::Module(module_scope) => find_type_symbol_in_module(module_scope, name), Scope::Class(class_scope) => find_type_symbol_in_class(class_scope, name), + Scope::ClassBody(class_body_scope) => { + find_type_symbol_in_class_body(class_body_scope, name) + } _ => None, } } diff --git a/dmc-lib/src/symbol_table/mod.rs b/dmc-lib/src/symbol_table/mod.rs index 6ba70a6..a3a944a 100644 --- a/dmc-lib/src/symbol_table/mod.rs +++ b/dmc-lib/src/symbol_table/mod.rs @@ -2,6 +2,7 @@ mod helpers; use crate::scope::Scope; use crate::scope::block_scope::BlockScope; +use crate::scope::class_body_scope::ClassBodyScope; use crate::scope::class_scope::ClassScope; use crate::scope::function_scope::FunctionScope; use crate::scope::module_scope::ModuleScope; @@ -16,10 +17,9 @@ use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::type_symbol::TypeSymbol; use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol_table::helpers::{ - find_expressible_symbol, find_in_block_by_name, find_in_class_by_name, - find_in_function_by_name, find_in_module_by_name, find_type_symbol, find_type_symbol_in_class, + find_expressible_symbol, find_in_block_by_name, find_in_class_body_by_name, + find_in_class_by_name, find_in_function_by_name, find_in_module_by_name, find_type_symbol, }; -use std::cell::RefCell; use std::rc::Rc; pub struct SymbolTable { @@ -39,282 +39,210 @@ impl SymbolTable { self.scopes.len() } - fn push_scope(&mut self, scope: Scope) { + fn push_scope(&mut self, scope: Scope) -> usize { let scope_id = self.new_scope_id(); self.scopes.push(scope); self.current_scope_id = Some(scope_id); + scope_id } - pub fn push_module_scope(&mut self, debug_name: &str) { + pub fn push_module_scope(&mut self, debug_name: &str) -> usize { self.push_scope(Scope::Module(ModuleScope::new( debug_name, self.current_scope_id, - ))); + ))) } - pub fn push_class_scope(&mut self, debug_name: &str) { + pub fn push_class_scope(&mut self, debug_name: &str) -> usize { self.push_scope(Scope::Class(ClassScope::new( debug_name, self.current_scope_id.unwrap(), - ))); + ))) } - pub fn push_function_scope(&mut self, debug_name: &str) { + pub fn push_class_body_scope(&mut self, debug_name: &str) -> usize { + self.push_scope(Scope::ClassBody(ClassBodyScope::new( + debug_name, + self.current_scope_id.unwrap(), + ))) + } + + pub fn push_function_scope(&mut self, debug_name: &str) -> usize { self.push_scope(Scope::Function(FunctionScope::new( debug_name, self.current_scope_id.unwrap(), - ))); + ))) } - pub fn push_block_scope(&mut self, debug_name: &str) { + pub fn push_block_scope(&mut self, debug_name: &str) -> usize { self.push_scope(Scope::Block(BlockScope::new( debug_name, self.current_scope_id.unwrap(), - ))); + ))) } pub fn pop_scope(&mut self) { - self.current_scope_id = self.current_scope().parent_id(); + self.current_scope_id = self.scopes[self.current_scope_id.unwrap()].parent_id(); } pub fn current_scope_id(&self) -> usize { self.current_scope_id.unwrap() } - fn current_scope(&self) -> &Scope { - &self.scopes[self.current_scope_id.unwrap()] + fn scope(&self, scope_id: usize) -> &Scope { + &self.scopes[scope_id] } - fn current_scope_mut(&mut self) -> &mut Scope { - &mut self.scopes[self.current_scope_id.unwrap()] + fn scope_mut(&mut self, scope_id: usize) -> &mut Scope { + &mut self.scopes[scope_id] } - pub fn insert_class_symbol( - &mut self, - class_symbol: ClassSymbol, - ) -> Result>, SymbolInsertError> { - let maybe_already_inserted = match self.current_scope() { - Scope::Module(module_scope) => { - find_in_module_by_name(module_scope, class_symbol.declared_name()) + pub fn get_symbol(&self, scope_id: usize, name: &str) -> Option { + match self.scope(scope_id) { + Scope::Module(module_scope) => find_in_module_by_name(module_scope, name), + Scope::Class(class_scope) => find_in_class_by_name(class_scope, name), + Scope::ClassBody(class_body_scope) => { + find_in_class_body_by_name(class_body_scope, name) } - Scope::Class(class_scope) => { - find_in_class_by_name(class_scope, class_symbol.declared_name()) + Scope::Function(function_scope) => find_in_function_by_name(function_scope, name), + Scope::Block(block_scope) => find_in_block_by_name(block_scope, name), + } + } + + pub fn insert_symbol(&mut self, symbol: Symbol) { + match symbol { + Symbol::Class(class_symbol) => { + self.insert_class_symbol(class_symbol); + } + Symbol::GenericParameter(generic_parameter_symbol) => { + self.insert_generic_parameter_symbol(generic_parameter_symbol); + } + Symbol::Field(field_symbol) => { + self.insert_field_symbol(field_symbol); + } + Symbol::Constructor(constructor_symbol) => { + self.insert_constructor_symbol(constructor_symbol); + } + Symbol::Function(function_symbol) => { + self.insert_function_symbol(function_symbol); + } + Symbol::Parameter(parameter_symbol) => { + self.insert_parameter_symbol(parameter_symbol); + } + Symbol::Variable(variable_symbol) => { + self.insert_variable_symbol(variable_symbol); + } + } + } + + pub fn insert_class_symbol(&mut self, class_symbol: Rc) { + let name = class_symbol.declared_name_owned(); + match self.scope_mut(class_symbol.scope_id()) { + Scope::Module(module_scope) => { + module_scope.class_symbols_mut().insert(name, class_symbol); + } + Scope::ClassBody(class_scope) => { + class_scope.class_symbols_mut().insert(name, class_symbol); } _ => panic!("Attempt to insert ClassSymbol in incompatible scope"), - }; - - if let Some(already_inserted) = maybe_already_inserted { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - already_inserted, - ))); } - - let name = class_symbol.declared_name_owned(); - let as_rc = Rc::new(RefCell::new(class_symbol)); - let to_return = as_rc.clone(); - - match self.current_scope_mut() { - Scope::Module(module_scope) => { - module_scope.class_symbols_mut().insert(name, as_rc); - } - Scope::Class(class_scope) => { - class_scope.class_symbols_mut().insert(name, as_rc); - } - _ => unreachable!(), - } - Ok(to_return) } pub fn insert_generic_parameter_symbol( &mut self, - generic_parameter_symbol: GenericParameterSymbol, - ) -> Result>, SymbolInsertError> { - let maybe_already_inserted = match self.current_scope() { - Scope::Class(class_scope) => { - find_type_symbol_in_class(class_scope, generic_parameter_symbol.declared_name()) - } - _ => panic!("Attempt to insert GenericParameterSymbol in incompatible scope"), - }; - - if let Some(already_inserted) = maybe_already_inserted { - match already_inserted { - TypeSymbol::Class(class_symbol) => { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - class_symbol as Rc>, - ))); - } - TypeSymbol::GenericParameter(generic_parameter_symbol) => { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - generic_parameter_symbol as Rc>, - ))); - } - } - } - + generic_parameter_symbol: Rc, + ) { let name = generic_parameter_symbol.declared_name_owned(); - let as_rc = Rc::new(RefCell::new(generic_parameter_symbol)); - let to_return = as_rc.clone(); - - match self.current_scope_mut() { + match self.scope_mut(generic_parameter_symbol.scope_id()) { Scope::Class(class_scope) => { class_scope .generic_parameter_symbols_mut() - .insert(name, as_rc); + .insert(name, generic_parameter_symbol); } - _ => unreachable!(), + _ => panic!("Attempt to insert GenericParameterSymbol in incompatible scope"), } - - Ok(to_return) } - pub fn insert_constructor_symbol( - &mut self, - constructor_symbol: ConstructorSymbol, - ) -> Result>, SymbolInsertError> { - let maybe_already_inserted = match self.current_scope() { - Scope::Class(class_scope) => class_scope.constructor_symbol(), + pub fn insert_constructor_symbol(&mut self, constructor_symbol: Rc) { + match self.scope_mut(constructor_symbol.scope_id()) { + Scope::ClassBody(class_scope) => { + class_scope + .constructor_symbol_mut() + .replace(constructor_symbol); + } _ => panic!("Attempt to insert ConstructorSymbol in incompatible scope"), - }; - if let Some(already_inserted) = maybe_already_inserted { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - already_inserted.clone() as Rc>, - ))); } - - let as_rc = Rc::new(RefCell::new(constructor_symbol)); - let to_return = as_rc.clone(); - - match self.current_scope_mut() { - Scope::Class(class_scope) => { - class_scope.constructor_symbol_mut().replace(as_rc); - } - _ => unreachable!(), - } - Ok(to_return) } - pub fn insert_field_symbol( - &mut self, - field_symbol: FieldSymbol, - ) -> Result>, SymbolInsertError> { - let maybe_already_inserted = match self.current_scope() { - Scope::Class(class_scope) => { - find_in_class_by_name(class_scope, field_symbol.declared_name()) + pub fn insert_field_symbol(&mut self, field_symbol: Rc) { + let name = field_symbol.declared_name_owned(); + match self.scope_mut(field_symbol.scope_id()) { + Scope::ClassBody(class_scope) => { + class_scope.field_symbols_mut().insert(name, field_symbol); } _ => panic!("Attempt to insert FieldSymbol in incompatible scope"), - }; - - if let Some(already_inserted) = maybe_already_inserted { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - already_inserted, - ))); } - - let name = field_symbol.declared_name_owned(); - let as_rc = Rc::new(RefCell::new(field_symbol)); - let to_return = as_rc.clone(); - - match self.current_scope_mut() { - Scope::Class(class_scope) => { - class_scope.field_symbols_mut().insert(name, as_rc); - } - _ => unreachable!(), - } - Ok(to_return) } - pub fn insert_function_symbol( - &mut self, - function_symbol: FunctionSymbol, - ) -> Result>, SymbolInsertError> { - let maybe_already_inserted = match self.current_scope() { + pub fn insert_function_symbol(&mut self, function_symbol: Rc) { + let name = function_symbol.declared_name_owned(); + match self.scope_mut(function_symbol.scope_id()) { Scope::Module(module_scope) => { - find_in_module_by_name(module_scope, function_symbol.declared_name()) + module_scope + .function_symbols_mut() + .insert(name, function_symbol); } - Scope::Class(class_scope) => { - find_in_class_by_name(class_scope, function_symbol.declared_name()) + Scope::ClassBody(class_scope) => { + class_scope + .function_symbols_mut() + .insert(name, function_symbol); } _ => panic!("Attempt to insert FunctionSymbol in incompatible scope"), - }; - - if let Some(already_inserted) = maybe_already_inserted { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - already_inserted, - ))); } - - let name = function_symbol.declared_name_owned(); - let as_rc = Rc::new(RefCell::new(function_symbol)); - let to_return = as_rc.clone(); - - match self.current_scope_mut() { - Scope::Module(module_scope) => { - module_scope.function_symbols_mut().insert(name, as_rc); - } - Scope::Class(class_scope) => { - class_scope.function_symbols_mut().insert(name, as_rc); - } - _ => unreachable!(), - } - Ok(to_return) } - pub fn insert_parameter_symbol( - &mut self, - parameter_symbol: ParameterSymbol, - ) -> Result>, SymbolInsertError> { - let maybe_already_inserted = match self.current_scope() { - Scope::Function(function_scope) => { - find_in_function_by_name(function_scope, parameter_symbol.declared_name()) - } - _ => panic!("Attempt to insert ParameterSymbol in incompatible scope"), - }; - if let Some(already_inserted) = maybe_already_inserted { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - already_inserted, - ))); - } - + pub fn insert_parameter_symbol(&mut self, parameter_symbol: Rc) { let name = parameter_symbol.declared_name_owned(); - let as_rc = Rc::new(RefCell::new(parameter_symbol)); - let to_return = as_rc.clone(); - - match self.current_scope_mut() { + match self.scope_mut(parameter_symbol.scope_id()) { Scope::Function(function_scope) => { - function_scope.parameter_symbols_mut().insert(name, as_rc); + function_scope + .parameter_symbols_mut() + .insert(name, parameter_symbol); } _ => panic!("Attempt to insert ParameterSymbol in incompatible scope"), } - - Ok(to_return) } - pub fn insert_variable_symbol( - &mut self, - variable_symbol: VariableSymbol, - ) -> Result<(), SymbolInsertError> { - let maybe_already_inserted = match self.current_scope() { + pub fn insert_variable_symbol(&mut self, variable_symbol: Rc) { + match self.scope_mut(variable_symbol.scope_id()) { Scope::Block(block_scope) => { - find_in_block_by_name(block_scope, variable_symbol.declared_name()) + block_scope + .variable_symbols_mut() + .insert(variable_symbol.declared_name_owned(), variable_symbol); } _ => panic!("Attempt to insert VariableSymbol in incompatible scope"), - }; - if let Some(already_inserted) = maybe_already_inserted { - return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( - already_inserted, - ))); } + } - match self.current_scope_mut() { - Scope::Block(block_scope) => { - block_scope.variable_symbols_mut().insert( - variable_symbol.declared_name_owned(), - Rc::new(RefCell::new(variable_symbol)), - ); - } - _ => unreachable!(), + fn find_symbol( + &self, + scope_id: usize, + name: &str, + f: impl Fn(&Scope, &str) -> Option, + ) -> Option { + let mut maybe_scope = self.scopes.get(scope_id); + if maybe_scope.is_none() { + panic!("Invalid scope_id: {}", scope_id); } - Ok(()) + while let Some(scope) = maybe_scope { + let maybe_symbol = f(scope, name); + if maybe_symbol.is_some() { + return maybe_symbol; + } else { + maybe_scope = scope.parent_id().and_then(|id| self.scopes.get(id)); + } + } + None } pub fn find_expressible_symbol( @@ -353,28 +281,82 @@ impl SymbolTable { None } - pub fn get_variable_symbol(&self, scope_id: usize, name: &str) -> Rc> { + pub fn get_class_symbol(&self, scope_id: usize, name: &str) -> Option<&Rc> { + match self.scope(scope_id) { + Scope::Module(module_scope) => module_scope.class_symbols().get(name), + Scope::ClassBody(class_body_scope) => class_body_scope.class_symbols().get(name), + _ => panic!("scope_id {} cannot contain classes", scope_id), + } + } + + pub fn get_field_symbol(&self, scope_id: usize, name: &str) -> Option<&FieldSymbol> { + match self.scope(scope_id) { + Scope::ClassBody(class_body_scope) => { + class_body_scope.field_symbols().get(name).map(Rc::as_ref) + } + _ => panic!("scope_id {} is not a ClassBodyScope", scope_id), + } + } + + pub fn get_constructor_symbol(&self, scope_id: usize) -> Option<&ConstructorSymbol> { + match self.scope(scope_id) { + Scope::ClassBody(class_body_scope) => { + class_body_scope.constructor_symbol().map(Rc::as_ref) + } + _ => panic!("scope_id {} is not a ClassBodyScope", scope_id), + } + } + + pub fn get_function_symbol(&self, scope_id: usize, name: &str) -> Option<&FunctionSymbol> { + match self.scope(scope_id) { + Scope::Module(module_scope) => { + module_scope.function_symbols().get(name).map(Rc::as_ref) + } + Scope::ClassBody(class_body_scope) => class_body_scope + .function_symbols() + .get(name) + .map(Rc::as_ref), + _ => panic!("scope_id {} cannot contain Functions", scope_id), + } + } + + pub fn get_parameter_symbol(&self, scope_id: usize, name: &str) -> Option<&ParameterSymbol> { + match self.scope(scope_id) { + Scope::Function(function_scope) => { + function_scope.parameter_symbols().get(name).map(Rc::as_ref) + } + _ => panic!("scope_id {} cannot contain Parameters", scope_id), + } + } + + pub fn get_parameter_symbol_owned( + &self, + scope_id: usize, + name: &str, + ) -> Option> { + match self.scope(scope_id) { + Scope::Function(function_scope) => { + function_scope.parameter_symbols().get(name).cloned() + } + _ => panic!("scope_id {} cannot contain Parameters", scope_id), + } + } + + pub fn get_variable_symbol(&self, scope_id: usize, name: &str) -> Option<&VariableSymbol> { match &self.scopes[scope_id] { - Scope::Block(block_scope) => block_scope.variable_symbols().get(name).cloned().unwrap(), + Scope::Block(block_scope) => block_scope.variable_symbols().get(name).map(Rc::as_ref), + _ => panic!("scope_id {} is not a BlockScope", scope_id), + } + } + + pub fn get_variable_symbol_owned( + &self, + scope_id: usize, + name: &str, + ) -> Option> { + match self.scope(scope_id) { + Scope::Block(block_scope) => block_scope.variable_symbols().get(name).map(Rc::clone), _ => panic!("scope_id {} is not a BlockScope", scope_id), } } } - -pub enum SymbolInsertError { - AlreadyDeclared(AlreadyDeclared), -} - -pub struct AlreadyDeclared { - symbol: Rc>, -} - -impl AlreadyDeclared { - pub fn new(symbol: Rc>) -> Self { - Self { symbol } - } - - pub fn symbol(&self) -> &Rc> { - &self.symbol - } -} diff --git a/dmc-lib/src/type_info.rs b/dmc-lib/src/type_info.rs index 52bc0fa..93ee5f3 100644 --- a/dmc-lib/src/type_info.rs +++ b/dmc-lib/src/type_info.rs @@ -1,10 +1,8 @@ -use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; use crate::symbol::type_symbol::TypeSymbol; use crate::symbol_table::SymbolTable; -use std::cell::RefCell; use std::fmt::{Display, Formatter}; use std::rc::Rc; @@ -14,9 +12,9 @@ pub enum TypeInfo { Integer, Double, String, - Function(Rc>), - ClassInstance(Rc>), - GenericType(Rc>), + Function(Rc), + ClassInstance(Rc), + GenericType(Rc), Void, } @@ -29,16 +27,19 @@ impl Display for TypeInfo { TypeInfo::String => write!(f, "String"), TypeInfo::Function(function_symbol) => { write!(f, "fn(")?; - for parameter in function_symbol.borrow().parameters() { - parameter.borrow().type_info().fmt(f)?; + for (i, parameter) in function_symbol.parameters().iter().enumerate() { + parameter.declared_name().fmt(f)?; + if i < function_symbol.parameters().len() - 1 { + f.write_str(", ")?; + } } write!(f, ")") } TypeInfo::ClassInstance(class_symbol) => { - write!(f, "{}", class_symbol.borrow().declared_name()) + write!(f, "{}", class_symbol.declared_name()) } TypeInfo::GenericType(generic_parameter_symbol) => { - write!(f, "{}", generic_parameter_symbol.borrow().declared_name()) + write!(f, "{}", generic_parameter_symbol.declared_name()) } TypeInfo::Void => write!(f, "Void"), } @@ -58,7 +59,7 @@ impl TypeInfo { declared_name: &str, scope_id: usize, symbol_table: &SymbolTable, - class_context: Option<&Rc>>, + class_context: Option<&Rc>, ) -> Option { match declared_name { "Any" => Some(TypeInfo::Any), @@ -66,7 +67,7 @@ impl TypeInfo { "Double" => Some(TypeInfo::Double), "String" => Some(TypeInfo::String), "Void" => Some(TypeInfo::Void), - "Self" => Some(TypeInfo::ClassInstance(class_context.unwrap().clone())), + "Self" => Some(TypeInfo::ClassInstance(class_context.cloned().unwrap())), _ => match symbol_table.find_type_symbol(scope_id, declared_name) { None => None, Some(type_symbol) => match type_symbol { @@ -97,14 +98,13 @@ impl TypeInfo { TypeInfo::ClassInstance(class_symbol) => { match other { TypeInfo::ClassInstance(other_class_symbol) => { - class_symbol.borrow().declared_name() - == other_class_symbol.borrow().declared_name() // good enough for now + class_symbol == other_class_symbol // good enough for now } _ => false, } } TypeInfo::GenericType(generic_parameter_symbol) => { - if generic_parameter_symbol.borrow().extends().len() > 0 { + if generic_parameter_symbol.extends().len() > 0 { unimplemented!( "Assigning to generic parameter type with extends type uses not yet supported." ); diff --git a/dmc-lib/src/types_table.rs b/dmc-lib/src/types_table.rs new file mode 100644 index 0000000..6b8afef --- /dev/null +++ b/dmc-lib/src/types_table.rs @@ -0,0 +1,68 @@ +use crate::symbol::class_symbol::ClassSymbol; +use crate::symbol::field_symbol::FieldSymbol; +use crate::symbol::function_symbol::FunctionSymbol; +use crate::symbol::parameter_symbol::ParameterSymbol; +use crate::symbol::variable_symbol::VariableSymbol; +use crate::type_info::TypeInfo; +use std::collections::HashMap; +use std::rc::Rc; + +pub struct TypesTable { + class_instance_types: HashMap, TypeInfo>, + field_types: HashMap, TypeInfo>, + parameter_types: HashMap, TypeInfo>, + variable_types: HashMap, TypeInfo>, + function_return_types: HashMap, TypeInfo>, +} + +impl TypesTable { + pub fn new() -> Self { + Self { + class_instance_types: HashMap::new(), + field_types: HashMap::new(), + parameter_types: HashMap::new(), + variable_types: HashMap::new(), + function_return_types: HashMap::new(), + } + } + + pub fn class_instance_types(&self) -> &HashMap, TypeInfo> { + &self.class_instance_types + } + + pub fn class_instance_types_mut(&mut self) -> &mut HashMap, TypeInfo> { + &mut self.class_instance_types + } + + pub fn field_types(&self) -> &HashMap, TypeInfo> { + &self.field_types + } + + pub fn field_types_mut(&mut self) -> &mut HashMap, TypeInfo> { + &mut self.field_types + } + + pub fn parameter_types(&self) -> &HashMap, TypeInfo> { + &self.parameter_types + } + + pub fn parameter_types_mut(&mut self) -> &mut HashMap, TypeInfo> { + &mut self.parameter_types + } + + pub fn variable_types(&self) -> &HashMap, TypeInfo> { + &self.variable_types + } + + pub fn variable_types_mut(&mut self) -> &mut HashMap, TypeInfo> { + &mut self.variable_types + } + + pub fn function_return_types(&self) -> &HashMap, TypeInfo> { + &self.function_return_types + } + + pub fn function_return_types_mut(&mut self) -> &mut HashMap, TypeInfo> { + &mut self.function_return_types + } +} diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 7a7d3c9..ae38b2d 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -4,6 +4,7 @@ mod e2e_tests { use dmc_lib::diagnostic::Diagnostic; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; + use dmc_lib::types_table::TypesTable; use dvm_lib::vm::class::Class; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; @@ -26,7 +27,7 @@ mod e2e_tests { panic!("There were diagnostics."); } - fn prepare_context(input: &str) -> DvmContext { + fn prepare_context(input: &str) -> Result> { let parse_result = parse_compilation_unit(input); let mut compilation_unit = match parse_result { Ok(compilation_unit) => compilation_unit, @@ -36,29 +37,14 @@ mod e2e_tests { }; let mut symbol_table = SymbolTable::new(); + let mut types_table = TypesTable::new(); - match compilation_unit.gather_declared_names(&mut symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - report_diagnostics(&diagnostics); - } - } + compilation_unit.init_scopes(&mut symbol_table); + compilation_unit.gather_symbols_into(&mut symbol_table)?; + compilation_unit.check_names(&mut symbol_table)?; + compilation_unit.type_check(&symbol_table, &mut types_table)?; - match compilation_unit.check_name_usages(&symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - report_diagnostics(&diagnostics); - } - } - - match compilation_unit.type_check(&symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - report_diagnostics(&diagnostics); - } - } - - let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table); + let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table, &types_table); let mut functions: Vec = vec![]; let mut constants_table = ConstantsTable::new(); @@ -95,7 +81,7 @@ mod e2e_tests { ); } - dvm_context + Ok(dvm_context) } fn get_result( @@ -116,7 +102,10 @@ mod e2e_tests { } fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) { - let context = prepare_context(input); + let context = match prepare_context(input) { + Ok(context) => context, + Err(diagnostics) => report_diagnostics(&diagnostics), + }; match get_result(&context, function_name, arguments) { None => panic!("Call returned no value"), Some(result_value) => { @@ -215,7 +204,7 @@ mod e2e_tests { } #[test] - fn two_classes() { + fn two_classes() -> Result<(), Vec> { let context = prepare_context( " class Foo @@ -239,7 +228,7 @@ mod e2e_tests { Foo(n) end ", - ); + )?; let result = get_result(&context, "foo", &[Value::Int(42)]); assert!(result.is_some()); let value = result.unwrap(); @@ -247,6 +236,7 @@ mod e2e_tests { let o = value.unwrap_object().borrow(); assert_eq!(o.fields().len(), 1); assert_eq!(o.fields()[0].unwrap_int(), 42); + Ok(()) } #[test] @@ -266,7 +256,7 @@ mod e2e_tests { } #[test] - fn assign_field() { + fn assign_field() -> Result<(), Vec> { let context = prepare_context( " class Foo @@ -281,7 +271,7 @@ mod e2e_tests { Foo(42) end ", - ); + )?; let result = get_result(&context, "foo", &vec![]); assert!(result.is_some()); let value = result.unwrap(); @@ -289,10 +279,11 @@ mod e2e_tests { let o = value.unwrap_object().borrow(); assert_eq!(o.fields().len(), 1); assert_eq!(o.fields()[0].unwrap_int(), 42); + Ok(()) } #[test] - fn see_what_happens() { + fn see_what_happens() -> Result<(), Vec> { let context = prepare_context( " class Foo @@ -306,7 +297,7 @@ mod e2e_tests { Foo(42) end ", - ); + )?; let result = get_result(&context, "main", &vec![]); assert!(result.is_some()); let value = result.unwrap(); @@ -314,49 +305,6 @@ mod e2e_tests { let o = value.unwrap_object().borrow(); assert_eq!(o.fields().len(), 1); assert_eq!(o.fields()[0].unwrap_int(), 42); - } -} - -#[cfg(test)] -mod diagnostic_tests { - use dmc_lib::diagnostic::Diagnostic; - use dmc_lib::parser::parse_compilation_unit; - use dmc_lib::symbol_table::SymbolTable; - - fn get_diagnostics(input: &str) -> Vec { - let parse_result = parse_compilation_unit(input); - let mut compilation_unit = match parse_result { - Ok(compilation_unit) => compilation_unit, - Err(diagnostics) => { - return diagnostics; - } - }; - - let mut symbol_table = SymbolTable::new(); - - match compilation_unit.gather_declared_names(&mut symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - return diagnostics; - } - } - - match compilation_unit.check_name_usages(&symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - return diagnostics; - } - } - - match compilation_unit.type_check(&symbol_table) { - Ok(_) => vec![], - Err(diagnostics) => diagnostics, - } - } - - #[test] - fn wrong_return_type() { - let diagnostics = get_diagnostics("fn main() -> String 42 end"); - assert_eq!(diagnostics.len(), 1); + Ok(()) } }