Fewer tests failing. WIP.

This commit is contained in:
Jesse Brault 2026-03-20 20:39:03 -05:00
parent cf92356585
commit 4e8fa159c0
22 changed files with 382 additions and 198 deletions

View File

@ -202,8 +202,10 @@ impl AssignStatement {
let ir_statement = match destination_symbol {
ExpressibleSymbol::Field(field_symbol) => {
let field_type = types_table.field_types().get(&field_symbol).unwrap();
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, field_type)
.clone();
let ir_set_field = IrSetField::new(
&mut_field_pointer_variable,
self.value
@ -240,10 +242,11 @@ mod tests {
fn compile_up_to_type_check(
compilation_unit: &mut CompilationUnit,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
) -> Result<TypesTable, Vec<Diagnostic>> {
compilation_unit.init_scopes(symbol_table);
compilation_unit.gather_symbols_into(symbol_table)?;
compilation_unit.check_names(symbol_table)
compilation_unit.check_names(symbol_table)?;
compilation_unit.gather_types(symbol_table)
}
#[test]
@ -258,8 +261,7 @@ mod tests {
)
.unwrap();
let mut symbol_table = SymbolTable::new();
compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?;
let mut types_table = TypesTable::new();
let mut types_table = compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?;
let diagnostics = compilation_unit
.type_check(&symbol_table, &mut types_table)
.unwrap_err();
@ -281,8 +283,7 @@ mod tests {
",
)?;
let mut symbol_table = SymbolTable::new();
compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?;
let mut types_table = TypesTable::new();
let mut types_table = compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?;
let diagnostics = compilation_unit
.type_check(&symbol_table, &mut types_table)
.unwrap_err();
@ -302,8 +303,7 @@ mod tests {
",
)?;
let mut symbol_table = SymbolTable::new();
compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?;
let mut types_table = TypesTable::new();
let mut types_table = compile_up_to_type_check(&mut compilation_unit, &mut symbol_table)?;
let diagnostics = compilation_unit
.type_check(&symbol_table, &mut types_table)
.unwrap_err();

View File

@ -12,7 +12,6 @@ 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;
@ -21,6 +20,7 @@ use crate::symbol::variable_symbol::VariableSymbol;
use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics, ok_or_err_diagnostics};
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
@ -64,8 +64,8 @@ impl Class {
generic_parameter.init_scopes(symbol_table, class_scope);
}
let class_body_scope =
symbol_table.push_class_scope(&format!("class_body_scope({})", self.declared_name));
let class_body_scope = symbol_table
.push_class_body_scope(&format!("class_body_scope({})", self.declared_name));
for field in &mut self.fields {
field.init_scopes(symbol_table, class_body_scope);
@ -93,8 +93,8 @@ impl Class {
}
let mut field_symbols = Vec::new();
for field in &self.fields {
let symbol = Rc::new(field.make_symbol());
for (field_index, field) in self.fields.iter().enumerate() {
let symbol = Rc::new(field.make_symbol(field_index));
all_symbols.push(Symbol::Field(symbol.clone()));
field_symbols.push(symbol);
}
@ -164,11 +164,15 @@ impl Class {
diagnostics
}
pub fn check_field_initializer_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
let class_symbol = symbol_table
fn get_class_symbol_owned(&self, symbol_table: &SymbolTable) -> Rc<ClassSymbol> {
symbol_table
.get_class_symbol(self.scope_id.unwrap(), &self.declared_name)
.cloned()
.unwrap();
.unwrap()
}
pub fn check_field_initializer_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
let class_symbol = self.get_class_symbol_owned(symbol_table);
self.fields
.iter()
.flat_map(|field| field.check_field_initializer_names(symbol_table, &class_symbol))
@ -176,11 +180,7 @@ impl Class {
}
pub fn analyze_local_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
let class_symbol = symbol_table
.get_class_symbol(self.scope_id.unwrap(), &self.declared_name)
.cloned()
.unwrap();
let class_symbol = self.get_class_symbol_owned(symbol_table);
let mut diagnostics: Vec<Diagnostic> = Vec::new();
if let Some(constructor) = &self.constructor {
diagnostics.append(&mut constructor.analyze_local_names(symbol_table, &class_symbol));
@ -192,9 +192,39 @@ impl Class {
diagnostics
}
fn type_check_generics(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
pub fn gather_types(
&self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> 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));
let mut diagnostics = Vec::new();
// field types
for field in &self.fields {
handle_diagnostics!(field.gather_types(symbol_table, types_table), diagnostics);
}
// function return types
for function in &self.functions {
function.gather_types(symbol_table, types_table);
}
diagnostics_result!(diagnostics)
}
fn type_check_generics(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
collect_diagnostics_mut(&mut self.generic_parameters, |gp| {
gp.type_check(symbol_table)
gp.type_check(symbol_table, types_table)
})
}
@ -373,7 +403,7 @@ impl Class {
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
self.type_check_generics(symbol_table)?;
self.type_check_generics(symbol_table, types_table)?;
self.type_check_fields(symbol_table, types_table)?;
self.type_check_constructor(symbol_table, types_table)?;
self.type_check_functions(symbol_table, types_table)?;
@ -410,10 +440,14 @@ impl Class {
self.fields
.iter()
.map(|field| {
let field_symbol = symbol_table
.get_field_symbol_owned(field.scope_id(), field.declared_name())
.unwrap();
let field_type = types_table.field_types().get(&field_symbol).unwrap();
IrField::new(
field.declared_name().into(),
field.field_symbol().borrow().field_index(),
field.field_symbol().borrow().type_info().clone(),
field_symbol.field_index(),
field_type.clone(),
)
})
.collect(),

View File

@ -8,7 +8,7 @@ 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};
use crate::{diagnostics_result, handle_diagnostics, ok_or_err_diagnostics};
pub struct CompilationUnit {
functions: Vec<Function>,
@ -95,6 +95,28 @@ impl CompilationUnit {
diagnostics_result!(diagnostics)
}
pub fn gather_types(&self, symbol_table: &SymbolTable) -> Result<TypesTable, Vec<Diagnostic>> {
let mut types_table = TypesTable::new();
let mut diagnostics = Vec::new();
for class in &self.classes {
handle_diagnostics!(
class.gather_types(symbol_table, &mut types_table),
diagnostics
);
}
for function in &self.functions {
function.gather_types(symbol_table, &mut types_table);
}
for extern_function in &self.extern_functions {
extern_function.gather_types(symbol_table, &mut types_table);
}
ok_or_err_diagnostics!(types_table, diagnostics)
}
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
@ -110,7 +132,7 @@ impl CompilationUnit {
collect_diagnostics_into_mut(
&mut self.extern_functions,
|ef| ef.type_check(symbol_table),
|ef| ef.type_check(symbol_table, types_table),
&mut diagnostics,
);

View File

@ -121,7 +121,7 @@ impl Constructor {
let parameters_diagnostics: Vec<Diagnostic> = self
.parameters
.iter_mut()
.map(|param| param.type_check(symbol_table))
.map(|param| param.type_check(symbol_table, types_table))
.filter_map(Result::err)
.flatten()
.collect();
@ -206,16 +206,20 @@ impl Constructor {
// next, initialize fields that have an initializer in their declaration
for field in fields {
if let Some(initializer) = field.initializer() {
let field_symbol = symbol_table
.get_field_symbol_owned(field.scope_id(), field.declared_name())
.unwrap();
let field_type = types_table.field_types().get(&field_symbol).unwrap();
// get a mut ref to the field
let ir_get_field_ref_mut = IrGetFieldRefMut::new(
IrParameterOrVariable::Variable(self_variable.clone()),
field.field_symbol().borrow().field_index(),
field_symbol.field_index(),
);
let field_mut_ref_variable_name: Rc<str> = ir_builder.new_t_var().into();
let field_mut_ref_variable = Rc::new(RefCell::new(IrVariable::new_vr(
field_mut_ref_variable_name.clone(),
ir_builder.current_block().id(),
field.field_symbol().borrow().type_info(),
field_type,
)));
let field_mut_ref_assign = IrAssign::new(
field_mut_ref_variable.clone(),

View File

@ -1,7 +1,8 @@
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,
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;
@ -15,6 +16,16 @@ pub fn symbol_not_found(name: &str, source_range: &SourceRange) -> Diagnostic {
.with_error_code(SYMBOL_NOT_FOUND)
}
pub fn field_has_no_type_or_init(name: &str, source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
&format!("Field {} has no declared type nor initializer.", name),
source_range.start(),
source_range.end(),
)
.with_error_code(FIELD_NO_TYPE_OR_INIT)
.with_primary_label_message("Declare a type and/or an initializer.")
}
pub fn cannot_reference_field_in_init(name: &str, source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
&format!("Cannot reference field {} during initialization.", name),

View File

@ -230,7 +230,7 @@ impl Expression {
IrOperation::Load(IrExpression::String(string_literal.content().into()))
}
Expression::Identifier(identifier) => {
IrOperation::Load(identifier.ir_expression(builder, symbol_table))
IrOperation::Load(identifier.ir_expression(builder, symbol_table, types_table))
}
Expression::Negative(negative_expression) => {
IrOperation::Load(negative_expression.to_ir(builder, symbol_table, types_table))
@ -276,7 +276,7 @@ impl Expression {
}
}
Expression::Identifier(identifier) => {
Some(identifier.ir_expression(builder, symbol_table))
Some(identifier.ir_expression(builder, symbol_table, types_table))
}
Expression::Integer(integer_literal) => {
Some(IrExpression::Int(integer_literal.value()))

View File

@ -7,8 +7,8 @@ use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics};
use std::cell::RefCell;
use std::rc::Rc;
pub struct ExternFunction {
@ -17,7 +17,6 @@ pub struct ExternFunction {
parameters: Vec<Parameter>,
return_type: TypeUse,
scope_id: Option<usize>,
function_symbol: Option<Rc<RefCell<FunctionSymbol>>>,
}
impl ExternFunction {
@ -26,14 +25,13 @@ impl ExternFunction {
declared_name_source_range: SourceRange,
parameters: Vec<Parameter>,
return_type: TypeUse,
) -> ExternFunction {
ExternFunction {
) -> Self {
Self {
declared_name: name.into(),
declared_name_source_range,
parameters,
return_type,
scope_id: None,
function_symbol: None,
}
}
@ -83,14 +81,28 @@ impl ExternFunction {
diagnostics
}
pub fn gather_types(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
let function_symbol = symbol_table
.get_function_symbol_owned(self.scope_id.unwrap(), self.declared_name())
.unwrap();
let resolved_return_type = self
.return_type
.type_info(symbol_table, types_table)
.clone();
types_table
.function_return_types_mut()
.insert(function_symbol, resolved_return_type);
}
fn type_check_parameters(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
diagnostics: &mut Vec<Diagnostic>,
) {
collect_diagnostics_into_mut(
&mut self.parameters,
|p| p.type_check(symbol_table),
|p| p.type_check(symbol_table, types_table),
diagnostics,
);
}
@ -98,16 +110,24 @@ impl ExternFunction {
fn type_check_return_type(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
diagnostics: &mut Vec<Diagnostic>,
) {
handle_diagnostics!(self.return_type.type_check(symbol_table), diagnostics);
handle_diagnostics!(
self.return_type.type_check(symbol_table, types_table),
diagnostics
);
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
self.type_check_parameters(symbol_table, &mut diagnostics);
self.type_check_return_type(symbol_table, &mut diagnostics);
self.type_check_parameters(symbol_table, types_table, &mut diagnostics);
self.type_check_return_type(symbol_table, types_table, &mut diagnostics);
diagnostics_result!(diagnostics)
}

View File

@ -1,3 +1,4 @@
use crate::ast::diagnostic_factories::field_has_no_type_or_init;
use crate::ast::expression::Expression;
use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic;
@ -6,7 +7,6 @@ use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
use std::cell::RefCell;
use std::rc::Rc;
pub struct Field {
@ -17,7 +17,6 @@ pub struct Field {
declared_type: Option<Box<TypeUse>>,
initializer: Option<Box<Expression>>,
scope_id: Option<usize>,
field_symbol: Option<Rc<RefCell<FieldSymbol>>>,
}
impl Field {
@ -37,7 +36,6 @@ impl Field {
declared_type: declared_type.map(Box::new),
initializer: initializer.map(Box::new),
scope_id: None,
field_symbol: None,
}
}
@ -67,12 +65,17 @@ impl Field {
}
}
pub fn make_symbol(&self) -> FieldSymbol {
pub fn scope_id(&self) -> usize {
self.scope_id.unwrap()
}
pub fn make_symbol(&self, field_index: usize) -> FieldSymbol {
FieldSymbol::new(
&self.declared_name,
self.declared_name_source_range.clone(),
self.is_mut,
self.scope_id.unwrap(),
field_index,
)
}
@ -96,6 +99,41 @@ impl Field {
}
}
pub fn gather_types(
&self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
// self field
let field_symbol = symbol_table
.get_field_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
match &self.declared_type {
Some(declared_type) => {
let resolved_type = declared_type.type_info(symbol_table, types_table).clone();
types_table
.field_types_mut()
.insert(field_symbol, resolved_type);
}
None => match &self.initializer {
Some(initializer) => {
let initializer_type = initializer.type_info(symbol_table, types_table).clone();
types_table
.field_types_mut()
.insert(field_symbol, initializer_type);
}
None => {
// this is an error
return Err(vec![field_has_no_type_or_init(
self.declared_name(),
self.declared_name_source_range(),
)]);
}
},
}
Ok(())
}
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
@ -104,7 +142,9 @@ impl Field {
let mut diagnostics: Vec<Diagnostic> = vec![];
if let Some(type_use) = &mut self.declared_type {
if let Some(mut type_use_diagnostics) = type_use.type_check(symbol_table).err() {
if let Some(mut type_use_diagnostics) =
type_use.type_check(symbol_table, types_table).err()
{
diagnostics.append(&mut type_use_diagnostics);
}
}
@ -121,19 +161,16 @@ impl Field {
return Err(diagnostics);
}
// Now check that types are assignable, and update field symbol's type info
let field_type_info =
// Now check that types are assignable
match self.declared_type.as_ref() {
Some(type_use) => {
match self.initializer.as_ref() {
Some(type_use) => match self.initializer.as_ref() {
Some(initializer) => {
let initializer_type_info =
initializer.type_info(symbol_table, types_table);
let declared_type_info = type_use.type_info();
let initializer_type_info = initializer.type_info(symbol_table, types_table);
let declared_type_info = type_use.type_info(symbol_table, types_table);
if declared_type_info.is_assignable_from(initializer_type_info) {
declared_type_info
Ok(())
} else {
return Err(vec![
Err(vec![
Diagnostic::new(
&format!(
"Mismatched types: {} is not assignable to {}",
@ -143,46 +180,12 @@ impl Field {
initializer.source_range().end(),
)
.with_reporter(file!(), line!()),
]);
])
}
}
None => {
// easy: the declared type
type_use.type_info()
None => Ok(()),
},
None => Ok(()),
}
}
}
None => {
// type is the initializer
match self.initializer.as_ref() {
Some(initializer) => initializer.type_info(symbol_table, types_table),
None => {
// this is an error
return Err(vec![Diagnostic::new(
"Field must have either a declared type or initializer, or both.",
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
).with_reporter(file!(), line!())]);
}
}
}
};
// update field symbol's type info
self.field_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_type_info(field_type_info.clone());
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn field_symbol(&self) -> &Rc<RefCell<FieldSymbol>> {
self.field_symbol.as_ref().unwrap()
}
}

View File

@ -137,6 +137,22 @@ impl Function {
.collect()
}
pub fn gather_types(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
let function_symbol = symbol_table
.get_function_symbol_owned(self.scope_id.unwrap(), self.declared_name())
.unwrap();
if let Some(type_use) = &self.return_type {
let resolved_return_type = type_use.type_info(symbol_table, types_table).clone();
types_table
.function_return_types_mut()
.insert(function_symbol, resolved_return_type);
} else {
types_table
.function_return_types_mut()
.insert(function_symbol, TypeInfo::Void);
}
}
fn get_return_type_info(
types_table: &TypesTable,
function_symbol: &FunctionSymbol,
@ -152,11 +168,12 @@ impl Function {
fn type_check_parameters(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
diagnostics: &mut Vec<Diagnostic>,
) {
collect_diagnostics_into_mut(
&mut self.parameters,
|p| p.type_check(symbol_table),
|p| p.type_check(symbol_table, types_table),
diagnostics,
)
}
@ -165,10 +182,11 @@ impl Function {
fn type_check_return_type(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
diagnostics: &mut Vec<Diagnostic>,
) {
if let Some(type_use) = &mut self.return_type {
handle_diagnostics!(type_use.type_check(symbol_table), diagnostics);
handle_diagnostics!(type_use.type_check(symbol_table, types_table), diagnostics);
}
}
@ -208,10 +226,10 @@ impl Function {
.unwrap();
// parameters
self.type_check_parameters(symbol_table, &mut diagnostics);
self.type_check_parameters(symbol_table, types_table, &mut diagnostics);
// return type
self.type_check_return_type(symbol_table, &mut diagnostics);
self.type_check_return_type(symbol_table, types_table, &mut diagnostics);
// statements
self.type_check_statements(symbol_table, types_table, &mut diagnostics, function_symbol);

View File

@ -3,6 +3,7 @@ use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics};
use std::cell::RefCell;
use std::rc::Rc;
@ -52,11 +53,15 @@ impl GenericParameter {
.collect()
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
// check extends type uses
for type_use in &mut self.extends {
handle_diagnostics!(type_use.type_check(symbol_table), diagnostics);
handle_diagnostics!(type_use.type_check(symbol_table, types_table), diagnostics);
}
diagnostics_result!(diagnostics)
}

View File

@ -234,6 +234,7 @@ impl Identifier {
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrExpression {
let expressible_symbol = symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
@ -243,14 +244,15 @@ impl Identifier {
todo!()
}
ExpressibleSymbol::Field(field_symbol) => {
let field_type = types_table.field_types().get(&field_symbol).unwrap();
let read_destination = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_symbol.type_info(),
field_type,
);
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(),
get_or_init_field_pointer_variable(builder, &field_symbol, field_type).clone(),
);
builder
.current_block_mut()

View File

@ -6,12 +6,14 @@ use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable;
use crate::symbol::field_symbol::FieldSymbol;
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub fn get_or_init_field_pointer_variable<'a>(
builder: &'a mut IrBuilder,
field_symbol: &Rc<FieldSymbol>,
field_type: &TypeInfo,
) -> &'a Rc<RefCell<IrVariable>> {
// 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.
@ -22,7 +24,7 @@ pub fn get_or_init_field_pointer_variable<'a>(
let field_ref_variable = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_symbol.type_info(),
field_type,
);
let as_rc = Rc::new(RefCell::new(field_ref_variable));
let to_insert = as_rc.clone();
@ -49,6 +51,7 @@ pub fn get_or_init_field_pointer_variable<'a>(
pub fn get_or_init_mut_field_pointer_variable<'a>(
builder: &'a mut IrBuilder,
field_symbol: &Rc<FieldSymbol>,
field_type: &TypeInfo,
) -> &'a Rc<RefCell<IrVariable>> {
if !builder
.field_mut_pointer_variables()
@ -57,7 +60,7 @@ pub fn get_or_init_mut_field_pointer_variable<'a>(
let mut_field_pointer_variable = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_symbol.type_info(),
field_type,
);
let as_rc = Rc::new(RefCell::new(mut_field_pointer_variable));
let to_insert = as_rc.clone();

View File

@ -128,13 +128,19 @@ impl LetStatement {
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
self.initializer.type_check(symbol_table, types_table)?;
let initializer_type_info = self.initializer.type_info(symbol_table, types_table);
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);
Ok(())
}

View File

@ -3,6 +3,7 @@ use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange;
use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
use std::rc::Rc;
pub struct Parameter {
@ -51,8 +52,12 @@ impl Parameter {
self.type_use.check_names(symbol_table)
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.type_use.type_check(symbol_table)?;
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
self.type_use.type_check(symbol_table, types_table)?;
Ok(())
}
}

View File

@ -3,10 +3,11 @@ use crate::diagnostic::Diagnostic;
use crate::error_codes::INCORRECT_GENERIC_ARGUMENTS;
use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::type_symbol::TypeSymbol;
use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics};
use std::cell::RefCell;
use std::rc::Rc;
pub struct TypeUse {
@ -14,7 +15,6 @@ pub struct TypeUse {
declared_name_source_range: SourceRange,
generic_arguments: Vec<TypeUse>,
scope_id: Option<usize>,
type_info: Option<TypeInfo>,
}
impl TypeUse {
@ -28,7 +28,6 @@ impl TypeUse {
declared_name_source_range,
generic_arguments,
scope_id: None,
type_info: None,
}
}
@ -86,14 +85,11 @@ impl TypeUse {
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
if let Some(type_info) = TypeInfo::from_declared_name(
&self.declared_name,
self.scope_id.unwrap(),
symbol_table,
class_context,
) {
self.type_info = Some(type_info);
} else {
if symbol_table
.find_type_symbol(self.scope_id.unwrap(), &self.declared_name)
.is_none()
{
// this is wonky
let diagnostic = Diagnostic::new(
&format!("Unable to resolve symbol {}", self.declared_name),
self.declared_name_source_range.start(),
@ -113,10 +109,33 @@ impl TypeUse {
diagnostics_result!(diagnostics)
}
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
pub fn type_info<'a>(
&self,
symbol_table: &SymbolTable,
types_table: &'a TypesTable,
) -> &'a TypeInfo {
let type_symbol = symbol_table
.find_type_symbol(self.scope_id.unwrap(), self.declared_name())
.unwrap();
match type_symbol {
TypeSymbol::Class(class_symbol) => types_table
.class_instance_types()
.get(&class_symbol)
.unwrap(),
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
todo!();
}
}
}
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
match self.type_info() {
match self.type_info(symbol_table, types_table) {
TypeInfo::ClassInstance(class_symbol) => {
// check number of params/args match
let generic_parameters = class_symbol.generic_parameters();
@ -151,7 +170,7 @@ impl TypeUse {
let diagnostic = Diagnostic::new(
&format!(
"Type {} does not accept generic arguments.",
self.type_info()
self.type_info(symbol_table, types_table)
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
@ -165,15 +184,14 @@ impl TypeUse {
// recurse on generic arguments
for generic_argument in &mut self.generic_arguments {
handle_diagnostics!(generic_argument.type_check(_symbol_table), diagnostics);
handle_diagnostics!(
generic_argument.type_check(symbol_table, types_table),
diagnostics
);
}
diagnostics_result!(diagnostics)
}
pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap()
}
}
#[cfg(test)]

View File

@ -15,3 +15,4 @@ 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;
pub const FIELD_NO_TYPE_OR_INIT: ErrorCode = 28;

View File

@ -124,7 +124,6 @@ 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() -> Result<(), Vec<Diagnostic>> {
@ -137,14 +136,14 @@ mod tests {
let x = a + b + c
end
",
)
.unwrap();
)?;
let mut symbol_table = SymbolTable::new();
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)?;
let mut types_table = compilation_unit.gather_types(&symbol_table)?;
compilation_unit.type_check(&mut symbol_table, &mut types_table)?;
let main = compilation_unit

View File

@ -1,5 +1,4 @@
use crate::source_range::SourceRange;
use crate::type_info::TypeInfo;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
@ -8,8 +7,7 @@ pub struct FieldSymbol {
declared_name_source_range: SourceRange,
is_mut: bool,
scope_id: usize,
type_info: Option<TypeInfo>,
field_index: Option<usize>,
field_index: usize,
}
impl FieldSymbol {
@ -18,14 +16,14 @@ impl FieldSymbol {
declared_name_source_range: SourceRange,
is_mut: bool,
scope_id: usize,
field_index: usize,
) -> Self {
Self {
declared_name: declared_name.clone(),
declared_name_source_range,
is_mut,
scope_id,
type_info: None,
field_index: None,
field_index,
}
}
@ -49,20 +47,8 @@ impl FieldSymbol {
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().unwrap()
}
pub fn set_field_index(&mut self, field_index: usize) {
self.field_index = Some(field_index);
}
pub fn field_index(&self) -> usize {
self.field_index.unwrap()
self.field_index
}
}

View File

@ -165,8 +165,8 @@ impl SymbolTable {
pub fn insert_constructor_symbol(&mut self, constructor_symbol: Rc<ConstructorSymbol>) {
match self.scope_mut(constructor_symbol.scope_id()) {
Scope::ClassBody(class_scope) => {
class_scope
Scope::ClassBody(class_body_scope) => {
class_body_scope
.constructor_symbol_mut()
.replace(constructor_symbol);
}
@ -298,6 +298,16 @@ impl SymbolTable {
}
}
pub fn get_field_symbol_owned(&self, scope_id: usize, name: &str) -> Option<Rc<FieldSymbol>> {
match self.scope(scope_id) {
Scope::ClassBody(class_body_scope) => class_body_scope
.field_symbols()
.get(name)
.map(|s| Rc::clone(s)),
_ => 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) => {
@ -320,6 +330,24 @@ impl SymbolTable {
}
}
pub fn get_function_symbol_owned(
&self,
scope_id: usize,
name: &str,
) -> Option<Rc<FunctionSymbol>> {
match self.scope(scope_id) {
Scope::Module(module_scope) => module_scope
.function_symbols()
.get(name)
.map(|s| Rc::clone(s)),
Scope::ClassBody(class_body_scope) => class_body_scope
.function_symbols()
.get(name)
.map(|s| Rc::clone(s)),
_ => 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) => {

View File

@ -1,8 +1,6 @@
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::fmt::{Display, Formatter};
use std::rc::Rc;
@ -55,31 +53,6 @@ fn are_numbers(left: &TypeInfo, right: &TypeInfo) -> bool {
}
impl TypeInfo {
pub fn from_declared_name(
declared_name: &str,
scope_id: usize,
symbol_table: &SymbolTable,
class_context: Option<&Rc<ClassSymbol>>,
) -> Option<Self> {
match declared_name {
"Any" => Some(TypeInfo::Any),
"Int" => Some(TypeInfo::Integer),
"Double" => Some(TypeInfo::Double),
"String" => Some(TypeInfo::String),
"Void" => Some(TypeInfo::Void),
"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 {
TypeSymbol::Class(class_symbol) => Some(TypeInfo::ClassInstance(class_symbol)),
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
Some(TypeInfo::GenericType(generic_parameter_symbol))
}
},
},
}
}
pub fn is_assignable_from(&self, other: &TypeInfo) -> bool {
match self {
TypeInfo::Any => true,

View File

@ -4,13 +4,12 @@ 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;
use dvm_lib::vm::operand::Operand;
use dvm_lib::vm::value::Value;
use dvm_lib::vm::{CallStack, DvmContext, call};
use dvm_lib::vm::{call, CallStack, DvmContext};
use std::rc::Rc;
const REGISTER_COUNT: usize = 8;
@ -37,11 +36,11 @@ mod e2e_tests {
};
let mut symbol_table = SymbolTable::new();
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)?;
let mut types_table = compilation_unit.gather_types(&symbol_table)?;
compilation_unit.type_check(&symbol_table, &mut types_table)?;
let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table, &types_table);

View File

@ -0,0 +1,47 @@
trait Add<R = Self>
type Output = Self
fn add(other: R) -> Self::Output
end
intrinsic class Int end
intrinsic class Double end
impl Add for Int
fn add(other: Self) -> Self
self + other
end
end
impl Add<Double> for Int
type Output = Double
fn add(other: Double) -> Double
self + other
end
end
impl Add for Double
fn add(other: Self) -> Self
self + other
end
end
impl Add<Int> for Double
fn add(other: Int) -> Self
self + other
end
end
fn add_rough_pi(a: impl Add<Double>) -> Double = a + 3.14
fn concat<T: impl Add<U>, U>(a: T, b: U) -> T::Output
a + b
end
fn main()
let x = add_rough_pi(3)
println(x) // 6.14
let y = concat(1, 2) // 3
let z = concat("Hello, ", "World!") // "Hello, World!"
end