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))
.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 } => {
Report::build(ReportKind::Error, path.clone(), span.lo)
.with_code("E_ID")

View file

@ -1,6 +1,9 @@
// Based on a cfg
use std::collections::{BTreeMap, HashMap, HashSet};
use std::{
collections::{BTreeMap, HashMap, HashSet},
fmt,
};
use edlang_span::Span;
use smallvec::SmallVec;
@ -207,13 +210,13 @@ pub struct SwitchTarget {
pub targets: Vec<usize>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct TypeInfo {
pub span: Option<Span>,
pub kind: TypeKind,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum TypeKind {
Unit,
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 {
I128,
I64,
@ -278,7 +324,7 @@ pub enum IntTy {
Isize,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub enum UintTy {
U128,
U64,
@ -288,7 +334,7 @@ pub enum UintTy {
Usize,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
pub enum FloatTy {
F32,
F64,

View file

@ -1,4 +1,5 @@
use edlang_ast::{Ident, Span};
use edlang_ir::{TypeInfo, TypeKind};
use thiserror::Error;
use crate::DefId;
@ -27,4 +28,10 @@ pub enum LoweringError {
IdNotFound { span: Span, id: DefId },
#[error("feature not yet implemented: {message}")]
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 {
lower_statement(&mut builder, stmt, &ret_ty.kind)?;
lower_statement(&mut builder, stmt, &ret_ty)?;
}
if !builder.statements.is_empty() {
@ -230,7 +230,7 @@ fn lower_function(
fn lower_statement(
builder: &mut BodyBuilder,
info: &ast::Statement,
ret_type: &TypeKind,
ret_type: &TypeInfo,
) -> Result<(), LoweringError> {
match info {
ast::Statement::Let(info) => lower_let(builder, info),
@ -249,7 +249,7 @@ fn lower_statement(
fn lower_while(
builder: &mut BodyBuilder,
info: &WhileStmt,
ret_type: &TypeKind,
ret_type: &TypeInfo,
) -> Result<(), LoweringError> {
let statements = std::mem::take(&mut builder.statements);
builder.body.blocks.push(BasicBlock {
@ -330,11 +330,17 @@ fn lower_while(
fn lower_if_stmt(
builder: &mut BodyBuilder,
info: &ast::IfStmt,
ret_type: &TypeKind,
ret_type: &TypeInfo,
) -> Result<(), LoweringError> {
let cond_ty = find_expr_type(builder, &info.condition).expect("couldnt find cond type");
let (condition, condition_ty, cond_span) =
lower_expr(builder, &info.condition, Some(&cond_ty))?;
let (condition, condition_ty, cond_span) = lower_expr(
builder,
&info.condition,
Some(&TypeInfo {
span: None,
kind: cond_ty,
}),
)?;
let local = builder.add_temp_local(TypeKind::Bool);
let place = Place {
@ -433,7 +439,16 @@ fn lower_if_stmt(
fn lower_let(builder: &mut BodyBuilder, info: &ast::LetStmt) -> Result<(), LoweringError> {
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();
builder.statements.push(Statement {
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> {
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 {
match &ty {
match &ty.kind {
TypeKind::Ptr(is_mut, inner) => {
if !is_mut {
panic!("trying to mutate non mut ptr");
}
ty = inner.kind.clone();
ty = *inner.clone();
}
TypeKind::Ref(is_mut, inner) => {
if !is_mut {
panic!("trying to mutate non mut ref");
}
ty = inner.kind.clone();
ty = *inner.clone();
}
_ => unreachable!(),
}
@ -546,24 +565,68 @@ fn find_expr_type(builder: &mut BodyBuilder, info: &ast::Expression) -> Option<T
fn lower_expr(
builder: &mut BodyBuilder,
info: &ast::Expression,
type_hint: Option<&TypeKind>,
type_hint: Option<&TypeInfo>,
) -> Result<(ir::RValue, TypeKind, Span), LoweringError> {
Ok(match info {
ast::Expression::Value(info) => {
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)
}
ast::Expression::FnCall(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)
}
ast::Expression::Unary(_, _) => todo!(),
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) => {
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.
let mut value = match value {
RValue::Use(op, _) => match op {
@ -580,14 +643,24 @@ fn lower_expr(
}
ast::Expression::AsRef(inner, mutable) => {
let type_hint = match type_hint {
Some(inner) => match inner {
TypeKind::Ref(_, inner) => Some(&inner.kind),
Some(inner) => match &inner.kind {
TypeKind::Ref(_, inner) => Some(inner.as_ref()),
_ => unreachable!(),
},
None => None,
};
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.
value = match value {
RValue::Use(op, _span) => RValue::Ref(*mutable, op, span),
@ -652,7 +725,7 @@ fn lower_expr(
.projection
.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))?;
@ -672,20 +745,34 @@ fn lower_binary_expr(
lhs: &ast::Expression,
op: &ast::BinaryOp,
rhs: &ast::Expression,
type_hint: Option<&TypeKind>,
type_hint: Option<&TypeInfo>,
) -> Result<(ir::RValue, TypeKind, Span), LoweringError> {
trace!("lowering binary op: {:?}", op);
let (lhs, lhs_ty, _) = if type_hint.is_none() {
let ty = find_expr_type(builder, lhs)
.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 {
lower_expr(builder, lhs, type_hint)?
};
let (rhs, rhs_ty, _) = if type_hint.is_none() {
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 {
lower_expr(builder, rhs, type_hint)?
};
@ -827,7 +914,7 @@ fn lower_fn_call(
let mut args = Vec::new();
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);
}
@ -861,7 +948,7 @@ fn lower_fn_call(
fn lower_value(
builder: &mut BodyBuilder,
info: &ast::ValueExpr,
type_hint: Option<&TypeKind>,
type_hint: Option<&TypeInfo>,
) -> Result<(Operand, TypeKind, Span), LoweringError> {
Ok(match info {
ast::ValueExpr::Bool { value, span } => (
@ -892,7 +979,7 @@ fn lower_value(
),
ast::ValueExpr::Int { value, span } => {
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::IntTy::I128 => (
ir::TypeKind::Int(ir::IntTy::I128),
@ -962,7 +1049,7 @@ fn lower_value(
)
}
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 {
ir::FloatTy::F32 => (
ir::Operand::Constant(ir::ConstData {
@ -975,7 +1062,7 @@ fn lower_value(
value.parse().unwrap(),
))),
}),
type_hint.clone(),
type_hint.kind.clone(),
*span,
),
ir::FloatTy::F64 => (
@ -989,7 +1076,7 @@ fn lower_value(
value.parse().unwrap(),
))),
}),
type_hint.clone(),
type_hint.kind.clone(),
*span,
),
},
@ -1008,7 +1095,7 @@ fn lower_value(
fn lower_return(
builder: &mut BodyBuilder,
info: &ast::ReturnStmt,
return_type: &TypeKind,
return_type: &TypeInfo,
) -> Result<(), LoweringError> {
if let Some(value_expr) = &info.value {
let (value, _ty, span) = lower_expr(builder, value_expr, Some(return_type))?;