Add return-type checking and fix string literal type-info bug.
This commit is contained in:
parent
705436ba61
commit
7de866cf9d
@ -3,7 +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::FunctionSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct ExpressionStatement {
|
||||
expression: Box<Expression>,
|
||||
@ -31,8 +34,30 @@ impl ExpressionStatement {
|
||||
self.expression.check_name_usages(symbol_table)
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||
self.expression.type_check(symbol_table)
|
||||
pub fn type_check(
|
||||
&mut self,
|
||||
symbol_table: &SymbolTable,
|
||||
is_last: bool,
|
||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
self.expression.type_check(symbol_table)?;
|
||||
|
||||
if is_last {
|
||||
let expression_type = self.expression.type_info();
|
||||
let borrowed_symbol = function_symbol.borrow();
|
||||
let return_type = borrowed_symbol.return_type_info();
|
||||
if !return_type.is_assignable_from(expression_type) {
|
||||
return Err(vec![Diagnostic::new(
|
||||
&format!(
|
||||
"Incompatible type on return expression: expected {} but found {}",
|
||||
return_type, expression_type
|
||||
),
|
||||
self.expression.source_range().start(),
|
||||
self.expression.source_range().end(),
|
||||
)]);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_ir(
|
||||
|
||||
@ -168,12 +168,19 @@ impl Function {
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let function_symbol = self.function_symbol.as_ref().unwrap();
|
||||
let statements_len = self.statements.len();
|
||||
|
||||
// statements
|
||||
diagnostics.append(
|
||||
&mut self
|
||||
.statements
|
||||
.iter_mut()
|
||||
.map(|statement| statement.type_check(symbol_table))
|
||||
.enumerate()
|
||||
.map(|(i, statement)| {
|
||||
let is_last = i == statements_len - 1;
|
||||
statement.type_check(symbol_table, is_last, function_symbol)
|
||||
})
|
||||
.filter_map(Result::err)
|
||||
.flatten()
|
||||
.collect(),
|
||||
|
||||
@ -2,7 +2,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::FunctionSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum Statement {
|
||||
Let(LetStatement),
|
||||
@ -31,11 +34,16 @@ impl Statement {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||
pub fn type_check(
|
||||
&mut self,
|
||||
symbol_table: &SymbolTable,
|
||||
is_last: bool,
|
||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
match self {
|
||||
Statement::Let(let_statement) => let_statement.type_check(symbol_table),
|
||||
Statement::Expression(expression_statement) => {
|
||||
expression_statement.type_check(symbol_table)
|
||||
expression_statement.type_check(symbol_table, is_last, function_symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ pub struct StringLiteral {
|
||||
|
||||
impl StringLiteral {
|
||||
pub fn new(content: &str, source_range: SourceRange) -> Self {
|
||||
const TYPE_INFO: TypeInfo = TypeInfo::Integer;
|
||||
const TYPE_INFO: TypeInfo = TypeInfo::String;
|
||||
Self {
|
||||
content: content.into(),
|
||||
source_range,
|
||||
|
||||
@ -68,9 +68,15 @@ impl TypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_result(&self, _rhs: &Self) -> TypeInfo {
|
||||
pub fn add_result(&self, rhs: &Self) -> TypeInfo {
|
||||
match self {
|
||||
TypeInfo::Integer => TypeInfo::Integer,
|
||||
TypeInfo::Integer => match rhs {
|
||||
TypeInfo::Integer => TypeInfo::Integer,
|
||||
TypeInfo::String => TypeInfo::String,
|
||||
_ => panic!(
|
||||
"Adding things other than integers/strings to integer not yet supported."
|
||||
),
|
||||
},
|
||||
TypeInfo::String => TypeInfo::String,
|
||||
_ => panic!("Adding things other than integers and strings not yet supported"),
|
||||
}
|
||||
|
||||
@ -137,3 +137,47 @@ mod e2e_tests {
|
||||
assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1))
|
||||
}
|
||||
}
|
||||
|
||||
#[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<Diagnostic> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user