Refactor typecheck into it's own module
This commit is contained in:
parent
0b3ee3bf92
commit
f8d2e4996a
@ -7,9 +7,11 @@ use crate::{
|
||||
ast::token_stream::{self, TokenRange},
|
||||
codegen,
|
||||
lexer::{self, Cursor, FullToken, Position},
|
||||
mir::{self, pass, Metadata, SourceModuleId},
|
||||
mir::{self, pass, typecheck, Metadata, SourceModuleId},
|
||||
};
|
||||
|
||||
use crate::mir::typecheck::ErrorKind as TypecheckError;
|
||||
|
||||
fn label(text: &str) -> &str {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
@ -26,9 +28,9 @@ pub enum ErrorKind {
|
||||
#[error("{}{}", label("(Parsing) "), .0.kind)]
|
||||
ParserError(#[from] mir::pass::Error<token_stream::Error>),
|
||||
#[error("{}{}", label("(TypeCheck) "), .0.kind)]
|
||||
TypeCheckError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
|
||||
TypeCheckError(#[source] mir::pass::Error<typecheck::ErrorKind>),
|
||||
#[error("{}{}", label("(TypeInference) "), .0.kind)]
|
||||
TypeInferenceError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
|
||||
TypeInferenceError(#[source] mir::pass::Error<TypecheckError>),
|
||||
#[error("{}{}", label("(Linker) "), .0.kind)]
|
||||
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
|
||||
#[error("{}{}", label("(Codegen) "), .0)]
|
||||
@ -36,11 +38,11 @@ pub enum ErrorKind {
|
||||
}
|
||||
|
||||
impl ErrorKind {
|
||||
pub fn from_typecheck(err: mir::pass::Error<mir::typecheck::ErrorKind>) -> ErrorKind {
|
||||
pub fn from_typecheck(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
|
||||
ErrorKind::TypeCheckError(err)
|
||||
}
|
||||
|
||||
pub fn from_typeinference(err: mir::pass::Error<mir::typecheck::ErrorKind>) -> ErrorKind {
|
||||
pub fn from_typeinference(err: mir::pass::Error<typecheck::ErrorKind>) -> ErrorKind {
|
||||
ErrorKind::TypeInferenceError(err)
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,8 @@ use ast::{
|
||||
use codegen::intrinsics::{form_intrinsic_binops, form_intrinsics};
|
||||
use error_raporting::{ErrorKind as ErrorRapKind, ErrorModules, ReidError};
|
||||
use mir::{
|
||||
linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs,
|
||||
linker::LinkerPass,
|
||||
typecheck::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs},
|
||||
};
|
||||
use reid_lib::{compile::CompileOutput, Context};
|
||||
|
||||
|
@ -2,24 +2,7 @@ use std::fmt::{Debug, Display, Write};
|
||||
|
||||
use crate::pad_adapter::PadAdapter;
|
||||
|
||||
use super::{typerefs::TypeRefs, *};
|
||||
|
||||
impl Display for TypeRefs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (i, typeref) in self.type_refs.borrow().iter().enumerate() {
|
||||
let idx = *typeref.borrow();
|
||||
writeln!(
|
||||
f,
|
||||
"{:<3} = {:<3} = {:?} = {}",
|
||||
i,
|
||||
unsafe { *self.recurse_type_ref(idx).borrow() },
|
||||
self.retrieve_type(idx),
|
||||
TypeKind::Vague(VagueType::TypeRef(idx)).resolve_ref(self)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
use super::{typecheck::typerefs::TypeRefs, *};
|
||||
|
||||
impl Display for Context {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -1,4 +1,6 @@
|
||||
use super::{pass::ScopeBinopDef, typecheck::ErrorKind, typerefs::TypeRefs, VagueType as Vague, *};
|
||||
use crate::util::maybe;
|
||||
|
||||
use super::{pass::ScopeBinopDef, typecheck::typerefs::TypeRefs, *};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ReturnTypeOther {
|
||||
@ -12,87 +14,18 @@ pub enum ReturnTypeOther {
|
||||
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<Box<Expression>>),
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
pub fn collapse_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||
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<TypeKind, ErrorKind> {
|
||||
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 simple_binop_type(&self, op: &BinaryOperator) -> Option<TypeKind> {
|
||||
@ -365,11 +298,6 @@ impl StructType {
|
||||
}
|
||||
}
|
||||
|
||||
enum BlockReturn<'b> {
|
||||
Early(&'b Statement),
|
||||
Normal(ReturnKind, &'b Option<Box<Expression>>),
|
||||
}
|
||||
|
||||
impl Block {
|
||||
fn return_expr(&self) -> Result<BlockReturn, ReturnTypeOther> {
|
||||
let mut early_return = None;
|
||||
@ -566,15 +494,7 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> Result<Option<bool>, ErrorKind> {
|
||||
if let Some(val) = self.num_value()? {
|
||||
Ok(Some(val == 0))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_value(&self) -> Result<Option<i128>, ErrorKind> {
|
||||
pub fn num_value(&self) -> Result<Option<i128>, NumValueError> {
|
||||
Ok(match &self.0 {
|
||||
ExprKind::Variable(_) => None,
|
||||
ExprKind::Indexed(..) => None,
|
||||
@ -591,14 +511,14 @@ impl Expression {
|
||||
BinaryOperator::Div => {
|
||||
let rhs_value = rhs.num_value()?;
|
||||
if rhs_value == Some(0) {
|
||||
Err(ErrorKind::DivideZero)?
|
||||
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(ErrorKind::DivideZero)?
|
||||
Err(NumValueError::DivideZero)?
|
||||
}
|
||||
maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a % b)
|
||||
}
|
||||
@ -613,16 +533,6 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe<T>(lhs: Option<i128>, rhs: Option<i128>, fun: T) -> Option<i128>
|
||||
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,
|
||||
@ -678,56 +588,6 @@ pub fn pick_return<T>(lhs: (ReturnKind, T), rhs: (ReturnKind, T)) -> (ReturnKind
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
/// Assert that a type is already known and not vague. Return said type or
|
||||
/// error.
|
||||
pub fn assert_unvague(&self) -> Result<TypeKind, ErrorKind> {
|
||||
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<TypeKind, ErrorKind> {
|
||||
Ok(match self {
|
||||
TypeKind::Vague(vague_type) => match &vague_type {
|
||||
Vague::Unknown => Err(ErrorKind::TypeIsVague(*vague_type))?,
|
||||
Vague::Integer => TypeKind::I32,
|
||||
Vague::TypeRef(_) => panic!("Hinted default!"),
|
||||
VagueType::Decimal => TypeKind::F32,
|
||||
},
|
||||
TypeKind::Array(type_kind, len) => {
|
||||
TypeKind::Array(Box::new(type_kind.or_default()?), *len)
|
||||
}
|
||||
TypeKind::Borrow(type_kind, mutable) => {
|
||||
TypeKind::Borrow(Box::new(type_kind.or_default()?), *mutable)
|
||||
}
|
||||
TypeKind::UserPtr(type_kind) => TypeKind::UserPtr(Box::new(type_kind.or_default()?)),
|
||||
TypeKind::CodegenPtr(type_kind) => {
|
||||
TypeKind::CodegenPtr(Box::new(type_kind.or_default()?))
|
||||
}
|
||||
_ => 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<i128> {
|
||||
match self {
|
||||
|
@ -15,8 +15,6 @@ pub mod implement;
|
||||
pub mod linker;
|
||||
pub mod pass;
|
||||
pub mod typecheck;
|
||||
pub mod typeinference;
|
||||
pub mod typerefs;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
|
||||
pub struct SourceModuleId(pub u32);
|
||||
|
241
reid/src/mir/typecheck/mod.rs
Normal file
241
reid/src/mir/typecheck/mod.rs
Normal file
@ -0,0 +1,241 @@
|
||||
use crate::mir::VagueType as Vague;
|
||||
use crate::mir::*;
|
||||
use typecheck::ErrorTypedefKind;
|
||||
use typerefs::TypeRefs;
|
||||
|
||||
use super::implement::{NumValueError, TypeCategory};
|
||||
use super::pass::PassState;
|
||||
|
||||
pub mod typecheck;
|
||||
pub mod typeinference;
|
||||
pub mod typerefs;
|
||||
|
||||
pub type TypecheckPassState<'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>;
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum ErrorKind {
|
||||
#[error("NULL error, should never occur!")]
|
||||
Null,
|
||||
#[error("Type is vague: {0}")]
|
||||
TypeIsVague(VagueType),
|
||||
#[error("Literal {0} can not be coerced to type {1}")]
|
||||
LiteralIncompatible(Literal, TypeKind),
|
||||
#[error("Types {0} and {1} are incompatible")]
|
||||
TypesIncompatible(TypeKind, TypeKind),
|
||||
#[error("Variable not defined: {0}")]
|
||||
VariableNotDefined(String),
|
||||
#[error("Function not defined: {0}")]
|
||||
FunctionNotDefined(String),
|
||||
#[error("Expected a return type of {0}, got {1} instead")]
|
||||
ReturnTypeMismatch(TypeKind, TypeKind),
|
||||
#[error("Function {0} already defined {1}")]
|
||||
FunctionAlreadyDefined(String, ErrorTypedefKind),
|
||||
#[error("Variable already defined: {0}")]
|
||||
VariableAlreadyDefined(String),
|
||||
#[error("Variable {0} is not declared as mutable")]
|
||||
VariableNotMutable(String),
|
||||
#[error("Function {0} was given {1} parameters, but {2} were expected")]
|
||||
InvalidAmountParameters(String, usize, usize),
|
||||
#[error("Unable to infer type {0}")]
|
||||
TypeNotInferrable(TypeKind),
|
||||
#[error("Expected branch type to be {0}, found {1} instead")]
|
||||
BranchTypesDiffer(TypeKind, TypeKind),
|
||||
#[error("Attempted to index a non-indexable type of {0}")]
|
||||
TriedIndexingNonIndexable(TypeKind),
|
||||
#[error("Index {0} out of bounds ({1})")]
|
||||
IndexOutOfBounds(u64, u64),
|
||||
#[error("No such type {0} could be found in module {1}")]
|
||||
NoSuchType(String, SourceModuleId),
|
||||
#[error("Attempted to access field of non-struct type of {0}")]
|
||||
TriedAccessingNonStruct(TypeKind),
|
||||
#[error("No such struct-field on type {0}")]
|
||||
NoSuchField(String),
|
||||
#[error("Struct field declared twice {0}")]
|
||||
DuplicateStructField(String),
|
||||
#[error("Type declared twice {0}")]
|
||||
DuplicateTypeName(String),
|
||||
#[error("Recursive type definition: {0}.{1}")]
|
||||
RecursiveTypeDefinition(String, String),
|
||||
#[error("This type of expression can not be used for assignment")]
|
||||
InvalidSetExpression,
|
||||
#[error("Can not deref {0}, as it is not a borrow")]
|
||||
AttemptedDerefNonBorrow(String),
|
||||
#[error("Types {0} and {1} differ in mutability")]
|
||||
TypesDifferMutability(TypeKind, TypeKind),
|
||||
#[error("Cannot mutably borrow variable {0}, which is not declared as mutable")]
|
||||
ImpossibleMutableBorrow(String),
|
||||
#[error("Cannot declare variable {0} as mutable, when it's type is immutable")]
|
||||
ImpossibleMutLet(String),
|
||||
#[error("Cannot produce a negative unsigned value of type {0}")]
|
||||
NegativeUnsignedValue(TypeKind),
|
||||
#[error("Cannot cast type {0} into type {1}")]
|
||||
NotCastableTo(TypeKind, TypeKind),
|
||||
#[error(transparent)]
|
||||
NumValueError(#[from] NumValueError),
|
||||
#[error("Binary operation {0} between {1} and {2} is already defined")]
|
||||
BinaryOpAlreadyDefined(BinaryOperator, TypeKind, TypeKind),
|
||||
#[error("Binary operation {0} between {1} and {2} is not defined")]
|
||||
InvalidBinop(BinaryOperator, TypeKind, TypeKind),
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
pub(super) fn collapse_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||
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(super) fn cast_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||
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())),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that a type is already known and not vague. Return said type or
|
||||
/// error.
|
||||
pub(super) fn assert_unvague(&self) -> Result<TypeKind, ErrorKind> {
|
||||
self.known().map_err(ErrorKind::TypeIsVague)
|
||||
}
|
||||
|
||||
/// Try to collapse a type on itself producing a default type if one exists,
|
||||
/// Error if not.
|
||||
pub(super) fn or_default(&self) -> Result<TypeKind, ErrorKind> {
|
||||
Ok(match self {
|
||||
TypeKind::Vague(vague_type) => match &vague_type {
|
||||
Vague::Unknown => Err(ErrorKind::TypeIsVague(*vague_type))?,
|
||||
Vague::Integer => TypeKind::I32,
|
||||
Vague::TypeRef(_) => panic!("Hinted default!"),
|
||||
VagueType::Decimal => TypeKind::F32,
|
||||
},
|
||||
TypeKind::Array(type_kind, len) => {
|
||||
TypeKind::Array(Box::new(type_kind.or_default()?), *len)
|
||||
}
|
||||
TypeKind::Borrow(type_kind, mutable) => {
|
||||
TypeKind::Borrow(Box::new(type_kind.or_default()?), *mutable)
|
||||
}
|
||||
TypeKind::UserPtr(type_kind) => TypeKind::UserPtr(Box::new(type_kind.or_default()?)),
|
||||
TypeKind::CodegenPtr(type_kind) => {
|
||||
TypeKind::CodegenPtr(Box::new(type_kind.or_default()?))
|
||||
}
|
||||
_ => self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn resolve_weak(&self, refs: &TypeRefs) -> TypeKind {
|
||||
match self {
|
||||
TypeKind::Vague(Vague::TypeRef(idx)) => refs.retrieve_type(*idx).unwrap(),
|
||||
_ => self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) 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,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn assert_known(
|
||||
&self,
|
||||
refs: &TypeRefs,
|
||||
state: &TypecheckPassState,
|
||||
) -> Result<TypeKind, ErrorKind> {
|
||||
self.is_known(refs, state).map(|_| self.clone())
|
||||
}
|
||||
|
||||
pub(super) fn is_known(
|
||||
&self,
|
||||
refs: &TypeRefs,
|
||||
state: &TypecheckPassState,
|
||||
) -> Result<(), ErrorKind> {
|
||||
match &self {
|
||||
TypeKind::Array(type_kind, _) => type_kind.as_ref().is_known(refs, state),
|
||||
TypeKind::CustomType(custom_type_key) => state
|
||||
.scope
|
||||
.types
|
||||
.get(custom_type_key)
|
||||
.map(|_| ())
|
||||
.ok_or(ErrorKind::NoSuchType(
|
||||
custom_type_key.0.clone(),
|
||||
state.module_id.unwrap(),
|
||||
)),
|
||||
TypeKind::Borrow(type_kind, _) => type_kind.is_known(refs, state),
|
||||
TypeKind::UserPtr(type_kind) => type_kind.is_known(refs, state),
|
||||
TypeKind::CodegenPtr(type_kind) => type_kind.is_known(refs, state),
|
||||
TypeKind::Vague(vague_type) => Err(ErrorKind::TypeIsVague(*vague_type)),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
@ -6,84 +6,17 @@ use crate::{mir::*, util::try_all};
|
||||
use VagueType as Vague;
|
||||
|
||||
use super::{
|
||||
pass::{Pass, PassResult, PassState, ScopeVariable},
|
||||
super::pass::{Pass, PassResult, ScopeVariable},
|
||||
typerefs::TypeRefs,
|
||||
ErrorKind, TypecheckPassState,
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub enum ErrorKind {
|
||||
#[error("NULL error, should never occur!")]
|
||||
Null,
|
||||
#[error("Type is vague: {0}")]
|
||||
TypeIsVague(VagueType),
|
||||
#[error("Literal {0} can not be coerced to type {1}")]
|
||||
LiteralIncompatible(Literal, TypeKind),
|
||||
#[error("Types {0} and {1} are incompatible")]
|
||||
TypesIncompatible(TypeKind, TypeKind),
|
||||
#[error("Variable not defined: {0}")]
|
||||
VariableNotDefined(String),
|
||||
#[error("Function not defined: {0}")]
|
||||
FunctionNotDefined(String),
|
||||
#[error("Expected a return type of {0}, got {1} instead")]
|
||||
ReturnTypeMismatch(TypeKind, TypeKind),
|
||||
#[error("Function {0} already defined {1}")]
|
||||
FunctionAlreadyDefined(String, ErrorTypedefKind),
|
||||
#[error("Variable already defined: {0}")]
|
||||
VariableAlreadyDefined(String),
|
||||
#[error("Variable {0} is not declared as mutable")]
|
||||
VariableNotMutable(String),
|
||||
#[error("Function {0} was given {1} parameters, but {2} were expected")]
|
||||
InvalidAmountParameters(String, usize, usize),
|
||||
#[error("Unable to infer type {0}")]
|
||||
TypeNotInferrable(TypeKind),
|
||||
#[error("Expected branch type to be {0}, found {1} instead")]
|
||||
BranchTypesDiffer(TypeKind, TypeKind),
|
||||
#[error("Attempted to index a non-indexable type of {0}")]
|
||||
TriedIndexingNonIndexable(TypeKind),
|
||||
#[error("Index {0} out of bounds ({1})")]
|
||||
IndexOutOfBounds(u64, u64),
|
||||
#[error("No such type {0} could be found in module {1}")]
|
||||
NoSuchType(String, SourceModuleId),
|
||||
#[error("Attempted to access field of non-struct type of {0}")]
|
||||
TriedAccessingNonStruct(TypeKind),
|
||||
#[error("No such struct-field on type {0}")]
|
||||
NoSuchField(String),
|
||||
#[error("Struct field declared twice {0}")]
|
||||
DuplicateStructField(String),
|
||||
#[error("Type declared twice {0}")]
|
||||
DuplicateTypeName(String),
|
||||
#[error("Recursive type definition: {0}.{1}")]
|
||||
RecursiveTypeDefinition(String, String),
|
||||
#[error("This type of expression can not be used for assignment")]
|
||||
InvalidSetExpression,
|
||||
#[error("Can not deref {0}, as it is not a borrow")]
|
||||
AttemptedDerefNonBorrow(String),
|
||||
#[error("Types {0} and {1} differ in mutability")]
|
||||
TypesDifferMutability(TypeKind, TypeKind),
|
||||
#[error("Cannot mutably borrow variable {0}, which is not declared as mutable")]
|
||||
ImpossibleMutableBorrow(String),
|
||||
#[error("Cannot declare variable {0} as mutable, when it's type is immutable")]
|
||||
ImpossibleMutLet(String),
|
||||
#[error("Cannot produce a negative unsigned value of type {0}")]
|
||||
NegativeUnsignedValue(TypeKind),
|
||||
#[error("Cannot cast type {0} into type {1}")]
|
||||
NotCastableTo(TypeKind, TypeKind),
|
||||
#[error("Cannot divide by zero")]
|
||||
DivideZero,
|
||||
#[error("Binary operation {0} between {1} and {2} is already defined")]
|
||||
BinaryOpAlreadyDefined(BinaryOperator, TypeKind, TypeKind),
|
||||
#[error("Binary operation {0} between {1} and {2} is not defined")]
|
||||
InvalidBinop(BinaryOperator, TypeKind, TypeKind),
|
||||
}
|
||||
|
||||
/// Struct used to implement a type-checking pass that can be performed on the
|
||||
/// MIR.
|
||||
pub struct TypeCheck<'t> {
|
||||
pub refs: &'t TypeRefs,
|
||||
}
|
||||
|
||||
type TypecheckPassState<'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>;
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ErrorTypedefKind {
|
||||
#[error("locally")]
|
||||
@ -920,32 +853,4 @@ impl Literal {
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
fn assert_known(
|
||||
&self,
|
||||
refs: &TypeRefs,
|
||||
state: &TypecheckPassState,
|
||||
) -> Result<TypeKind, ErrorKind> {
|
||||
self.is_known(refs, state).map(|_| self.clone())
|
||||
}
|
||||
|
||||
fn is_known(&self, refs: &TypeRefs, state: &TypecheckPassState) -> Result<(), ErrorKind> {
|
||||
match &self {
|
||||
TypeKind::Array(type_kind, _) => type_kind.as_ref().is_known(refs, state),
|
||||
TypeKind::CustomType(custom_type_key) => state
|
||||
.scope
|
||||
.types
|
||||
.get(custom_type_key)
|
||||
.map(|_| ())
|
||||
.ok_or(ErrorKind::NoSuchType(
|
||||
custom_type_key.0.clone(),
|
||||
state.module_id.unwrap(),
|
||||
)),
|
||||
TypeKind::Borrow(type_kind, _) => type_kind.is_known(refs, state),
|
||||
TypeKind::UserPtr(type_kind) => type_kind.is_known(refs, state),
|
||||
TypeKind::CodegenPtr(type_kind) => type_kind.is_known(refs, state),
|
||||
TypeKind::Vague(vague_type) => Err(ErrorKind::TypeIsVague(*vague_type)),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TypeKind {}
|
@ -10,17 +10,24 @@ use std::{
|
||||
iter,
|
||||
};
|
||||
|
||||
use crate::{mir::TypeKind, util::try_all};
|
||||
use crate::{
|
||||
mir::{
|
||||
BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition,
|
||||
FunctionDefinitionKind, IfExpression, Module, ReturnKind, StmtKind, TypeKind,
|
||||
WhileStatement,
|
||||
},
|
||||
util::try_all,
|
||||
};
|
||||
|
||||
use super::{
|
||||
pass::{Pass, PassResult, PassState, ScopeBinopKey},
|
||||
typecheck::{ErrorKind, ErrorTypedefKind},
|
||||
super::{
|
||||
pass::{Pass, PassResult, PassState, ScopeBinopKey},
|
||||
TypeKind::*,
|
||||
VagueType::*,
|
||||
},
|
||||
typecheck::ErrorTypedefKind,
|
||||
typerefs::{ScopeTypeRefs, TypeRef, TypeRefs},
|
||||
BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition,
|
||||
FunctionDefinitionKind, IfExpression, Module, ReturnKind, StmtKind,
|
||||
TypeKind::*,
|
||||
VagueType::*,
|
||||
WhileStatement,
|
||||
ErrorKind, TypecheckPassState,
|
||||
};
|
||||
|
||||
/// Struct used to implement Type Inference, where an intermediary
|
||||
@ -30,13 +37,11 @@ pub struct TypeInference<'t> {
|
||||
pub refs: &'t TypeRefs,
|
||||
}
|
||||
|
||||
type TypeInferencePassState<'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>;
|
||||
|
||||
impl<'t> Pass for TypeInference<'t> {
|
||||
type Data = ();
|
||||
type TError = ErrorKind;
|
||||
|
||||
fn module(&mut self, module: &mut Module, mut state: TypeInferencePassState) -> PassResult {
|
||||
fn module(&mut self, module: &mut Module, mut state: TypecheckPassState) -> PassResult {
|
||||
let mut seen_functions = HashMap::new();
|
||||
for function in &mut module.functions {
|
||||
if let Some(kind) = seen_functions.get(&function.name) {
|
||||
@ -98,7 +103,7 @@ impl BinopDefinition {
|
||||
fn infer_types(
|
||||
&mut self,
|
||||
type_refs: &TypeRefs,
|
||||
state: &mut TypeInferencePassState,
|
||||
state: &mut TypecheckPassState,
|
||||
) -> Result<(), ErrorKind> {
|
||||
let scope_hints = ScopeTypeRefs::from(type_refs);
|
||||
|
||||
@ -142,7 +147,7 @@ impl FunctionDefinition {
|
||||
fn infer_types(
|
||||
&mut self,
|
||||
type_refs: &TypeRefs,
|
||||
state: &mut TypeInferencePassState,
|
||||
state: &mut TypecheckPassState,
|
||||
) -> Result<(), ErrorKind> {
|
||||
let scope_refs = ScopeTypeRefs::from(type_refs);
|
||||
for param in &self.parameters {
|
||||
@ -183,7 +188,7 @@ impl FunctionDefinition {
|
||||
impl FunctionDefinitionKind {
|
||||
fn infer_types<'s>(
|
||||
&mut self,
|
||||
state: &mut TypeInferencePassState,
|
||||
state: &mut TypecheckPassState,
|
||||
scope_refs: &'s ScopeTypeRefs,
|
||||
hint: Option<TypeKind>,
|
||||
) -> Result<Option<TypeRef<'s>>, ErrorKind> {
|
||||
@ -206,7 +211,7 @@ impl FunctionDefinitionKind {
|
||||
impl Block {
|
||||
fn infer_types<'s>(
|
||||
&mut self,
|
||||
state: &mut TypeInferencePassState,
|
||||
state: &mut TypecheckPassState,
|
||||
outer_refs: &'s ScopeTypeRefs,
|
||||
) -> Result<(ReturnKind, TypeRef<'s>), ErrorKind> {
|
||||
let mut state = state.inner();
|
||||
@ -293,7 +298,7 @@ impl Block {
|
||||
impl Expression {
|
||||
fn infer_types<'s>(
|
||||
&mut self,
|
||||
state: &mut TypeInferencePassState,
|
||||
state: &mut TypecheckPassState,
|
||||
type_refs: &'s ScopeTypeRefs<'s>,
|
||||
) -> Result<TypeRef<'s>, ErrorKind> {
|
||||
match &mut self.0 {
|
@ -4,12 +4,11 @@ use std::{
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::mir::VagueType;
|
||||
use crate::mir::{BinaryOperator, TypeKind, VagueType};
|
||||
|
||||
use super::{
|
||||
pass::{ScopeBinopDef, ScopeBinopKey, Storage},
|
||||
typecheck::ErrorKind,
|
||||
BinaryOperator, TypeKind,
|
||||
super::pass::{ScopeBinopDef, ScopeBinopKey, Storage},
|
||||
ErrorKind,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -43,7 +42,7 @@ impl<'scope> TypeRef<'scope> {
|
||||
}
|
||||
|
||||
pub fn as_type(&self) -> TypeKind {
|
||||
TypeKind::Vague(super::VagueType::TypeRef(*self.0.borrow()))
|
||||
TypeKind::Vague(VagueType::TypeRef(*self.0.borrow()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +65,23 @@ pub struct TypeRefs {
|
||||
pub(super) type_refs: RefCell<Vec<TypeIdRef>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TypeRefs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (i, typeref) in self.type_refs.borrow().iter().enumerate() {
|
||||
let idx = *typeref.borrow();
|
||||
writeln!(
|
||||
f,
|
||||
"{:<3} = {:<3} = {:?} = {}",
|
||||
i,
|
||||
unsafe { *self.recurse_type_ref(idx).borrow() },
|
||||
self.retrieve_type(idx),
|
||||
TypeKind::Vague(VagueType::TypeRef(idx)).resolve_ref(self)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeRefs {
|
||||
pub fn new(&self, ty: &TypeKind) -> TypeIdRef {
|
||||
let idx = self.hints.borrow().len();
|
||||
@ -157,7 +173,7 @@ impl<'outer> ScopeTypeRefs<'outer> {
|
||||
|
||||
pub fn from_type(&'outer self, ty: &TypeKind) -> Option<TypeRef<'outer>> {
|
||||
let idx = match ty {
|
||||
TypeKind::Vague(super::VagueType::TypeRef(idx)) => {
|
||||
TypeKind::Vague(VagueType::TypeRef(idx)) => {
|
||||
let inner_idx = unsafe { *self.types.recurse_type_ref(*idx).borrow() };
|
||||
self.types.type_refs.borrow().get(inner_idx).cloned()?
|
||||
}
|
@ -19,3 +19,14 @@ pub fn try_all<U, E>(list: Vec<Result<U, E>>) -> Result<Vec<U>, Vec<E>> {
|
||||
Ok(successes)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe<T, U>(lhs: Option<U>, rhs: Option<U>, fun: T) -> Option<U>
|
||||
where
|
||||
T: FnOnce(U, U) -> U,
|
||||
{
|
||||
if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
|
||||
Some(fun(lhs, rhs))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user