From fc83cf7827b34a72380555b92acd06b09680a918 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Sat, 21 Mar 2026 11:36:42 -0500 Subject: [PATCH] Preparing for intrinsic class symbols. WIP. --- dmc-lib/src/ast/assign_statement.rs | 58 ++++++++++++------- dmc-lib/src/ast/call.rs | 71 ++++++++++++++---------- dmc-lib/src/ast/class.rs | 18 ++++-- dmc-lib/src/ast/diagnostic_factories.rs | 46 ++++++++++----- dmc-lib/src/error_codes.rs | 1 + dmc-lib/src/intrinsics/mod.rs | 1 + dmc-lib/src/intrinsics/symbols.rs | 3 + dmc-lib/src/lib.rs | 1 + dmc-lib/src/symbol/callable_symbol.rs | 8 +-- dmc-lib/src/symbol/class_symbol.rs | 30 +++++++--- dmc-lib/src/symbol/expressible_symbol.rs | 14 ++--- dmc-lib/src/symbol/mod.rs | 16 +++--- dmc-lib/src/symbol_table/mod.rs | 11 ++++ dmc-lib/src/types_table.rs | 15 ++++- 14 files changed, 200 insertions(+), 93 deletions(-) create mode 100644 dmc-lib/src/intrinsics/mod.rs create mode 100644 dmc-lib/src/intrinsics/symbols.rs diff --git a/dmc-lib/src/ast/assign_statement.rs b/dmc-lib/src/ast/assign_statement.rs index 1e951fa..5759f45 100644 --- a/dmc-lib/src/ast/assign_statement.rs +++ b/dmc-lib/src/ast/assign_statement.rs @@ -111,20 +111,30 @@ impl AssignStatement { // check mutable if !is_mut { - let secondary_label = SecondaryLabel::new( - expressible_symbol.source_range().start(), - expressible_symbol.source_range().end(), - Some("Destination (declared here) is immutable.".to_string()), - ); - let diagnostic = Diagnostic::new( + let secondary_label = + if let Some(source_range) = expressible_symbol.source_range() { + Some(SecondaryLabel::new( + source_range.start(), + source_range.end(), + Some("Destination (declared here) is immutable.".to_string()), + )) + } else { + None + }; + + let mut diagnostic = Diagnostic::new( "Destination is immutable and not re-assignable.", self.destination.source_range().start(), self.destination.source_range().end(), ) .with_primary_label_message("Attempt to mutate immutable destination.") .with_reporter(file!(), line!()) - .with_error_code(ASSIGN_LHS_IMMUTABLE) - .with_secondary_labels(&[secondary_label]); + .with_error_code(ASSIGN_LHS_IMMUTABLE); + + if let Some(secondary_label) = secondary_label { + diagnostic = diagnostic.with_secondary_labels(&[secondary_label]); + } + diagnostics.push(diagnostic); } @@ -141,15 +151,21 @@ impl AssignStatement { 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(), - expressible_symbol.source_range().end(), - Some(format!( - "Destination declared here is of type {}.", - lhs_type - )), - ); - let diagnostic = Diagnostic::new( + let secondary_label = + if let Some(source_range) = expressible_symbol.source_range() { + Some(SecondaryLabel::new( + source_range.start(), + source_range.end(), + Some(format!( + "Destination declared here is of type {}.", + lhs_type + )), + )) + } else { + None + }; + + let mut diagnostic = Diagnostic::new( &format!( "Mismatched types: right-hand side {} is not assignable to left {}.", rhs_type, lhs_type @@ -162,8 +178,12 @@ impl AssignStatement { rhs_type, lhs_type )) .with_error_code(ASSIGN_MISMATCHED_TYPES) - .with_reporter(file!(), line!()) - .with_secondary_labels(&[secondary_label]); + .with_reporter(file!(), line!()); + + if let Some(secondary_label) = secondary_label { + diagnostic = diagnostic.with_secondary_labels(&[secondary_label]); + } + diagnostics.push(diagnostic); } } diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index d9d1986..bbd3dac 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -1,3 +1,4 @@ +use crate::ast::diagnostic_factories::class_has_no_constructor; use crate::ast::expression::Expression; use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::ir_builder::IrBuilder; @@ -124,7 +125,18 @@ impl Call { TypeInfo::Function(function_symbol) => { CallableSymbol::Function(function_symbol.clone()) } - TypeInfo::ClassInstance(class_symbol) => CallableSymbol::Class(class_symbol.clone()), + TypeInfo::ClassInstance(class_symbol) => { + match class_symbol.constructor_symbol_owned() { + None => { + diagnostics.push(class_has_no_constructor( + class_symbol.declared_name(), + self.callee.source_range(), + )); + return Err(diagnostics); + } + Some(constructor_symbol) => CallableSymbol::Constructor(constructor_symbol), + } + } _ => { diagnostics.push(Diagnostic::new( &format!( @@ -181,24 +193,6 @@ impl Call { } } - 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, symbol_table: &SymbolTable) -> CallableSymbol { match self.callee() { Expression::Identifier(identifier) => { @@ -210,7 +204,14 @@ impl Call { CallableSymbol::Function(function_symbol.clone()) } ExpressibleSymbol::Class(class_symbol) => { - CallableSymbol::Class(class_symbol.clone()) + match class_symbol.constructor_symbol_owned() { + None => { + panic!("Attempt to get non-existent constructor symbol") + } + Some(constructor_symbol) => { + CallableSymbol::Constructor(constructor_symbol) + } + } } _ => panic!("Calling things other than functions not yet supported."), } @@ -219,6 +220,23 @@ impl Call { } } + pub fn return_type_info<'a>( + &self, + symbol_table: &SymbolTable, + types_table: &'a TypesTable, + ) -> &'a TypeInfo { + match self.get_callee_symbol(symbol_table) { + CallableSymbol::Function(function_symbol) => types_table + .function_return_types() + .get(&function_symbol) + .unwrap(), + CallableSymbol::Constructor(constructor_symbol) => types_table + .constructor_return_types() + .get(&constructor_symbol) + .unwrap(), + } + } + pub fn to_ir( &self, builder: &mut IrBuilder, @@ -243,14 +261,11 @@ impl Call { arguments, function_symbol.is_extern(), ), - CallableSymbol::Class(class_symbol) => { - let constructor_symbol = class_symbol.constructor_symbol(); - IrCall::new( - fqn_parts_to_string(constructor_symbol.fqn_parts()), - arguments, - false, - ) - } + CallableSymbol::Constructor(constructor_symbol) => IrCall::new( + 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 02e6995..8e8a986 100644 --- a/dmc-lib/src/ast/class.rs +++ b/dmc-lib/src/ast/class.rs @@ -134,12 +134,12 @@ impl Class { let class_symbol = Rc::new(ClassSymbol::new( &self.declared_name, - &self.declared_name_source_range, + Some(self.declared_name_source_range.clone()), fqn_context.resolve(&self.declared_name), false, self.scope_id.unwrap(), generic_parameter_symbols, - constructor_symbol, + Some(constructor_symbol), field_symbols, function_symbols, )); @@ -205,9 +205,19 @@ impl Class { ) -> Result<(), Vec> { // instance type let class_symbol = self.get_class_symbol_owned(symbol_table); + types_table.class_instance_types_mut().insert( + class_symbol.clone(), + TypeInfo::ClassInstance(class_symbol.clone()), + ); + + // constructor return type + // this works for both declared and default constructors + let constructor_symbol = symbol_table + .get_constructor_symbol_owned(self.self_class_body_scope_id.unwrap()) + .unwrap(); types_table - .class_instance_types_mut() - .insert(class_symbol.clone(), TypeInfo::ClassInstance(class_symbol)); + .constructor_return_types_mut() + .insert(constructor_symbol, TypeInfo::ClassInstance(class_symbol)); let mut diagnostics = Vec::new(); diff --git a/dmc-lib/src/ast/diagnostic_factories.rs b/dmc-lib/src/ast/diagnostic_factories.rs index 9768ca1..563e81c 100644 --- a/dmc-lib/src/ast/diagnostic_factories.rs +++ b/dmc-lib/src/ast/diagnostic_factories.rs @@ -1,8 +1,8 @@ use crate::diagnostic::{Diagnostic, SecondaryLabel}; use crate::error_codes::{ - FIELD_NO_TYPE_OR_INIT, 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, + CLASS_NO_CONSTRUCTOR, FIELD_NO_TYPE_OR_INIT, 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; @@ -36,21 +36,32 @@ pub fn cannot_reference_field_in_init(name: &str, source_range: &SourceRange) -> } 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( + let secondary_label = if let Some(source_range) = already_inserted.declared_name_source_range() + { + Some(SecondaryLabel::new( + source_range.start(), + source_range.end(), + Some("Symbol already declared here.".to_string()), + )) + } else { + None + }; + + let diagnostic = 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(), + would_insert.declared_name_source_range().unwrap().start(), // unwrap should be okay, since this is user code + would_insert.declared_name_source_range().unwrap().end(), ) - .with_error_code(SYMBOL_ALREADY_DECLARED) - .with_secondary_labels(&[secondary_label]) + .with_error_code(SYMBOL_ALREADY_DECLARED); + + if let Some(secondary_label) = secondary_label { + diagnostic.with_secondary_labels(&[secondary_label]) + } else { + diagnostic + } } pub fn self_constructor_used_in_init(source_range: &SourceRange) -> Diagnostic { @@ -97,3 +108,12 @@ pub fn outer_class_method_usage(source_range: &SourceRange) -> Diagnostic { ) .with_error_code(OUTER_CLASS_METHOD_USED_IN_INIT) } + +pub fn class_has_no_constructor(name: &str, source_range: &SourceRange) -> Diagnostic { + Diagnostic::new( + &format!("Class {} has no constructor.", name), + source_range.start(), + source_range.end(), + ) + .with_error_code(CLASS_NO_CONSTRUCTOR) +} diff --git a/dmc-lib/src/error_codes.rs b/dmc-lib/src/error_codes.rs index b8ab053..32b3115 100644 --- a/dmc-lib/src/error_codes.rs +++ b/dmc-lib/src/error_codes.rs @@ -16,3 +16,4 @@ 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; pub const FIELD_NO_TYPE_OR_INIT: ErrorCode = 28; +pub const CLASS_NO_CONSTRUCTOR: ErrorCode = 29; diff --git a/dmc-lib/src/intrinsics/mod.rs b/dmc-lib/src/intrinsics/mod.rs new file mode 100644 index 0000000..6eb441d --- /dev/null +++ b/dmc-lib/src/intrinsics/mod.rs @@ -0,0 +1 @@ +mod symbols; diff --git a/dmc-lib/src/intrinsics/symbols.rs b/dmc-lib/src/intrinsics/symbols.rs new file mode 100644 index 0000000..f6d9e47 --- /dev/null +++ b/dmc-lib/src/intrinsics/symbols.rs @@ -0,0 +1,3 @@ +use crate::symbol_table::SymbolTable; + +pub fn add_intrinsic_symbols(symbol_table: &mut SymbolTable) {} diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index 0137943..80bb9a0 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -2,6 +2,7 @@ pub mod ast; pub mod constants_table; pub mod diagnostic; pub mod error_codes; +mod intrinsics; pub mod ir; pub mod lexer; pub mod parser; diff --git a/dmc-lib/src/symbol/callable_symbol.rs b/dmc-lib/src/symbol/callable_symbol.rs index 799c69a..7021e6c 100644 --- a/dmc-lib/src/symbol/callable_symbol.rs +++ b/dmc-lib/src/symbol/callable_symbol.rs @@ -1,19 +1,19 @@ -use crate::symbol::class_symbol::ClassSymbol; +use crate::symbol::constructor_symbol::ConstructorSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::parameter_symbol::ParameterSymbol; use std::rc::Rc; pub enum CallableSymbol { Function(Rc), - Class(Rc), + Constructor(Rc), } impl CallableSymbol { pub fn parameters(&self) -> Vec> { match self { CallableSymbol::Function(function_symbol) => function_symbol.parameters().to_vec(), - CallableSymbol::Class(class_symbol) => { - class_symbol.constructor_symbol().parameters().to_vec() + CallableSymbol::Constructor(constructor_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 bef2bb0..98bfa1c 100644 --- a/dmc-lib/src/symbol/class_symbol.rs +++ b/dmc-lib/src/symbol/class_symbol.rs @@ -1,20 +1,22 @@ +use crate::ast::fqn_util::fqn_parts_to_string; use crate::source_range::SourceRange; 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::collections::HashMap; +use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::rc::Rc; pub struct ClassSymbol { declared_name: Rc, - declared_name_source_range: SourceRange, + declared_name_source_range: Option, fqn_parts: Vec>, is_extern: bool, scope_id: usize, generic_parameters: Vec>, - constructor_symbol: Rc, + constructor_symbol: Option>, fields: HashMap, Rc>, functions: HashMap, Rc>, } @@ -22,18 +24,18 @@ pub struct ClassSymbol { impl ClassSymbol { pub fn new( declared_name: &Rc, - declared_name_source_range: &SourceRange, + declared_name_source_range: Option, fqn_parts: Vec>, is_extern: bool, scope_id: usize, generic_parameters: Vec>, - constructor_symbol: Rc, + constructor_symbol: Option>, fields: Vec>, functions: Vec>, ) -> Self { Self { declared_name: declared_name.clone(), - declared_name_source_range: declared_name_source_range.clone(), + declared_name_source_range, fqn_parts, is_extern, scope_id, @@ -58,8 +60,8 @@ impl ClassSymbol { self.declared_name.clone() } - pub fn declared_name_source_range(&self) -> &SourceRange { - &self.declared_name_source_range + pub fn declared_name_source_range(&self) -> Option<&SourceRange> { + self.declared_name_source_range.as_ref() } pub fn fqn_parts(&self) -> &[Rc] { @@ -74,8 +76,12 @@ impl ClassSymbol { &self.generic_parameters } - pub fn constructor_symbol(&self) -> &ConstructorSymbol { - &self.constructor_symbol + pub fn constructor_symbol(&self) -> Option<&ConstructorSymbol> { + self.constructor_symbol.as_ref().map(|s| s.as_ref()) + } + + pub fn constructor_symbol_owned(&self) -> Option> { + self.constructor_symbol.as_ref().cloned() } pub fn fields(&self) -> &HashMap, Rc> { @@ -101,3 +107,9 @@ impl Hash for ClassSymbol { self.scope_id.hash(state); } } + +impl Debug for ClassSymbol { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "ClassSymbol({})", fqn_parts_to_string(&self.fqn_parts)) + } +} diff --git a/dmc-lib/src/symbol/expressible_symbol.rs b/dmc-lib/src/symbol/expressible_symbol.rs index 6c05412..25b0ac5 100644 --- a/dmc-lib/src/symbol/expressible_symbol.rs +++ b/dmc-lib/src/symbol/expressible_symbol.rs @@ -15,22 +15,20 @@ pub enum ExpressibleSymbol { } impl ExpressibleSymbol { - pub fn source_range(&self) -> SourceRange { + pub fn source_range(&self) -> Option<&SourceRange> { match self { - ExpressibleSymbol::Class(class_symbol) => { - class_symbol.declared_name_source_range().clone() - } + ExpressibleSymbol::Class(class_symbol) => class_symbol.declared_name_source_range(), ExpressibleSymbol::Field(field_symbol) => { - field_symbol.declared_name_source_range().clone() + Some(field_symbol.declared_name_source_range()) } ExpressibleSymbol::Function(function_symbol) => { - function_symbol.declared_name_source_range().clone() + Some(function_symbol.declared_name_source_range()) } ExpressibleSymbol::Parameter(parameter_symbol) => { - parameter_symbol.declared_name_source_range().clone() + Some(parameter_symbol.declared_name_source_range()) } ExpressibleSymbol::Variable(variable_symbol) => { - variable_symbol.declared_name_source_range().clone() + Some(variable_symbol.declared_name_source_range()) } } } diff --git a/dmc-lib/src/symbol/mod.rs b/dmc-lib/src/symbol/mod.rs index 7fb073f..409d740 100644 --- a/dmc-lib/src/symbol/mod.rs +++ b/dmc-lib/src/symbol/mod.rs @@ -59,19 +59,21 @@ impl Symbol { } } - pub fn declared_name_source_range(&self) -> &SourceRange { + pub fn declared_name_source_range(&self) -> Option<&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() + Some(generic_parameter_symbol.declared_name_source_range()) } - Symbol::Field(field_symbol) => field_symbol.declared_name_source_range(), + Symbol::Field(field_symbol) => Some(field_symbol.declared_name_source_range()), Symbol::Constructor(constructor_symbol) => { - constructor_symbol.declared_name_source_range() + Some(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(), + Symbol::Function(function_symbol) => Some(function_symbol.declared_name_source_range()), + Symbol::Parameter(parameter_symbol) => { + Some(parameter_symbol.declared_name_source_range()) + } + Symbol::Variable(variable_symbol) => Some(variable_symbol.declared_name_source_range()), } } } diff --git a/dmc-lib/src/symbol_table/mod.rs b/dmc-lib/src/symbol_table/mod.rs index 59f9894..36b75c2 100644 --- a/dmc-lib/src/symbol_table/mod.rs +++ b/dmc-lib/src/symbol_table/mod.rs @@ -331,6 +331,17 @@ impl SymbolTable { } } + pub fn get_constructor_symbol_owned(&self, scope_id: usize) -> Option> { + match self.scope(scope_id) { + Scope::ClassBody(class_body_scope) => class_body_scope.constructor_symbol().cloned(), + _ => panic!( + "scope_id {} is not a ClassBodyScope: {:?}", + scope_id, + self.scope(scope_id) + ), + } + } + pub fn get_function_symbol(&self, scope_id: usize, name: &str) -> Option<&FunctionSymbol> { match self.scope(scope_id) { Scope::Module(module_scope) => { diff --git a/dmc-lib/src/types_table.rs b/dmc-lib/src/types_table.rs index 2feb822..81cf5d3 100644 --- a/dmc-lib/src/types_table.rs +++ b/dmc-lib/src/types_table.rs @@ -1,4 +1,5 @@ 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; @@ -12,9 +13,10 @@ pub struct TypesTable { class_instance_types: HashMap, TypeInfo>, generic_parameter_types: HashMap, TypeInfo>, field_types: HashMap, TypeInfo>, + constructor_return_types: HashMap, TypeInfo>, + function_return_types: HashMap, TypeInfo>, parameter_types: HashMap, TypeInfo>, variable_types: HashMap, TypeInfo>, - function_return_types: HashMap, TypeInfo>, } impl TypesTable { @@ -26,6 +28,7 @@ impl TypesTable { parameter_types: HashMap::new(), variable_types: HashMap::new(), function_return_types: HashMap::new(), + constructor_return_types: HashMap::new(), } } @@ -78,4 +81,14 @@ impl TypesTable { pub fn function_return_types_mut(&mut self) -> &mut HashMap, TypeInfo> { &mut self.function_return_types } + + pub fn constructor_return_types(&self) -> &HashMap, TypeInfo> { + &self.constructor_return_types + } + + pub fn constructor_return_types_mut( + &mut self, + ) -> &mut HashMap, TypeInfo> { + &mut self.constructor_return_types + } }