use super::{typecheck::ErrorKind, typerefs::TypeRefs, VagueType as Vague, *}; #[derive(Debug, Clone)] pub enum ReturnTypeOther { Import(Metadata), Let(Metadata), Set(Metadata), EmptyBlock(Metadata), NoBlockReturn(Metadata), IndexingNonArray(Metadata), DerefNonBorrow(Metadata), Loop, } impl TypeKind { pub fn collapse_into(&self, other: &TypeKind) -> Result { if self == other { return Ok(self.clone()); } match (self, other) { (TypeKind::Vague(Vague::Integer), other) | (other, TypeKind::Vague(Vague::Integer)) => { match other { TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Integer)), TypeKind::Vague(Vague::Integer) => Ok(TypeKind::Vague(Vague::Integer)), TypeKind::I8 | TypeKind::I16 | TypeKind::I32 | TypeKind::I64 | TypeKind::I128 | TypeKind::U8 | TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 => Ok(other.clone()), _ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())), } } (TypeKind::Vague(Vague::Decimal), other) | (other, TypeKind::Vague(Vague::Decimal)) => { match other { TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Decimal)), TypeKind::Vague(Vague::Decimal) => Ok(TypeKind::Vague(Vague::Decimal)), TypeKind::F16 | TypeKind::F32B | TypeKind::F32 | TypeKind::F64 | TypeKind::F80 | TypeKind::F128 | TypeKind::F128PPC => Ok(other.clone()), _ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())), } } (TypeKind::Vague(Vague::Unknown), other) | (other, TypeKind::Vague(Vague::Unknown)) => { Ok(other.clone()) } (TypeKind::Borrow(val1, mut1), TypeKind::Borrow(val2, mut2)) => { // Extracted to give priority for other collapse-error let collapsed = val1.collapse_into(val2)?; if mut1 == mut2 { Ok(TypeKind::Borrow(Box::new(collapsed), *mut1 && *mut2)) } else { Err(ErrorKind::TypesDifferMutability( self.clone(), other.clone(), )) } } (TypeKind::UserPtr(val1), TypeKind::UserPtr(val2)) => { Ok(TypeKind::UserPtr(Box::new(val1.collapse_into(val2)?))) } _ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())), } } pub fn cast_into(&self, other: &TypeKind) -> Result { if let Ok(collapsed) = self.collapse_into(other) { Ok(collapsed) } else { let self_cat = self.category(); let other_cat = other.category(); match (self, other) { (TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Ok(other.clone()), (TypeKind::Char, TypeKind::U8) => Ok(other.clone()), (TypeKind::U8, TypeKind::Char) => Ok(other.clone()), _ => match (&self_cat, &other_cat) { (TypeCategory::Integer, TypeCategory::Integer) => Ok(other.clone()), (TypeCategory::Integer, TypeCategory::Real) => Ok(other.clone()), (TypeCategory::Real, TypeCategory::Integer) => Ok(other.clone()), (TypeCategory::Real, TypeCategory::Real) => Ok(other.clone()), _ => Err(ErrorKind::NotCastableTo(self.clone(), other.clone())), }, } } } /// Return the type that is the result of a binary operator between two /// values of this type pub fn binop_type(&self, op: &BinaryOperator) -> TypeKind { // TODO make some type of mechanism that allows to binop two values of // differing types.. // TODO Return None for arrays later match op { BinaryOperator::Add => self.clone(), BinaryOperator::Minus => self.clone(), BinaryOperator::Mult => self.clone(), BinaryOperator::And => TypeKind::Bool, BinaryOperator::Cmp(_) => TypeKind::Bool, BinaryOperator::Div => self.clone(), BinaryOperator::Mod => self.clone(), } } /// Reverse of binop_type, where the given hint is the known required output /// type of the binop, and the output is the hint for the lhs/rhs type. pub fn binop_hint(&self, op: &BinaryOperator) -> Option { match op { BinaryOperator::Add | BinaryOperator::Minus | BinaryOperator::Mult | BinaryOperator::Div | BinaryOperator::Mod => Some(self.clone()), BinaryOperator::And => None, BinaryOperator::Cmp(_) => None, } } pub fn signed(&self) -> bool { match self { TypeKind::Bool => false, TypeKind::I8 => true, TypeKind::I16 => true, TypeKind::I32 => true, TypeKind::I64 => true, TypeKind::I128 => true, TypeKind::U8 => false, TypeKind::U16 => false, TypeKind::U32 => false, TypeKind::U64 => false, TypeKind::U128 => false, TypeKind::Void => false, TypeKind::Char => false, TypeKind::Array(..) => false, TypeKind::CustomType(..) => false, TypeKind::CodegenPtr(..) => false, TypeKind::Vague(..) => false, TypeKind::Borrow(..) => false, TypeKind::UserPtr(..) => false, TypeKind::F16 => true, TypeKind::F32B => true, TypeKind::F32 => true, TypeKind::F64 => true, TypeKind::F128 => true, TypeKind::F80 => true, TypeKind::F128PPC => true, } } pub fn size_of(&self) -> u64 { match self { TypeKind::Bool => 1, TypeKind::I8 => 8, TypeKind::U8 => 8, TypeKind::I16 => 16, TypeKind::U16 => 16, TypeKind::I32 => 32, TypeKind::U32 => 32, TypeKind::I64 => 64, TypeKind::U64 => 64, TypeKind::I128 => 128, TypeKind::U128 => 128, TypeKind::Void => 0, TypeKind::Char => 8, TypeKind::Array(type_kind, len) => type_kind.size_of() * (*len as u64), TypeKind::CustomType(..) => 32, TypeKind::CodegenPtr(_) => 64, TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"), TypeKind::Borrow(..) => 64, TypeKind::UserPtr(_) => 64, TypeKind::F16 => 16, TypeKind::F32B => 16, TypeKind::F32 => 32, TypeKind::F64 => 64, TypeKind::F128 => 128, TypeKind::F80 => 80, TypeKind::F128PPC => 128, } } pub fn alignment(&self) -> u32 { match self { TypeKind::Bool => 1, TypeKind::I8 => 8, TypeKind::U8 => 8, TypeKind::I16 => 16, TypeKind::U16 => 16, TypeKind::I32 => 32, TypeKind::U32 => 32, TypeKind::I64 => 64, TypeKind::U64 => 64, TypeKind::I128 => 128, TypeKind::U128 => 128, TypeKind::Void => 0, TypeKind::Char => 8, TypeKind::Array(type_kind, _) => type_kind.alignment(), TypeKind::CustomType(..) => 32, TypeKind::CodegenPtr(_) => 64, TypeKind::Vague(_) => panic!("Tried to sizeof a vague type!"), TypeKind::Borrow(_, _) => 64, TypeKind::UserPtr(_) => 64, TypeKind::F16 => 16, TypeKind::F32B => 16, TypeKind::F32 => 32, TypeKind::F64 => 64, TypeKind::F128 => 128, TypeKind::F80 => 80, TypeKind::F128PPC => 128, } } pub fn is_mutable(&self) -> bool { match self { TypeKind::Borrow(_, false) => false, _ => true, } } pub fn category(&self) -> TypeCategory { match self { TypeKind::I8 | TypeKind::I16 | TypeKind::I32 | TypeKind::I64 | TypeKind::I128 | TypeKind::U8 | TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 | TypeKind::Char => TypeCategory::Integer, TypeKind::F16 | TypeKind::F32B | TypeKind::F32 | TypeKind::F64 | TypeKind::F128 | TypeKind::F80 | TypeKind::F128PPC => TypeCategory::Real, TypeKind::Void => TypeCategory::Other, TypeKind::Bool => TypeCategory::Other, TypeKind::Array(_, _) => TypeCategory::Other, TypeKind::CustomType(..) => TypeCategory::Other, TypeKind::Borrow(_, _) => TypeCategory::Other, TypeKind::UserPtr(_) => TypeCategory::Other, TypeKind::CodegenPtr(_) => TypeCategory::Other, TypeKind::Vague(vague_type) => match vague_type { VagueType::Unknown => TypeCategory::Other, VagueType::Integer => TypeCategory::Integer, VagueType::Decimal => TypeCategory::Real, VagueType::TypeRef(_) => TypeCategory::TypeRef, }, } } } #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum TypeCategory { Integer, Real, Other, TypeRef, } impl StructType { pub fn get_field_ty(&self, name: &String) -> Option<&TypeKind> { self.0 .iter() .find(|StructField(n, _, _)| n == name) .map(|StructField(_, ty, _)| ty) } pub fn get_field_ty_mut(&mut self, name: &String) -> Option<&mut TypeKind> { self.0 .iter_mut() .find(|StructField(n, _, _)| n == name) .map(|StructField(_, ty, _)| ty) } } enum BlockReturn<'b> { Early(&'b Statement), Normal(ReturnKind, &'b Expression), } impl Block { fn return_expr(&self) -> Result { let mut early_return = None; for statement in &self.statements { let ret = statement.return_type(&Default::default(), SourceModuleId(0)); if let Ok((ReturnKind::Hard, _)) = ret { early_return = Some(statement); } } if let Some(s) = early_return { return Ok(BlockReturn::Early(s)); } self.return_expression .as_ref() .map(|(r, e)| BlockReturn::Normal(*r, e)) .ok_or(ReturnTypeOther::NoBlockReturn(self.meta)) } pub fn return_meta(&self) -> Metadata { self.return_expression .as_ref() .map(|e| e.1 .1) .or(self.statements.last().map(|s| s.1)) .unwrap_or(self.meta) } pub fn return_type( &self, refs: &TypeRefs, mod_id: SourceModuleId, ) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { let mut early_return = None; for statement in &self.statements { let ret = statement.return_type(refs, mod_id); if let Ok((ReturnKind::Hard, _)) = ret { early_return = early_return.or(ret.ok()); } } if let Some((ReturnKind::Hard, ret_ty)) = early_return { return Ok((ReturnKind::Hard, ret_ty)); } self.return_expression .as_ref() .ok_or(ReturnTypeOther::NoBlockReturn(self.meta)) .and_then(|(kind, stmt)| Ok((*kind, stmt.return_type(refs, mod_id)?.1))) } pub fn backing_var(&self) -> Option<&NamedVariableRef> { match self.return_expr().ok()? { BlockReturn::Early(statement) => statement.backing_var(), BlockReturn::Normal(kind, expr) => { if kind == ReturnKind::Soft { expr.backing_var() } else { None } } } } } impl Statement { pub fn return_type( &self, refs: &TypeRefs, mod_id: SourceModuleId, ) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { use StmtKind::*; match &self.0 { Let(var, _, expr) => if_hard( expr.return_type(refs, mod_id)?, Err(ReturnTypeOther::Let(var.2 + expr.1)), ), Set(lhs, rhs) => if_hard( rhs.return_type(refs, mod_id)?, Err(ReturnTypeOther::Set(lhs.1 + rhs.1)), ), Import(_) => todo!(), Expression(expression) => expression.return_type(refs, mod_id), For(_) => Err(ReturnTypeOther::Loop), While(_) => Err(ReturnTypeOther::Loop), } } pub fn backing_var(&self) -> Option<&NamedVariableRef> { match &self.0 { StmtKind::Let(_, _, _) => None, StmtKind::Set(_, _) => None, StmtKind::Import(_) => None, StmtKind::Expression(expr) => expr.backing_var(), StmtKind::For(_) => None, StmtKind::While(_) => None, } } } impl Expression { pub fn return_type( &self, refs: &TypeRefs, mod_id: SourceModuleId, ) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { use ExprKind::*; match &self.0 { Literal(lit) => Ok((ReturnKind::Soft, lit.as_type())), Variable(var) => var.return_type(), BinOp(_, then_e, else_e) => { let then_r = then_e.return_type(refs, mod_id)?; let else_r = else_e.return_type(refs, mod_id)?; Ok(pick_return(then_r, else_r)) } Block(block) => block.return_type(refs, mod_id), FunctionCall(fcall) => fcall.return_type(), If(expr) => expr.return_type(refs, mod_id), Indexed(expression, _, _) => { let expr_type = expression.return_type(refs, mod_id)?; if let TypeKind::Array(elem_ty, _) = expr_type.1.resolve_weak(refs) { Ok((ReturnKind::Soft, *elem_ty)) } else if let TypeKind::UserPtr(_) = expr_type.1.resolve_weak(refs) { Ok((ReturnKind::Soft, expr_type.1)) } else { Err(ReturnTypeOther::IndexingNonArray(expression.1)) } } Array(expressions) => { let first = expressions .iter() .next() .map(|e| e.return_type(refs, mod_id)) .unwrap_or(Ok((ReturnKind::Soft, TypeKind::Void)))?; Ok(( ReturnKind::Soft, TypeKind::Array(Box::new(first.1), expressions.len() as u64), )) } Accessed(_, type_kind, _) => Ok((ReturnKind::Soft, type_kind.clone())), Struct(name, _) => Ok(( ReturnKind::Soft, TypeKind::CustomType(CustomTypeKey(name.clone(), mod_id)), )), Borrow(var, mutable) => { let ret_type = var.return_type()?; Ok((ret_type.0, TypeKind::Borrow(Box::new(ret_type.1), *mutable))) } Deref(var) => { let (kind, ret_type) = var.return_type()?; match ret_type.resolve_weak(refs) { TypeKind::Borrow(type_kind, _) => Ok((kind, *type_kind)), _ => Err(ReturnTypeOther::DerefNonBorrow(var.2)), } } CastTo(expr, type_kind) => match expr.return_type(refs, mod_id) { Ok(ret_type) => match ret_type { (ReturnKind::Hard, ty) => Ok((ReturnKind::Hard, ty)), _ => Ok((ReturnKind::Soft, type_kind.clone())), }, Err(_) => Ok((ReturnKind::Soft, type_kind.clone())), }, } } pub fn backing_var(&self) -> Option<&NamedVariableRef> { match &self.0 { ExprKind::Variable(var_ref) => Some(var_ref), ExprKind::Indexed(lhs, _, _) => lhs.backing_var(), ExprKind::Accessed(lhs, _, _) => lhs.backing_var(), ExprKind::Borrow(var, _) => Some(var), ExprKind::Deref(var) => Some(var), ExprKind::Block(block) => block.backing_var(), ExprKind::Array(_) => None, ExprKind::Struct(_, _) => None, ExprKind::Literal(_) => None, ExprKind::BinOp(_, _, _) => None, ExprKind::FunctionCall(_) => None, ExprKind::If(_) => None, ExprKind::CastTo(expression, _) => expression.backing_var(), } } pub fn is_zero(&self) -> Result, ErrorKind> { if let Some(val) = self.num_value()? { Ok(Some(val == 0)) } else { Ok(None) } } pub fn num_value(&self) -> Result, ErrorKind> { Ok(match &self.0 { ExprKind::Variable(_) => None, ExprKind::Indexed(..) => None, ExprKind::Accessed(..) => None, ExprKind::Array(_) => None, ExprKind::Struct(..) => None, ExprKind::Literal(literal) => literal.num_value(), ExprKind::BinOp(op, lhs, rhs) => match op { BinaryOperator::Add => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a + b), BinaryOperator::Minus => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a - b), BinaryOperator::Mult => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a * b), BinaryOperator::And => None, BinaryOperator::Cmp(_) => None, BinaryOperator::Div => { let rhs_value = rhs.num_value()?; if rhs_value == Some(0) { Err(ErrorKind::DivideZero)? } maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a / b) } BinaryOperator::Mod => { let rhs_value = rhs.num_value()?; if rhs_value == Some(0) { Err(ErrorKind::DivideZero)? } maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a % b) } }, ExprKind::FunctionCall(..) => None, ExprKind::If(_) => None, ExprKind::Block(_) => None, ExprKind::Borrow(_, _) => None, ExprKind::Deref(_) => None, ExprKind::CastTo(expression, _) => expression.num_value()?, }) } } fn maybe(lhs: Option, rhs: Option, fun: T) -> Option where T: FnOnce(i128, i128) -> i128, { if let (Some(lhs), Some(rhs)) = (lhs, rhs) { Some(fun(lhs, rhs)) } else { None } } impl IfExpression { pub fn return_type( &self, refs: &TypeRefs, mod_id: SourceModuleId, ) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { let then_r = self.1.return_type(refs, mod_id)?; if let Some(else_e) = self.2.as_ref() { let else_r = else_e.return_type(refs, mod_id)?; let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard { ReturnKind::Hard } else { ReturnKind::Soft }; Ok((kind, then_r.1)) } else { Ok((ReturnKind::Soft, then_r.1)) } } } impl NamedVariableRef { pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { Ok((ReturnKind::Soft, self.0.clone())) } } impl FunctionCall { pub fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> { Ok((ReturnKind::Soft, self.return_type.clone())) } } fn if_hard( return_type: (ReturnKind, TypeKind), default: Result<(ReturnKind, TypeKind), TErr>, ) -> Result<(ReturnKind, TypeKind), TErr> { if let (ReturnKind::Hard, _) = return_type { Ok(return_type) } else { default } } pub fn pick_return(lhs: (ReturnKind, T), rhs: (ReturnKind, T)) -> (ReturnKind, T) { use ReturnKind::*; match (lhs.0, rhs.0) { (Hard, Hard) => (Hard, lhs.1), (Hard, Soft) => (Soft, rhs.1), (Soft, Hard) => (Soft, lhs.1), (_, _) => (Soft, lhs.1), } } impl TypeKind { /// Assert that a type is already known and not vague. Return said type or /// error. pub fn assert_unvague(&self) -> Result { self.known().map_err(ErrorKind::TypeIsVague) } /// Try to collapse a type on itself producing a default type if one exists, /// Error if not. pub fn or_default(&self) -> Result { match self { TypeKind::Vague(vague_type) => match &vague_type { Vague::Unknown => Err(ErrorKind::TypeIsVague(*vague_type)), Vague::Integer => Ok(TypeKind::I32), Vague::TypeRef(_) => panic!("Hinted default!"), VagueType::Decimal => Ok(TypeKind::F32), }, _ => Ok(self.clone()), } } pub fn resolve_weak(&self, refs: &TypeRefs) -> TypeKind { match self { TypeKind::Vague(Vague::TypeRef(idx)) => refs.retrieve_type(*idx).unwrap(), _ => self.clone(), } } pub fn resolve_ref(&self, refs: &TypeRefs) -> TypeKind { let resolved = self.resolve_weak(refs); match resolved { TypeKind::Array(t, len) => TypeKind::Array(Box::new(t.resolve_ref(refs)), len), TypeKind::Borrow(inner, mutable) => { TypeKind::Borrow(Box::new(inner.resolve_ref(refs)), mutable) } _ => resolved, } } } impl Literal { pub fn num_value(&self) -> Option { match self { Literal::I8(val) => Some(*val as i128), Literal::I16(val) => Some(*val as i128), Literal::I32(val) => Some(*val as i128), Literal::I64(val) => Some(*val as i128), Literal::I128(val) => Some(*val as i128), Literal::U8(val) => Some(*val as i128), Literal::U16(val) => Some(*val as i128), Literal::U32(val) => Some(*val as i128), Literal::U64(val) => Some(*val as i128), Literal::U128(val) => Some(*val as i128), Literal::Bool(_) => None, Literal::String(_) => None, Literal::Vague(VagueLiteral::Number(val)) => Some(*val as i128), Literal::Vague(VagueLiteral::Decimal(_)) => None, Literal::F16(_) => None, Literal::F32B(_) => None, Literal::F32(_) => None, Literal::F64(_) => None, Literal::F80(_) => None, Literal::F128(_) => None, Literal::F128PPC(_) => None, Literal::Char(_) => None, } } } #[derive(Debug, Clone, thiserror::Error, PartialEq, Eq, PartialOrd, Ord)] pub enum EqualsIssue { #[error("Function is already defined locally at {:?}", (.0).range)] ExistsLocally(Metadata), #[error("Equals")] Equals, #[error("Function {0} is already declared locally at {:?}", (.1).range)] AlreadyExtern(String, Metadata), #[error("Function {0} is already imported from another module")] ConflictWithImport(String), } impl FunctionDefinition { pub fn equals_as_imported(&self, other: &FunctionDefinition) -> Result<(), EqualsIssue> { match &self.kind { FunctionDefinitionKind::Local(_, metadata) => { Err(EqualsIssue::ExistsLocally(*metadata)) } FunctionDefinitionKind::Extern(imported) => { if *imported { Err(EqualsIssue::ConflictWithImport(self.name.clone())) } else { if self.is_pub == other.is_pub && self.name == other.name && self.parameters == other.parameters && self.return_type == other.return_type { Ok(()) } else { Err(EqualsIssue::AlreadyExtern( self.name.clone(), self.signature(), )) } } } } } }