Preparing for intrinsic class symbols. WIP.

This commit is contained in:
Jesse Brault 2026-03-21 11:36:42 -05:00
parent 7f1d507f4f
commit fc83cf7827
14 changed files with 200 additions and 93 deletions

View File

@ -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);
}
}

View File

@ -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,
),
}
}

View File

@ -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<Diagnostic>> {
// 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();

View File

@ -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)
}

View File

@ -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;

View File

@ -0,0 +1 @@
mod symbols;

View File

@ -0,0 +1,3 @@
use crate::symbol_table::SymbolTable;
pub fn add_intrinsic_symbols(symbol_table: &mut SymbolTable) {}

View File

@ -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;

View File

@ -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<FunctionSymbol>),
Class(Rc<ClassSymbol>),
Constructor(Rc<ConstructorSymbol>),
}
impl CallableSymbol {
pub fn parameters(&self) -> Vec<Rc<ParameterSymbol>> {
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()
}
}
}

View File

@ -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<str>,
declared_name_source_range: SourceRange,
declared_name_source_range: Option<SourceRange>,
fqn_parts: Vec<Rc<str>>,
is_extern: bool,
scope_id: usize,
generic_parameters: Vec<Rc<GenericParameterSymbol>>,
constructor_symbol: Rc<ConstructorSymbol>,
constructor_symbol: Option<Rc<ConstructorSymbol>>,
fields: HashMap<Rc<str>, Rc<FieldSymbol>>,
functions: HashMap<Rc<str>, Rc<FunctionSymbol>>,
}
@ -22,18 +24,18 @@ pub struct ClassSymbol {
impl ClassSymbol {
pub fn new(
declared_name: &Rc<str>,
declared_name_source_range: &SourceRange,
declared_name_source_range: Option<SourceRange>,
fqn_parts: Vec<Rc<str>>,
is_extern: bool,
scope_id: usize,
generic_parameters: Vec<Rc<GenericParameterSymbol>>,
constructor_symbol: Rc<ConstructorSymbol>,
constructor_symbol: Option<Rc<ConstructorSymbol>>,
fields: Vec<Rc<FieldSymbol>>,
functions: Vec<Rc<FunctionSymbol>>,
) -> 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<str>] {
@ -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<Rc<ConstructorSymbol>> {
self.constructor_symbol.as_ref().cloned()
}
pub fn fields(&self) -> &HashMap<Rc<str>, Rc<FieldSymbol>> {
@ -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))
}
}

View File

@ -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())
}
}
}

View File

@ -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()),
}
}
}

View File

@ -331,6 +331,17 @@ impl SymbolTable {
}
}
pub fn get_constructor_symbol_owned(&self, scope_id: usize) -> Option<Rc<ConstructorSymbol>> {
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) => {

View File

@ -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<Rc<ClassSymbol>, TypeInfo>,
generic_parameter_types: HashMap<Rc<GenericParameterSymbol>, TypeInfo>,
field_types: HashMap<Rc<FieldSymbol>, TypeInfo>,
constructor_return_types: HashMap<Rc<ConstructorSymbol>, TypeInfo>,
function_return_types: HashMap<Rc<FunctionSymbol>, TypeInfo>,
parameter_types: HashMap<Rc<ParameterSymbol>, TypeInfo>,
variable_types: HashMap<Rc<VariableSymbol>, TypeInfo>,
function_return_types: HashMap<Rc<FunctionSymbol>, 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<Rc<FunctionSymbol>, TypeInfo> {
&mut self.function_return_types
}
pub fn constructor_return_types(&self) -> &HashMap<Rc<ConstructorSymbol>, TypeInfo> {
&self.constructor_return_types
}
pub fn constructor_return_types_mut(
&mut self,
) -> &mut HashMap<Rc<ConstructorSymbol>, TypeInfo> {
&mut self.constructor_return_types
}
}