Add type-checking to classes/fields/constructors.
This commit is contained in:
parent
3c0bf948ac
commit
8082f4c2e6
@ -5,6 +5,7 @@ use crate::diagnostic::Diagnostic;
|
|||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol::class_symbol::ClassSymbol;
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
@ -138,10 +139,6 @@ impl Class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
// 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 mut diagnostics: Vec<Diagnostic> = vec![];
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
self.fields
|
self.fields
|
||||||
@ -151,7 +148,58 @@ impl Class {
|
|||||||
.flatten()
|
.flatten()
|
||||||
.for_each(|diagnostic| diagnostics.push(diagnostic));
|
.for_each(|diagnostic| diagnostics.push(diagnostic));
|
||||||
|
|
||||||
// todo check constructor and functions and re-check fields
|
if let Some(constructor) = &mut self.constructor {
|
||||||
|
match constructor.type_check(symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut constructor_diagnostics) => {
|
||||||
|
diagnostics.append(&mut constructor_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.functions
|
||||||
|
.iter_mut()
|
||||||
|
.map(|function| function.type_check(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.for_each(|diagnostic| diagnostics.push(diagnostic));
|
||||||
|
|
||||||
|
// 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 mut initialized_field_names: HashSet<&str> = HashSet::new();
|
||||||
|
|
||||||
|
for field in &self.fields {
|
||||||
|
if field.initializer().is_some() {
|
||||||
|
initialized_field_names.insert(field.declared_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check constructor
|
||||||
|
if let Some(constructor) = &self.constructor {
|
||||||
|
for statement in constructor.statements() {
|
||||||
|
match statement {
|
||||||
|
_ => {
|
||||||
|
// no-op for now, because we don't yet have assign statements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that all fields are present in the hash set
|
||||||
|
for field in &self.fields {
|
||||||
|
if !initialized_field_names.contains(field.declared_name()) {
|
||||||
|
diagnostics.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!("Field {} is not initialized.", field.declared_name()),
|
||||||
|
field.declared_name_source_range().start(),
|
||||||
|
field.declared_name_source_range().end(),
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if diagnostics.is_empty() {
|
if diagnostics.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -167,11 +215,11 @@ mod tests {
|
|||||||
use crate::parser::parse_compilation_unit;
|
use crate::parser::parse_compilation_unit;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn name_analysis_no_diagnostics() {
|
fn complete_example_no_diagnostics() {
|
||||||
let parse_result = parse_compilation_unit(
|
let parse_result = parse_compilation_unit(
|
||||||
"
|
"
|
||||||
class Foo
|
class Foo
|
||||||
mut bar: Int
|
mut bar: Int = 42
|
||||||
|
|
||||||
ctor(_bar: Int)
|
ctor(_bar: Int)
|
||||||
end
|
end
|
||||||
@ -210,5 +258,12 @@ mod tests {
|
|||||||
panic!("{:?}", diagnostics);
|
panic!("{:?}", diagnostics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match compilation_unit.type_check(&symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(diagnostics) => {
|
||||||
|
panic!("{:?}", diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,10 @@ impl Constructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn statements(&self) -> &[Statement] {
|
||||||
|
&self.statements
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gather_declared_names(
|
pub fn gather_declared_names(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
@ -124,4 +128,32 @@ impl Constructor {
|
|||||||
Err(statements_diagnostics)
|
Err(statements_diagnostics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let parameters_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.parameters
|
||||||
|
.iter_mut()
|
||||||
|
.map(|param| param.type_check(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !parameters_diagnostics.is_empty() {
|
||||||
|
return Err(parameters_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
let statements_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.statements
|
||||||
|
.iter_mut()
|
||||||
|
.map(|statement| statement.type_check(symbol_table, None))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if statements_diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(statements_diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,10 +3,8 @@ use crate::ast::ir_builder::IrBuilder;
|
|||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::ir::ir_return::IrReturn;
|
use crate::ir::ir_return::IrReturn;
|
||||||
use crate::ir::ir_statement::IrStatement;
|
use crate::ir::ir_statement::IrStatement;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use std::cell::RefCell;
|
use crate::type_info::TypeInfo;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub struct ExpressionStatement {
|
pub struct ExpressionStatement {
|
||||||
expression: Box<Expression>,
|
expression: Box<Expression>,
|
||||||
@ -37,15 +35,13 @@ impl ExpressionStatement {
|
|||||||
pub fn type_check(
|
pub fn type_check(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
is_last: bool,
|
must_return_type_info: Option<&TypeInfo>,
|
||||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
self.expression.type_check(symbol_table)?;
|
self.expression.type_check(symbol_table)?;
|
||||||
|
|
||||||
if is_last {
|
if must_return_type_info.is_some() {
|
||||||
let expression_type = self.expression.type_info();
|
let expression_type = self.expression.type_info();
|
||||||
let borrowed_symbol = function_symbol.borrow();
|
let return_type = must_return_type_info.unwrap();
|
||||||
let return_type = borrowed_symbol.return_type_info();
|
|
||||||
if !return_type.is_assignable_from(expression_type) {
|
if !return_type.is_assignable_from(expression_type) {
|
||||||
return Err(vec![Diagnostic::new(
|
return Err(vec![Diagnostic::new(
|
||||||
&format!(
|
&format!(
|
||||||
|
|||||||
@ -37,6 +37,18 @@ impl Field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declared_name(&self) -> &str {
|
||||||
|
&self.declared_name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_source_range(&self) -> &SourceRange {
|
||||||
|
&self.declared_name_source_range
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initializer(&self) -> Option<&Expression> {
|
||||||
|
self.initializer.as_ref().map(Box::as_ref)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gather_declared_names(
|
pub fn gather_declared_names(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
|
|||||||
@ -208,6 +208,7 @@ impl Function {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let function_symbol = self.function_symbol.as_ref().unwrap();
|
let function_symbol = self.function_symbol.as_ref().unwrap();
|
||||||
|
let return_type_info = function_symbol.borrow().return_type_info().clone();
|
||||||
let statements_len = self.statements.len();
|
let statements_len = self.statements.len();
|
||||||
|
|
||||||
// statements
|
// statements
|
||||||
@ -218,7 +219,11 @@ impl Function {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, statement)| {
|
.map(|(i, statement)| {
|
||||||
let is_last = i == statements_len - 1;
|
let is_last = i == statements_len - 1;
|
||||||
statement.type_check(symbol_table, is_last, function_symbol)
|
if is_last {
|
||||||
|
statement.type_check(symbol_table, Some(&return_type_info))
|
||||||
|
} else {
|
||||||
|
statement.type_check(symbol_table, None)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.filter_map(Result::err)
|
.filter_map(Result::err)
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|||||||
@ -36,7 +36,7 @@ impl Parameter {
|
|||||||
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
||||||
&self.declared_name,
|
&self.declared_name,
|
||||||
self.declared_name_source_range.clone(),
|
self.declared_name_source_range.clone(),
|
||||||
TypeInfo::from_declared_name(self.type_use.declared_name()),
|
TypeInfo::from_declared_name(self.type_use.declared_name()), // todo: this will blow up if type is a Class
|
||||||
));
|
));
|
||||||
match insert_result {
|
match insert_result {
|
||||||
Ok(parameter_symbol) => {
|
Ok(parameter_symbol) => {
|
||||||
|
|||||||
@ -2,10 +2,8 @@ use crate::ast::expression_statement::ExpressionStatement;
|
|||||||
use crate::ast::ir_builder::IrBuilder;
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use std::cell::RefCell;
|
use crate::type_info::TypeInfo;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Let(LetStatement),
|
Let(LetStatement),
|
||||||
@ -37,13 +35,12 @@ impl Statement {
|
|||||||
pub fn type_check(
|
pub fn type_check(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
is_last: bool,
|
must_return_type_info: Option<&TypeInfo>,
|
||||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Let(let_statement) => let_statement.type_check(symbol_table),
|
Statement::Let(let_statement) => let_statement.type_check(symbol_table),
|
||||||
Statement::Expression(expression_statement) => {
|
Statement::Expression(expression_statement) => {
|
||||||
expression_statement.type_check(symbol_table, is_last, function_symbol)
|
expression_statement.type_check(symbol_table, must_return_type_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ impl TypeUse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
Ok(()) // no-op, for now
|
Ok(()) // no-op, for now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user