Implement typechecking for structs
This commit is contained in:
parent
aafab49f82
commit
77439ee34a
@ -103,10 +103,6 @@ impl<T: Clone + std::fmt::Debug> Storage<T> {
|
|||||||
pub fn get(&self, key: &String) -> Option<&T> {
|
pub fn get(&self, key: &String) -> Option<&T> {
|
||||||
self.0.get(key)
|
self.0.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, key: &String) -> Option<&mut T> {
|
|
||||||
self.0.get_mut(key)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! This module contains code relevant to doing a type checking pass on the MIR.
|
//! This module contains code relevant to doing a type checking pass on the MIR.
|
||||||
//! During typechecking relevant types are also coerced if possible.
|
//! During typechecking relevant types are also coerced if possible.
|
||||||
use std::{convert::Infallible, iter};
|
use std::{collections::HashSet, convert::Infallible, iter};
|
||||||
|
|
||||||
use crate::{mir::*, util::try_all};
|
use crate::{mir::*, util::try_all};
|
||||||
use VagueType as Vague;
|
use VagueType as Vague;
|
||||||
@ -49,6 +49,12 @@ pub enum ErrorKind {
|
|||||||
TriedAccessingNonStruct(TypeKind),
|
TriedAccessingNonStruct(TypeKind),
|
||||||
#[error("No such struct-field on type {0}")]
|
#[error("No such struct-field on type {0}")]
|
||||||
NoSuchField(String),
|
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),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Struct used to implement a type-checking pass that can be performed on the
|
/// Struct used to implement a type-checking pass that can be performed on the
|
||||||
@ -57,10 +63,72 @@ pub struct TypeCheck<'t> {
|
|||||||
pub refs: &'t TypeRefs,
|
pub refs: &'t TypeRefs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_typedefs_for_recursion<'a, 'b>(
|
||||||
|
defmap: &'b HashMap<&'a String, &'b TypeDefinition>,
|
||||||
|
typedef: &'b TypeDefinition,
|
||||||
|
mut seen: HashSet<String>,
|
||||||
|
state: &mut PassState<ErrorKind>,
|
||||||
|
) {
|
||||||
|
match &typedef.kind {
|
||||||
|
TypeDefinitionKind::Struct(StructType(fields)) => {
|
||||||
|
for field_ty in fields.iter().map(|(_, ty)| ty) {
|
||||||
|
if let TypeKind::CustomType(name) = field_ty {
|
||||||
|
if seen.contains(name) {
|
||||||
|
state.ok::<_, Infallible>(
|
||||||
|
Err(ErrorKind::RecursiveTypeDefinition(
|
||||||
|
typedef.name.clone(),
|
||||||
|
name.clone(),
|
||||||
|
)),
|
||||||
|
typedef.meta,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
seen.insert(name.clone());
|
||||||
|
if let Some(inner_typedef) = defmap.get(name) {
|
||||||
|
check_typedefs_for_recursion(defmap, inner_typedef, seen.clone(), state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'t> Pass for TypeCheck<'t> {
|
impl<'t> Pass for TypeCheck<'t> {
|
||||||
type TError = ErrorKind;
|
type TError = ErrorKind;
|
||||||
|
|
||||||
fn module(&mut self, module: &mut Module, mut state: PassState<ErrorKind>) {
|
fn module(&mut self, module: &mut Module, mut state: PassState<ErrorKind>) {
|
||||||
|
let mut defmap = HashMap::new();
|
||||||
|
for typedef in &module.typedefs {
|
||||||
|
let TypeDefinition { name, kind, meta } = &typedef;
|
||||||
|
match kind {
|
||||||
|
TypeDefinitionKind::Struct(StructType(fields)) => {
|
||||||
|
let mut fieldmap = HashMap::new();
|
||||||
|
for (name, field_ty) in fields {
|
||||||
|
if let Some(_) = fieldmap.insert(name, field_ty) {
|
||||||
|
state.ok::<_, Infallible>(
|
||||||
|
Err(ErrorKind::DuplicateStructField(name.clone())),
|
||||||
|
meta.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_) = defmap.insert(&typedef.name, typedef) {
|
||||||
|
state.ok::<_, Infallible>(
|
||||||
|
Err(ErrorKind::DuplicateTypeName(name.clone())),
|
||||||
|
meta.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let seen = HashSet::new();
|
||||||
|
for typedef in defmap.values() {
|
||||||
|
let mut curr = seen.clone();
|
||||||
|
curr.insert(typedef.name.clone());
|
||||||
|
check_typedefs_for_recursion(&defmap, typedef, HashSet::new(), &mut state);
|
||||||
|
}
|
||||||
|
|
||||||
for function in &mut module.functions {
|
for function in &mut module.functions {
|
||||||
let res = function.typecheck(&self.refs, &mut state.inner());
|
let res = function.typecheck(&self.refs, &mut state.inner());
|
||||||
state.ok(res, function.block_meta());
|
state.ok(res, function.block_meta());
|
||||||
@ -185,7 +253,8 @@ impl Block {
|
|||||||
StmtKind::Set(variable_reference, expression) => {
|
StmtKind::Set(variable_reference, expression) => {
|
||||||
if let Some(var) = state
|
if let Some(var) = state
|
||||||
.ok(
|
.ok(
|
||||||
variable_reference.get_variable(&state.scope.variables),
|
variable_reference
|
||||||
|
.get_variable(&state.scope.variables, &state.scope.types),
|
||||||
variable_reference.meta,
|
variable_reference.meta,
|
||||||
)
|
)
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -493,10 +562,59 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::StructIndex(expression, type_kind, _) => {
|
ExprKind::StructIndex(expression, type_kind, field_name) => {
|
||||||
todo!("typechecking for struct index")
|
// Resolve expected type
|
||||||
|
let expected_ty = type_kind.resolve_hinted(hints);
|
||||||
|
|
||||||
|
// Typecheck expression
|
||||||
|
let expr_res = expression.typecheck(state, hints, Some(&expected_ty));
|
||||||
|
let expr_ty =
|
||||||
|
state.or_else(expr_res, TypeKind::Vague(Vague::Unknown), expression.1);
|
||||||
|
|
||||||
|
if let TypeKind::CustomType(struct_name) = expr_ty {
|
||||||
|
let struct_type = state.scope.get_struct_type(&struct_name)?;
|
||||||
|
if let Some(expr_field_ty) = struct_type.get_field_ty(&field_name) {
|
||||||
|
// Make sure they are the same
|
||||||
|
let true_ty = state.or_else(
|
||||||
|
expr_field_ty.collapse_into(&expected_ty),
|
||||||
|
TypeKind::Vague(Vague::Unknown),
|
||||||
|
self.1,
|
||||||
|
);
|
||||||
|
*type_kind = true_ty.clone();
|
||||||
|
// Update possibly resolved type
|
||||||
|
Ok(true_ty)
|
||||||
|
} else {
|
||||||
|
Err(ErrorKind::NoSuchField(field_name.clone()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ErrorKind::TriedAccessingNonStruct(expr_ty))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Struct(struct_name, items) => {
|
||||||
|
let struct_def = state.scope.get_struct_type(struct_name)?.clone();
|
||||||
|
for (field_name, field_expr) in items {
|
||||||
|
// Get expected type, or error if field does not exist
|
||||||
|
let expected_ty = state.or_else(
|
||||||
|
struct_def
|
||||||
|
.get_field_ty(field_name)
|
||||||
|
.ok_or(ErrorKind::NoSuchField(format!(
|
||||||
|
"{}.{}",
|
||||||
|
struct_name, field_name
|
||||||
|
))),
|
||||||
|
&TypeKind::Vague(VagueType::Unknown),
|
||||||
|
field_expr.1,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Typecheck the actual expression
|
||||||
|
let expr_res = field_expr.typecheck(state, hints, Some(expected_ty));
|
||||||
|
let expr_ty =
|
||||||
|
state.or_else(expr_res, TypeKind::Vague(Vague::Unknown), field_expr.1);
|
||||||
|
|
||||||
|
// Make sure both are the same type, report error if not
|
||||||
|
state.ok(expr_ty.collapse_into(&expr_ty), field_expr.1);
|
||||||
|
}
|
||||||
|
Ok(TypeKind::CustomType(struct_name.clone()))
|
||||||
}
|
}
|
||||||
ExprKind::Struct(_, items) => todo!("typechecking for struct expression"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -505,13 +623,14 @@ impl IndexedVariableReference {
|
|||||||
fn get_variable(
|
fn get_variable(
|
||||||
&self,
|
&self,
|
||||||
storage: &Storage<ScopeVariable>,
|
storage: &Storage<ScopeVariable>,
|
||||||
|
types: &Storage<TypeDefinitionKind>,
|
||||||
) -> Result<Option<ScopeVariable>, ErrorKind> {
|
) -> Result<Option<ScopeVariable>, ErrorKind> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
IndexedVariableReferenceKind::Named(NamedVariableRef(_, name, _)) => {
|
IndexedVariableReferenceKind::Named(NamedVariableRef(_, name, _)) => {
|
||||||
Ok(storage.get(&name).cloned())
|
Ok(storage.get(&name).cloned())
|
||||||
}
|
}
|
||||||
IndexedVariableReferenceKind::ArrayIndex(inner_ref, _) => {
|
IndexedVariableReferenceKind::ArrayIndex(inner_ref, _) => {
|
||||||
if let Some(var) = inner_ref.get_variable(storage)? {
|
if let Some(var) = inner_ref.get_variable(storage, types)? {
|
||||||
match &var.ty {
|
match &var.ty {
|
||||||
TypeKind::Array(inner_ty, _) => Ok(Some(ScopeVariable {
|
TypeKind::Array(inner_ty, _) => Ok(Some(ScopeVariable {
|
||||||
ty: *inner_ty.clone(),
|
ty: *inner_ty.clone(),
|
||||||
@ -523,8 +642,34 @@ impl IndexedVariableReference {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IndexedVariableReferenceKind::StructIndex(indexed_variable_reference, _) => {
|
IndexedVariableReferenceKind::StructIndex(var_ref, field_name) => {
|
||||||
todo!("struct index refrence typecheck")
|
if let Some(var) = var_ref.get_variable(storage, types)? {
|
||||||
|
match &var.ty {
|
||||||
|
TypeKind::CustomType(type_name) => {
|
||||||
|
if let Some(kind) = types.get(type_name) {
|
||||||
|
match &kind {
|
||||||
|
TypeDefinitionKind::Struct(struct_type) => {
|
||||||
|
if let Some((_, field_ty)) =
|
||||||
|
struct_type.0.iter().find(|(n, _)| n == field_name)
|
||||||
|
{
|
||||||
|
Ok(Some(ScopeVariable {
|
||||||
|
ty: field_ty.clone(),
|
||||||
|
mutable: var.mutable,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(ErrorKind::NoSuchField(field_name.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ErrorKind::NoSuchType(type_name.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(ErrorKind::TriedAccessingNonStruct(var.ty.clone())),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -656,3 +801,15 @@ impl Collapsable for ScopeFunction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl pass::Scope {
|
||||||
|
pub fn get_struct_type(&self, name: &String) -> Result<&StructType, ErrorKind> {
|
||||||
|
let ty = self
|
||||||
|
.types
|
||||||
|
.get(&name)
|
||||||
|
.ok_or(ErrorKind::NoSuchType(name.clone()))?;
|
||||||
|
match ty {
|
||||||
|
TypeDefinitionKind::Struct(struct_ty) => Ok(struct_ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -412,25 +412,3 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pass::Scope {
|
|
||||||
fn get_struct_type(&self, name: &String) -> Result<&StructType, ErrorKind> {
|
|
||||||
let ty = self
|
|
||||||
.types
|
|
||||||
.get(&name)
|
|
||||||
.ok_or(ErrorKind::NoSuchType(name.clone()))?;
|
|
||||||
match ty {
|
|
||||||
TypeDefinitionKind::Struct(struct_ty) => Ok(struct_ty),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_struct_type_mut(&mut self, name: &String) -> Result<&mut StructType, ErrorKind> {
|
|
||||||
let ty = self
|
|
||||||
.types
|
|
||||||
.get_mut(&name)
|
|
||||||
.ok_or(ErrorKind::NoSuchType(name.clone()))?;
|
|
||||||
match ty {
|
|
||||||
TypeDefinitionKind::Struct(struct_ty) => Ok(struct_ty),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user