mirror of
https://github.com/edg-l/edlang.git
synced 2024-11-21 23:48:24 +00:00
feat: check type correctness
This commit is contained in:
parent
cc1e367982
commit
a2a3bdbb3e
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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))?;
|
||||
|
|
Loading…
Reference in a new issue