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> { | ||||
|         self.0.get(key) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_mut(&mut self, key: &String) -> Option<&mut T> { | ||||
|         self.0.get_mut(key) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Default, Debug)] | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| //! This module contains code relevant to doing a type checking pass on the MIR.
 | ||||
| //! 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 VagueType as Vague; | ||||
| @ -49,6 +49,12 @@ pub enum ErrorKind { | ||||
|     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), | ||||
| } | ||||
| 
 | ||||
| /// 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, | ||||
| } | ||||
| 
 | ||||
| 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> { | ||||
|     type TError = 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 { | ||||
|             let res = function.typecheck(&self.refs, &mut state.inner()); | ||||
|             state.ok(res, function.block_meta()); | ||||
| @ -185,7 +253,8 @@ impl Block { | ||||
|                 StmtKind::Set(variable_reference, expression) => { | ||||
|                     if let Some(var) = state | ||||
|                         .ok( | ||||
|                             variable_reference.get_variable(&state.scope.variables), | ||||
|                             variable_reference | ||||
|                                 .get_variable(&state.scope.variables, &state.scope.types), | ||||
|                             variable_reference.meta, | ||||
|                         ) | ||||
|                         .flatten() | ||||
| @ -493,10 +562,59 @@ impl Expression { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             ExprKind::StructIndex(expression, type_kind, _) => { | ||||
|                 todo!("typechecking for struct index") | ||||
|             ExprKind::StructIndex(expression, type_kind, field_name) => { | ||||
|                 // 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( | ||||
|         &self, | ||||
|         storage: &Storage<ScopeVariable>, | ||||
|         types: &Storage<TypeDefinitionKind>, | ||||
|     ) -> Result<Option<ScopeVariable>, ErrorKind> { | ||||
|         match &self.kind { | ||||
|             IndexedVariableReferenceKind::Named(NamedVariableRef(_, name, _)) => { | ||||
|                 Ok(storage.get(&name).cloned()) | ||||
|             } | ||||
|             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 { | ||||
|                         TypeKind::Array(inner_ty, _) => Ok(Some(ScopeVariable { | ||||
|                             ty: *inner_ty.clone(), | ||||
| @ -523,8 +642,34 @@ impl IndexedVariableReference { | ||||
|                     Ok(None) | ||||
|                 } | ||||
|             } | ||||
|             IndexedVariableReferenceKind::StructIndex(indexed_variable_reference, _) => { | ||||
|                 todo!("struct index refrence typecheck") | ||||
|             IndexedVariableReferenceKind::StructIndex(var_ref, field_name) => { | ||||
|                 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