feat: check type correctness

This commit is contained in:
Edgar 2024-02-27 08:27:12 +01:00
parent cc1e367982
commit a2a3bdbb3e
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
4 changed files with 193 additions and 32 deletions

View file

@ -84,6 +84,27 @@ pub fn lowering_error_to_report(
.with_message(format!("Unresolved type {:?}.", name)) .with_message(format!("Unresolved type {:?}.", name))
.finish() .finish()
}, },
LoweringError::UnexpectedType { span, found, expected } => {
let mut labels = vec![
Label::new((path.clone(), span.into()))
.with_message(format!("Unexpected type '{}', expected '{}'", found, expected.kind))
.with_color(colors.next())
];
if let Some(span) = expected.span {
labels.push(
Label::new((path.clone(), span.into()))
.with_message(format!("expected '{}' due to this type", expected.kind))
.with_color(colors.next())
);
}
Report::build(ReportKind::Error, path.clone(), span.lo)
.with_code("E3")
.with_labels(labels)
.with_message(format!("expected type {}.", expected.kind))
.finish()
},
LoweringError::IdNotFound { span, id } => { LoweringError::IdNotFound { span, id } => {
Report::build(ReportKind::Error, path.clone(), span.lo) Report::build(ReportKind::Error, path.clone(), span.lo)
.with_code("E_ID") .with_code("E_ID")

View file

@ -1,6 +1,9 @@
// Based on a cfg // Based on a cfg
use std::collections::{BTreeMap, HashMap, HashSet}; use std::{
collections::{BTreeMap, HashMap, HashSet},
fmt,
};
use edlang_span::Span; use edlang_span::Span;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -207,13 +210,13 @@ pub struct SwitchTarget {
pub targets: Vec<usize>, pub targets: Vec<usize>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct TypeInfo { pub struct TypeInfo {
pub span: Option<Span>, pub span: Option<Span>,
pub kind: TypeKind, pub kind: TypeKind,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum TypeKind { pub enum TypeKind {
Unit, Unit,
Bool, Bool,
@ -268,7 +271,50 @@ impl TypeKind {
} }
} }
#[derive(Debug, Clone)] impl fmt::Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeKind::Unit => write!(f, "()"),
TypeKind::Bool => write!(f, "bool"),
TypeKind::Char => write!(f, "char"),
TypeKind::Int(ty) => match ty {
IntTy::I128 => write!(f, "i128"),
IntTy::I64 => write!(f, "i64"),
IntTy::I32 => write!(f, "i32"),
IntTy::I16 => write!(f, "i16"),
IntTy::I8 => write!(f, "i8"),
IntTy::Isize => write!(f, "isize"),
},
TypeKind::Uint(ty) => match ty {
UintTy::U128 => write!(f, "u128"),
UintTy::U64 => write!(f, "u64"),
UintTy::U32 => write!(f, "u32"),
UintTy::U16 => write!(f, "u16"),
UintTy::U8 => write!(f, "u8"),
UintTy::Usize => write!(f, "usize"),
},
TypeKind::Float(ty) => match ty {
FloatTy::F32 => write!(f, "f64"),
FloatTy::F64 => write!(f, "f32"),
},
TypeKind::FnDef(_, _) => todo!(),
TypeKind::Str => write!(f, "str"),
TypeKind::Ptr(is_mut, inner) => {
let word = if *is_mut { "mut" } else { "const" };
write!(f, "*{word} {}", inner.kind)
}
TypeKind::Ref(is_mut, inner) => {
let word = if *is_mut { "mut" } else { "const" };
write!(f, "&{word} {}", inner.kind)
}
TypeKind::Struct(_) => todo!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub enum IntTy { pub enum IntTy {
I128, I128,
I64, I64,
@ -278,7 +324,7 @@ pub enum IntTy {
Isize, Isize,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub enum UintTy { pub enum UintTy {
U128, U128,
U64, U64,
@ -288,7 +334,7 @@ pub enum UintTy {
Usize, Usize,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub enum FloatTy { pub enum FloatTy {
F32, F32,
F64, F64,

View file

@ -1,4 +1,5 @@
use edlang_ast::{Ident, Span}; use edlang_ast::{Ident, Span};
use edlang_ir::{TypeInfo, TypeKind};
use thiserror::Error; use thiserror::Error;
use crate::DefId; use crate::DefId;
@ -27,4 +28,10 @@ pub enum LoweringError {
IdNotFound { span: Span, id: DefId }, IdNotFound { span: Span, id: DefId },
#[error("feature not yet implemented: {message}")] #[error("feature not yet implemented: {message}")]
NotYetImplemented { span: Span, message: &'static str }, NotYetImplemented { span: Span, message: &'static str },
#[error("unexpected type")]
UnexpectedType {
span: Span,
found: TypeKind,
expected: TypeInfo,
},
} }

View file

@ -209,7 +209,7 @@ fn lower_function(
} }
for stmt in &func.body.body { for stmt in &func.body.body {
lower_statement(&mut builder, stmt, &ret_ty.kind)?; lower_statement(&mut builder, stmt, &ret_ty)?;
} }
if !builder.statements.is_empty() { if !builder.statements.is_empty() {
@ -230,7 +230,7 @@ fn lower_function(
fn lower_statement( fn lower_statement(
builder: &mut BodyBuilder, builder: &mut BodyBuilder,
info: &ast::Statement, info: &ast::Statement,
ret_type: &TypeKind, ret_type: &TypeInfo,
) -> Result<(), LoweringError> { ) -> Result<(), LoweringError> {
match info { match info {
ast::Statement::Let(info) => lower_let(builder, info), ast::Statement::Let(info) => lower_let(builder, info),
@ -249,7 +249,7 @@ fn lower_statement(
fn lower_while( fn lower_while(
builder: &mut BodyBuilder, builder: &mut BodyBuilder,
info: &WhileStmt, info: &WhileStmt,
ret_type: &TypeKind, ret_type: &TypeInfo,
) -> Result<(), LoweringError> { ) -> Result<(), LoweringError> {
let statements = std::mem::take(&mut builder.statements); let statements = std::mem::take(&mut builder.statements);
builder.body.blocks.push(BasicBlock { builder.body.blocks.push(BasicBlock {
@ -330,11 +330,17 @@ fn lower_while(
fn lower_if_stmt( fn lower_if_stmt(
builder: &mut BodyBuilder, builder: &mut BodyBuilder,
info: &ast::IfStmt, info: &ast::IfStmt,
ret_type: &TypeKind, ret_type: &TypeInfo,
) -> Result<(), LoweringError> { ) -> Result<(), LoweringError> {
let cond_ty = find_expr_type(builder, &info.condition).expect("couldnt find cond type"); let cond_ty = find_expr_type(builder, &info.condition).expect("couldnt find cond type");
let (condition, condition_ty, cond_span) = let (condition, condition_ty, cond_span) = lower_expr(
lower_expr(builder, &info.condition, Some(&cond_ty))?; builder,
&info.condition,
Some(&TypeInfo {
span: None,
kind: cond_ty,
}),
)?;
let local = builder.add_temp_local(TypeKind::Bool); let local = builder.add_temp_local(TypeKind::Bool);
let place = Place { let place = Place {
@ -433,7 +439,16 @@ fn lower_if_stmt(
fn lower_let(builder: &mut BodyBuilder, info: &ast::LetStmt) -> Result<(), LoweringError> { fn lower_let(builder: &mut BodyBuilder, info: &ast::LetStmt) -> Result<(), LoweringError> {
let ty = lower_type(&builder.ctx, &info.r#type, builder.local_module)?; let ty = lower_type(&builder.ctx, &info.r#type, builder.local_module)?;
let (rvalue, _ty, _span) = lower_expr(builder, &info.value, Some(&ty.kind))?; let (rvalue, found_ty, _span) = lower_expr(builder, &info.value, Some(&ty))?;
if ty.kind != found_ty {
return Err(LoweringError::UnexpectedType {
span: info.span,
found: found_ty,
expected: ty.clone(),
});
}
let local_idx = builder.name_to_local.get(&info.name.name).copied().unwrap(); let local_idx = builder.name_to_local.get(&info.name.name).copied().unwrap();
builder.statements.push(Statement { builder.statements.push(Statement {
span: Some(info.name.span), span: Some(info.name.span),
@ -454,21 +469,25 @@ fn lower_let(builder: &mut BodyBuilder, info: &ast::LetStmt) -> Result<(), Lower
} }
fn lower_assign(builder: &mut BodyBuilder, info: &ast::AssignStmt) -> Result<(), LoweringError> { fn lower_assign(builder: &mut BodyBuilder, info: &ast::AssignStmt) -> Result<(), LoweringError> {
let (mut place, mut ty, _span) = lower_path(builder, &info.name)?; let (mut place, ty, _span) = lower_path(builder, &info.name)?;
let mut ty = TypeInfo {
span: None,
kind: ty,
};
for _ in 0..info.deref_times { for _ in 0..info.deref_times {
match &ty { match &ty.kind {
TypeKind::Ptr(is_mut, inner) => { TypeKind::Ptr(is_mut, inner) => {
if !is_mut { if !is_mut {
panic!("trying to mutate non mut ptr"); panic!("trying to mutate non mut ptr");
} }
ty = inner.kind.clone(); ty = *inner.clone();
} }
TypeKind::Ref(is_mut, inner) => { TypeKind::Ref(is_mut, inner) => {
if !is_mut { if !is_mut {
panic!("trying to mutate non mut ref"); panic!("trying to mutate non mut ref");
} }
ty = inner.kind.clone(); ty = *inner.clone();
} }
_ => unreachable!(), _ => unreachable!(),
} }
@ -546,24 +565,68 @@ fn find_expr_type(builder: &mut BodyBuilder, info: &ast::Expression) -> Option<T
fn lower_expr( fn lower_expr(
builder: &mut BodyBuilder, builder: &mut BodyBuilder,
info: &ast::Expression, info: &ast::Expression,
type_hint: Option<&TypeKind>, type_hint: Option<&TypeInfo>,
) -> Result<(ir::RValue, TypeKind, Span), LoweringError> { ) -> Result<(ir::RValue, TypeKind, Span), LoweringError> {
Ok(match info { Ok(match info {
ast::Expression::Value(info) => { ast::Expression::Value(info) => {
let (value, ty, span) = lower_value(builder, info, type_hint)?; let (value, ty, span) = lower_value(builder, info, type_hint)?;
if let Some(expected_ty) = type_hint {
if expected_ty.kind != ty {
return Err(LoweringError::UnexpectedType {
span,
found: ty,
expected: expected_ty.clone(),
});
}
}
(ir::RValue::Use(value, span), ty, span) (ir::RValue::Use(value, span), ty, span)
} }
ast::Expression::FnCall(info) => { ast::Expression::FnCall(info) => {
let (value, ty, span) = lower_fn_call(builder, info)?; let (value, ty, span) = lower_fn_call(builder, info)?;
if let Some(expected_ty) = type_hint {
if expected_ty.kind != ty {
return Err(LoweringError::UnexpectedType {
span,
found: ty,
expected: expected_ty.clone(),
});
}
}
(ir::RValue::Use(value, span), ty, span) (ir::RValue::Use(value, span), ty, span)
} }
ast::Expression::Unary(_, _) => todo!(), ast::Expression::Unary(_, _) => todo!(),
ast::Expression::Binary(lhs, op, rhs) => { ast::Expression::Binary(lhs, op, rhs) => {
lower_binary_expr(builder, lhs, op, rhs, type_hint)? let result = lower_binary_expr(builder, lhs, op, rhs, type_hint)?;
if let Some(expected_ty) = type_hint {
if expected_ty.kind != result.1 {
return Err(LoweringError::UnexpectedType {
span: result.2,
found: result.1,
expected: expected_ty.clone(),
});
}
}
result
} }
ast::Expression::Deref(inner) => { ast::Expression::Deref(inner) => {
let (value, ty, span) = lower_expr(builder, inner, type_hint)?; let (value, ty, span) = lower_expr(builder, inner, type_hint)?;
if let Some(expected_ty) = type_hint {
if expected_ty.kind != ty {
return Err(LoweringError::UnexpectedType {
span,
found: ty,
expected: expected_ty.clone(),
});
}
}
// check if its a use directly, to avoid a temporary. // check if its a use directly, to avoid a temporary.
let mut value = match value { let mut value = match value {
RValue::Use(op, _) => match op { RValue::Use(op, _) => match op {
@ -580,14 +643,24 @@ fn lower_expr(
} }
ast::Expression::AsRef(inner, mutable) => { ast::Expression::AsRef(inner, mutable) => {
let type_hint = match type_hint { let type_hint = match type_hint {
Some(inner) => match inner { Some(inner) => match &inner.kind {
TypeKind::Ref(_, inner) => Some(&inner.kind), TypeKind::Ref(_, inner) => Some(inner.as_ref()),
_ => unreachable!(), _ => unreachable!(),
}, },
None => None, None => None,
}; };
let (mut value, ty, span) = lower_expr(builder, inner, type_hint)?; let (mut value, ty, span) = lower_expr(builder, inner, type_hint)?;
if let Some(expected_ty) = type_hint {
if expected_ty.kind != ty {
return Err(LoweringError::UnexpectedType {
span,
found: ty,
expected: expected_ty.clone(),
});
}
}
// check if its a use directly, to avoid a temporary. // check if its a use directly, to avoid a temporary.
value = match value { value = match value {
RValue::Use(op, _span) => RValue::Ref(*mutable, op, span), RValue::Use(op, _span) => RValue::Ref(*mutable, op, span),
@ -652,7 +725,7 @@ fn lower_expr(
.projection .projection
.push(PlaceElem::Field { field_idx: idx }); .push(PlaceElem::Field { field_idx: idx });
let variant = &struct_body.variants[idx].ty.kind; let variant = &struct_body.variants[idx].ty;
let (value, _value_ty, span) = lower_expr(builder, &value.value, Some(variant))?; let (value, _value_ty, span) = lower_expr(builder, &value.value, Some(variant))?;
@ -672,20 +745,34 @@ fn lower_binary_expr(
lhs: &ast::Expression, lhs: &ast::Expression,
op: &ast::BinaryOp, op: &ast::BinaryOp,
rhs: &ast::Expression, rhs: &ast::Expression,
type_hint: Option<&TypeKind>, type_hint: Option<&TypeInfo>,
) -> Result<(ir::RValue, TypeKind, Span), LoweringError> { ) -> Result<(ir::RValue, TypeKind, Span), LoweringError> {
trace!("lowering binary op: {:?}", op); trace!("lowering binary op: {:?}", op);
let (lhs, lhs_ty, _) = if type_hint.is_none() { let (lhs, lhs_ty, _) = if type_hint.is_none() {
let ty = find_expr_type(builder, lhs) let ty = find_expr_type(builder, lhs)
.unwrap_or_else(|| find_expr_type(builder, rhs).expect("cant find type")); .unwrap_or_else(|| find_expr_type(builder, rhs).expect("cant find type"));
lower_expr(builder, lhs, Some(&ty))? lower_expr(
builder,
lhs,
Some(&TypeInfo {
span: None,
kind: ty,
}),
)?
} else { } else {
lower_expr(builder, lhs, type_hint)? lower_expr(builder, lhs, type_hint)?
}; };
let (rhs, rhs_ty, _) = if type_hint.is_none() { let (rhs, rhs_ty, _) = if type_hint.is_none() {
let ty = find_expr_type(builder, rhs).unwrap_or(lhs_ty.clone()); let ty = find_expr_type(builder, rhs).unwrap_or(lhs_ty.clone());
lower_expr(builder, rhs, Some(&ty))? lower_expr(
builder,
rhs,
Some(&TypeInfo {
span: None,
kind: ty,
}),
)?
} else { } else {
lower_expr(builder, rhs, type_hint)? lower_expr(builder, rhs, type_hint)?
}; };
@ -827,7 +914,7 @@ fn lower_fn_call(
let mut args = Vec::new(); let mut args = Vec::new();
for (arg, arg_ty) in info.params.iter().zip(args_ty) { for (arg, arg_ty) in info.params.iter().zip(args_ty) {
let (rvalue, _rvalue_ty, _span) = lower_expr(builder, arg, Some(&arg_ty.kind))?; let (rvalue, _rvalue_ty, _span) = lower_expr(builder, arg, Some(&arg_ty))?;
args.push(rvalue); args.push(rvalue);
} }
@ -861,7 +948,7 @@ fn lower_fn_call(
fn lower_value( fn lower_value(
builder: &mut BodyBuilder, builder: &mut BodyBuilder,
info: &ast::ValueExpr, info: &ast::ValueExpr,
type_hint: Option<&TypeKind>, type_hint: Option<&TypeInfo>,
) -> Result<(Operand, TypeKind, Span), LoweringError> { ) -> Result<(Operand, TypeKind, Span), LoweringError> {
Ok(match info { Ok(match info {
ast::ValueExpr::Bool { value, span } => ( ast::ValueExpr::Bool { value, span } => (
@ -892,7 +979,7 @@ fn lower_value(
), ),
ast::ValueExpr::Int { value, span } => { ast::ValueExpr::Int { value, span } => {
let (ty, val) = match type_hint { let (ty, val) = match type_hint {
Some(type_hint) => match &type_hint { Some(type_hint) => match &type_hint.kind {
ir::TypeKind::Int(int_type) => match int_type { ir::TypeKind::Int(int_type) => match int_type {
ir::IntTy::I128 => ( ir::IntTy::I128 => (
ir::TypeKind::Int(ir::IntTy::I128), ir::TypeKind::Int(ir::IntTy::I128),
@ -962,7 +1049,7 @@ fn lower_value(
) )
} }
ast::ValueExpr::Float { value, span } => match type_hint { ast::ValueExpr::Float { value, span } => match type_hint {
Some(type_hint) => match &type_hint { Some(type_hint) => match &type_hint.kind {
TypeKind::Float(float_ty) => match float_ty { TypeKind::Float(float_ty) => match float_ty {
ir::FloatTy::F32 => ( ir::FloatTy::F32 => (
ir::Operand::Constant(ir::ConstData { ir::Operand::Constant(ir::ConstData {
@ -975,7 +1062,7 @@ fn lower_value(
value.parse().unwrap(), value.parse().unwrap(),
))), ))),
}), }),
type_hint.clone(), type_hint.kind.clone(),
*span, *span,
), ),
ir::FloatTy::F64 => ( ir::FloatTy::F64 => (
@ -989,7 +1076,7 @@ fn lower_value(
value.parse().unwrap(), value.parse().unwrap(),
))), ))),
}), }),
type_hint.clone(), type_hint.kind.clone(),
*span, *span,
), ),
}, },
@ -1008,7 +1095,7 @@ fn lower_value(
fn lower_return( fn lower_return(
builder: &mut BodyBuilder, builder: &mut BodyBuilder,
info: &ast::ReturnStmt, info: &ast::ReturnStmt,
return_type: &TypeKind, return_type: &TypeInfo,
) -> Result<(), LoweringError> { ) -> Result<(), LoweringError> {
if let Some(value_expr) = &info.value { if let Some(value_expr) = &info.value {
let (value, _ty, span) = lower_expr(builder, value_expr, Some(return_type))?; let (value, _ty, span) = lower_expr(builder, value_expr, Some(return_type))?;