From a2a3bdbb3e5d50ce068260d7fd9be41882ac7db7 Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Tue, 27 Feb 2024 08:27:12 +0100 Subject: [PATCH] feat: check type correctness --- lib/edlang_check/src/lib.rs | 21 +++++ lib/edlang_ir/src/lib.rs | 58 +++++++++++-- lib/edlang_lowering/src/errors.rs | 7 ++ lib/edlang_lowering/src/lib.rs | 139 ++++++++++++++++++++++++------ 4 files changed, 193 insertions(+), 32 deletions(-) diff --git a/lib/edlang_check/src/lib.rs b/lib/edlang_check/src/lib.rs index bdcb623b0a..3a21fffb4b 100644 --- a/lib/edlang_check/src/lib.rs +++ b/lib/edlang_check/src/lib.rs @@ -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") diff --git a/lib/edlang_ir/src/lib.rs b/lib/edlang_ir/src/lib.rs index 9afa1d6afb..eeb8f1dd6d 100644 --- a/lib/edlang_ir/src/lib.rs +++ b/lib/edlang_ir/src/lib.rs @@ -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, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct TypeInfo { pub span: Option, 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, diff --git a/lib/edlang_lowering/src/errors.rs b/lib/edlang_lowering/src/errors.rs index 259c9cc617..a964e8ecf4 100644 --- a/lib/edlang_lowering/src/errors.rs +++ b/lib/edlang_lowering/src/errors.rs @@ -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, + }, } diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index 9ec5eda5ff..204e7f7a9d 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -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, + 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))?;