use crate::util::maybe; use super::{typecheck::typerefs::TypeRefs, *}; #[derive(Debug, Clone)] pub enum ReturnTypeOther { Import(Metadata), Let(Metadata), Set(Metadata), EmptyBlock(Metadata), NoBlockReturn(Metadata), IndexingNonArray(Metadata), DerefNonBorrow(Metadata), Loop, } #[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)] pub enum NumValueError { #[error("Cannot divide by zero")] DivideZero, } enum BlockReturn<'b> { Early(&'b Statement), Normal(ReturnKind, &'b Option>), } impl TypeKind { 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::Bool, 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, }, } } pub fn try_collapse_two( (lhs1, rhs1): (&TypeKind, &TypeKind), (lhs2, rhs2): (&TypeKind, &TypeKind), ) -> Option<(TypeKind, TypeKind)> { if let (Ok(lhs), Ok(rhs)) = (lhs1.narrow_into(&lhs2), rhs1.narrow_into(&rhs2)) { Some((lhs, rhs)) } else if let (Ok(lhs), Ok(rhs)) = (lhs1.narrow_into(&rhs2), rhs1.narrow_into(&lhs2)) { Some((rhs, lhs)) } else { None } } pub fn unroll_borrow(&self) -> TypeKind { match self { TypeKind::Borrow(type_kind, mut1) => match *type_kind.clone() { TypeKind::Borrow(type_kind, mut2) => match (mut1, mut2) { (false, false) => TypeKind::Borrow(Box::new(*type_kind.clone()), false), _ => TypeKind::Borrow(Box::new(*type_kind.clone()), true), }, _ => self.clone(), }, _ => self.clone(), } } } impl BinaryOperator { pub fn is_commutative(&self) -> bool { match self { BinaryOperator::Add => true, BinaryOperator::Minus => false, BinaryOperator::Mult => true, BinaryOperator::Div => false, BinaryOperator::Mod => false, BinaryOperator::And => true, BinaryOperator::Cmp(cmp_operator) => match cmp_operator { CmpOperator::LT => false, CmpOperator::LE => false, CmpOperator::GT => false, CmpOperator::GE => false, CmpOperator::EQ => true, CmpOperator::NE => true, }, BinaryOperator::Or => true, BinaryOperator::Xor => true, BinaryOperator::BitOr => true, BinaryOperator::BitAnd => true, BinaryOperator::BitshiftRight => false, BinaryOperator::BitshiftLeft => false, } } } #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum TypeCategory { Integer, Real, Bool, Other, TypeRef, } impl TypeCategory { pub fn is_simple_maths(&self) -> bool { match self { TypeCategory::Integer => true, TypeCategory::Real => true, TypeCategory::Other => false, TypeCategory::TypeRef => false, TypeCategory::Bool => true, } } } 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) } } 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.as_ref().map(|e| e.1).unwrap_or(Metadata::default())) .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.as_ref() .and_then(|s| s.return_type(refs, mod_id).ok()) .map(|s| s.1) .unwrap_or(TypeKind::Void), )) }) } pub fn backing_var(&self) -> Option<&NamedVariableRef> { match self.return_expr().ok()? { BlockReturn::Early(statement) => statement.backing_var(), BlockReturn::Normal(kind, expr) => { if let Some(expr) = expr { if kind == ReturnKind::Soft { expr.backing_var() } else { None } } 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), 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::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, return_ty) => { let then_r = then_e.return_type(refs, mod_id)?; let else_r = else_e.return_type(refs, mod_id)?; Ok(match (then_r.0, else_r.0) { (ReturnKind::Hard, ReturnKind::Hard) => (ReturnKind::Hard, return_ty.clone()), _ => (ReturnKind::Soft, return_ty.clone()), }) } 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(expr, mutable) => { let ret_type = expr.return_type(refs, mod_id)?; Ok((ret_type.0, TypeKind::Borrow(Box::new(ret_type.1), *mutable))) } Deref(expr) => { let (kind, ret_type) = expr.return_type(refs, mod_id)?; match ret_type.resolve_weak(refs) { TypeKind::Borrow(type_kind, _) => Ok((kind, *type_kind)), _ => Err(ReturnTypeOther::DerefNonBorrow(expr.1)), } } 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())), }, AssociatedFunctionCall(_, fcall) => fcall.return_type(), } } 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(expr, _) => expr.backing_var(), ExprKind::Deref(expr) => expr.backing_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(), ExprKind::AssociatedFunctionCall(..) => None, } } pub fn num_value(&self) -> Result, NumValueError> { 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(NumValueError::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(NumValueError::DivideZero)? } maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a % b) } BinaryOperator::Or => None, BinaryOperator::Xor => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a ^ b), BinaryOperator::BitOr => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a | b), BinaryOperator::BitAnd => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a & b), BinaryOperator::BitshiftRight => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a >> b), BinaryOperator::BitshiftLeft => 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()?, ExprKind::AssociatedFunctionCall(..) => 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 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), #[error("Function is defined as an intrinsic")] ExistsAsIntrinsic, } 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())) } } } FunctionDefinitionKind::Intrinsic(_) => Err(EqualsIssue::ExistsAsIntrinsic), } } }