Implement type inference for structs
This commit is contained in:
		
							parent
							
								
									e13b6349f0
								
							
						
					
					
						commit
						1d1e574136
					
				| @ -2,7 +2,7 @@ use std::path::PathBuf; | ||||
| 
 | ||||
| use crate::{ | ||||
|     ast::{self}, | ||||
|     mir::{self, NamedVariableRef, StmtKind}, | ||||
|     mir::{self, NamedVariableRef, StmtKind, StructType}, | ||||
| }; | ||||
| 
 | ||||
| impl mir::Context { | ||||
| @ -68,12 +68,12 @@ impl ast::Module { | ||||
|                         name: name.clone(), | ||||
|                         kind: match kind { | ||||
|                             ast::TypeDefinitionKind::Struct(struct_definition_fields) => { | ||||
|                                 mir::TypeDefinitionKind::Struct( | ||||
|                                 mir::TypeDefinitionKind::Struct(StructType( | ||||
|                                     struct_definition_fields | ||||
|                                         .iter() | ||||
|                                         .map(|s| (s.name.clone(), s.ty.clone().into())) | ||||
|                                         .collect(), | ||||
|                                 ) | ||||
|                                 )) | ||||
|                             } | ||||
|                         }, | ||||
|                         meta: (*range).into(), | ||||
|  | ||||
| @ -518,7 +518,7 @@ impl TypeKind { | ||||
|             TypeKind::Array(elem_t, _) => Type::Ptr(Box::new(elem_t.get_type())), | ||||
|             TypeKind::Void => Type::Void, | ||||
|             TypeKind::Vague(_) => panic!("Tried to compile a vague type!"), | ||||
|             TypeKind::CustomType(_, custom_type_kind) => todo!("codegen for custom type"), | ||||
|             TypeKind::CustomType(_) => todo!("codegen for custom type"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -55,7 +55,7 @@ impl Display for TypeDefinitionKind { | ||||
|                 writeln!(f)?; | ||||
|                 let mut state = Default::default(); | ||||
|                 let mut inner_f = PadAdapter::wrap(f, &mut state); | ||||
|                 for (field_name, field_ty) in items { | ||||
|                 for (field_name, field_ty) in &items.0 { | ||||
|                     writeln!(inner_f, "{}: {:?},", field_name, field_ty)?; | ||||
|                 } | ||||
|                 f.write_char('}') | ||||
|  | ||||
| @ -2,9 +2,9 @@ | ||||
| //! Reid. It contains a simplified version of Reid which can be e.g.
 | ||||
| //! typechecked.
 | ||||
| 
 | ||||
| use std::path::PathBuf; | ||||
| use std::{collections::HashMap, path::PathBuf}; | ||||
| 
 | ||||
| use crate::{ast::Type, token_stream::TokenRange}; | ||||
| use crate::token_stream::TokenRange; | ||||
| 
 | ||||
| mod display; | ||||
| pub mod linker; | ||||
| @ -65,8 +65,8 @@ pub enum TypeKind { | ||||
|     StringPtr, | ||||
|     #[error("[{0}; {1}]")] | ||||
|     Array(Box<TypeKind>, u64), | ||||
|     #[error("{0} ({1})")] | ||||
|     CustomType(String, CustomTypeKind), | ||||
|     #[error("{0}")] | ||||
|     CustomType(String), | ||||
|     #[error(transparent)] | ||||
|     Vague(#[from] VagueType), | ||||
| } | ||||
| @ -81,13 +81,10 @@ pub enum VagueType { | ||||
|     TypeRef(usize), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] | ||||
| pub enum CustomTypeKind { | ||||
|     #[error("struct({0:?})")] | ||||
|     Struct(Vec<TypeKind>), | ||||
|     #[error("CustomType")] | ||||
|     Unknown, | ||||
| } | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct StructType(pub Vec<(String, TypeKind)>); | ||||
| 
 | ||||
| pub type TypedefMap = HashMap<String, TypeDefinitionKind>; | ||||
| 
 | ||||
| impl TypeKind { | ||||
|     pub fn known(&self) -> Result<TypeKind, VagueType> { | ||||
| @ -100,7 +97,7 @@ impl TypeKind { | ||||
| } | ||||
| 
 | ||||
| impl TypeKind { | ||||
|     pub fn signed(&self) -> bool { | ||||
|     pub fn signed(&self, typedefs: &TypedefMap) -> bool { | ||||
|         match self { | ||||
|             TypeKind::Void => false, | ||||
|             TypeKind::Vague(_) => false, | ||||
| @ -117,11 +114,13 @@ impl TypeKind { | ||||
|             TypeKind::U128 => false, | ||||
|             TypeKind::StringPtr => false, | ||||
|             TypeKind::Array(_, _) => false, | ||||
|             TypeKind::CustomType(_, _) => false, | ||||
|             TypeKind::CustomType(name) => match typedefs.get(name).unwrap() { | ||||
|                 TypeDefinitionKind::Struct(_) => false, | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_maths(&self) -> bool { | ||||
|     pub fn is_maths(&self, typedefs: &TypedefMap) -> bool { | ||||
|         use TypeKind::*; | ||||
|         match &self { | ||||
|             I8 => true, | ||||
| @ -139,7 +138,9 @@ impl TypeKind { | ||||
|             Void => false, | ||||
|             StringPtr => false, | ||||
|             Array(_, _) => false, | ||||
|             TypeKind::CustomType(_, _) => false, | ||||
|             TypeKind::CustomType(name) => match typedefs.get(name).unwrap() { | ||||
|                 TypeDefinitionKind::Struct(_) => false, | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -328,9 +329,9 @@ pub struct TypeDefinition { | ||||
|     pub meta: Metadata, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum TypeDefinitionKind { | ||||
|     Struct(Vec<(String, TypeKind)>), | ||||
|     Struct(StructType), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
|  | ||||
| @ -103,13 +103,17 @@ 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)] | ||||
| pub struct Scope { | ||||
|     pub function_returns: Storage<ScopeFunction>, | ||||
|     pub variables: Storage<ScopeVariable>, | ||||
|     pub types: Storage<ScopeTypedefKind>, | ||||
|     pub types: Storage<TypeDefinitionKind>, | ||||
|     /// Hard Return type of this scope, if inside a function
 | ||||
|     pub return_type_hint: Option<TypeKind>, | ||||
| } | ||||
| @ -126,11 +130,6 @@ pub struct ScopeVariable { | ||||
|     pub mutable: bool, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum ScopeTypedefKind { | ||||
|     Struct(Vec<(String, TypeKind)>), | ||||
| } | ||||
| 
 | ||||
| impl Scope { | ||||
|     pub fn inner(&self) -> Scope { | ||||
|         Scope { | ||||
| @ -140,6 +139,10 @@ impl Scope { | ||||
|             return_type_hint: self.return_type_hint.clone(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_typedefs(&self) -> &TypedefMap { | ||||
|         &self.types.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct PassState<'st, 'sc, TError: STDError + Clone> { | ||||
| @ -227,7 +230,7 @@ impl Module { | ||||
|     fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) { | ||||
|         for typedef in &self.typedefs { | ||||
|             let kind = match &typedef.kind { | ||||
|                 TypeDefinitionKind::Struct(fields) => ScopeTypedefKind::Struct(fields.clone()), | ||||
|                 TypeDefinitionKind::Struct(fields) => TypeDefinitionKind::Struct(fields.clone()), | ||||
|             }; | ||||
|             scope.types.set(typedef.name.clone(), kind).ok(); | ||||
|         } | ||||
|  | ||||
| @ -43,6 +43,12 @@ pub enum ErrorKind { | ||||
|     TriedIndexingNonArray(TypeKind), | ||||
|     #[error("Index {0} out of bounds ({1})")] | ||||
|     IndexOutOfBounds(u64, u64), | ||||
|     #[error("No such type {0} could be found")] | ||||
|     NoSuchType(String), | ||||
|     #[error("Attempted to access field of non-struct type of {0}")] | ||||
|     TriedAccessingNonStruct(TypeKind), | ||||
|     #[error("No such struct-field on type {0}")] | ||||
|     NoSuchField(String), | ||||
| } | ||||
| 
 | ||||
| /// Struct used to implement a type-checking pass that can be performed on the
 | ||||
|  | ||||
| @ -4,17 +4,18 @@ | ||||
| //! must then be passed through TypeCheck with the same [`TypeRefs`] in order to
 | ||||
| //! place the correct types from the IDs and check that there are no issues.
 | ||||
| 
 | ||||
| use std::iter; | ||||
| use std::{convert::Infallible, iter}; | ||||
| 
 | ||||
| use crate::{mir::TypeKind, util::try_all}; | ||||
| 
 | ||||
| use super::{ | ||||
|     pass::{Pass, PassState}, | ||||
|     pass::{self, Pass, PassState}, | ||||
|     typecheck::ErrorKind, | ||||
|     typerefs::{ScopeTypeRefs, TypeRef, TypeRefs}, | ||||
|     types::{pick_return, ReturnType}, | ||||
|     Block, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind, IfExpression, | ||||
|     IndexedVariableReference, Module, NamedVariableRef, ReturnKind, StmtKind, | ||||
|     IndexedVariableReference, IndexedVariableReferenceKind, Module, NamedVariableRef, ReturnKind, | ||||
|     StmtKind, StructType, TypeDefinitionKind, | ||||
|     TypeKind::*, | ||||
|     VagueType::*, | ||||
| }; | ||||
| @ -106,7 +107,7 @@ impl Block { | ||||
|                 } | ||||
|                 StmtKind::Set(var, expr) => { | ||||
|                     // Get the TypeRef for this variable declaration
 | ||||
|                     let var_ref = var.find_hint(&inner_hints)?; | ||||
|                     let var_ref = var.find_hint(&state, &inner_hints)?; | ||||
| 
 | ||||
|                     // If ok, update the MIR type to this TypeRef
 | ||||
|                     if let Some((_, var_ref)) = &var_ref { | ||||
| @ -155,14 +156,15 @@ impl Block { | ||||
| impl IndexedVariableReference { | ||||
|     fn find_hint<'s>( | ||||
|         &self, | ||||
|         state: &PassState<ErrorKind>, | ||||
|         hints: &'s ScopeTypeRefs, | ||||
|     ) -> Result<Option<(bool, TypeRef<'s>)>, ErrorKind> { | ||||
|         match &self.kind { | ||||
|             super::IndexedVariableReferenceKind::Named(NamedVariableRef(_, name, _)) => { | ||||
|                 Ok(hints.find_hint(&name)) | ||||
|             IndexedVariableReferenceKind::Named(NamedVariableRef(_, name, _)) => { | ||||
|                 Ok(hints.find_var(&name)) | ||||
|             } | ||||
|             super::IndexedVariableReferenceKind::ArrayIndex(inner, _) => { | ||||
|                 if let Some((mutable, inner_ref)) = inner.find_hint(hints)? { | ||||
|             IndexedVariableReferenceKind::ArrayIndex(inner, _) => { | ||||
|                 if let Some((mutable, inner_ref)) = inner.find_hint(state, hints)? { | ||||
|                     // Check that the resolved type is at least an array, no
 | ||||
|                     // need for further resolution.
 | ||||
|                     let inner_ty = inner_ref.resolve_weak().unwrap(); | ||||
| @ -177,8 +179,30 @@ impl IndexedVariableReference { | ||||
|                     Ok(None) | ||||
|                 } | ||||
|             } | ||||
|             super::IndexedVariableReferenceKind::StructIndex(indexed_variable_reference, _) => { | ||||
|                 todo!("struct index refrence type inference") | ||||
|             IndexedVariableReferenceKind::StructIndex(inner, field_name) => { | ||||
|                 if let Some((mutable, inner_ref)) = inner.find_hint(state, hints)? { | ||||
|                     // Check that the resolved type is at least an array, no
 | ||||
|                     // need for further resolution.
 | ||||
|                     let inner_ty = inner_ref.resolve_weak().unwrap(); | ||||
|                     match &inner_ty { | ||||
|                         CustomType(struct_name) => match state.scope.types.get(&struct_name) { | ||||
|                             Some(kind) => match kind { | ||||
|                                 TypeDefinitionKind::Struct(struct_ty) => Ok(hints | ||||
|                                     .from_type( | ||||
|                                         &struct_ty | ||||
|                                             .get_field_ty(field_name) | ||||
|                                             .cloned() | ||||
|                                             .ok_or(ErrorKind::NoSuchField(self.get_name()))?, | ||||
|                                     ) | ||||
|                                     .map(|v| (mutable, v))), | ||||
|                             }, | ||||
|                             None => Err(ErrorKind::TriedAccessingNonStruct(inner_ty.clone())), | ||||
|                         }, | ||||
|                         _ => Err(ErrorKind::TriedAccessingNonStruct(inner_ty)), | ||||
|                     } | ||||
|                 } else { | ||||
|                     Ok(None) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -194,7 +218,7 @@ impl Expression { | ||||
|             ExprKind::Variable(var) => { | ||||
|                 // Find variable type
 | ||||
|                 let type_ref = type_refs | ||||
|                     .find_hint(&var.1) | ||||
|                     .find_var(&var.1) | ||||
|                     .map(|(_, hint)| hint) | ||||
|                     .ok_or(ErrorKind::VariableNotDefined(var.1.clone())); | ||||
| 
 | ||||
| @ -339,10 +363,71 @@ impl Expression { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             ExprKind::StructIndex(expression, type_kind, _) => { | ||||
|                 todo!("type inference for struct indexes") | ||||
|             ExprKind::StructIndex(expression, type_kind, field_name) => { | ||||
|                 let expr_ty = expression.infer_types(state, type_refs)?; | ||||
| 
 | ||||
|                 // Check that the resolved type is at least a struct, no
 | ||||
|                 // need for further resolution.
 | ||||
|                 let kind = expr_ty.resolve_weak().unwrap(); | ||||
|                 match kind { | ||||
|                     CustomType(name) => { | ||||
|                         let struct_ty = state.scope.get_struct_type_mut(&name)?; | ||||
|                         match struct_ty.get_field_ty_mut(&field_name) { | ||||
|                             Some(field_ty) => { | ||||
|                                 let elem_ty = type_refs.from_type(&type_kind).unwrap(); | ||||
|                                 *field_ty = elem_ty.as_type().clone(); | ||||
|                                 Ok(elem_ty) | ||||
|                             } | ||||
|                             None => Err(ErrorKind::NoSuchField(field_name.clone())), | ||||
|                         } | ||||
|                     } | ||||
|                     _ => Err(ErrorKind::TriedAccessingNonStruct(kind)), | ||||
|                 } | ||||
|             } | ||||
|             ExprKind::Struct(struct_name, fields) => { | ||||
|                 let expected_struct_ty = state.scope.get_struct_type(&struct_name)?.clone(); | ||||
|                 for field in fields { | ||||
|                     if let Some(expected_field_ty) = expected_struct_ty.get_field_ty(&field.0) { | ||||
|                         let field_ty = field.1.infer_types(state, type_refs); | ||||
|                         if let Some(mut field_ty) = state.ok(field_ty, field.1 .1) { | ||||
|                             field_ty.narrow(&type_refs.from_type(&expected_field_ty).unwrap()); | ||||
|                         } | ||||
|                     } else { | ||||
|                         state.ok::<_, Infallible>( | ||||
|                             Err(ErrorKind::NoSuchField(format!( | ||||
|                                 "{}.{}", | ||||
|                                 struct_name, field.0 | ||||
|                             ))), | ||||
|                             field.1 .1, | ||||
|                         ); | ||||
|                     } | ||||
|                 } | ||||
|                 Ok(type_refs | ||||
|                     .from_type(&TypeKind::CustomType(struct_name.clone())) | ||||
|                     .unwrap()) | ||||
|             } | ||||
|             ExprKind::Struct(_, items) => todo!("type inference for struct 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), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -8,7 +8,7 @@ use crate::mir::VagueType; | ||||
| 
 | ||||
| use super::{ | ||||
|     typecheck::{Collapsable, ErrorKind}, | ||||
|     BinaryOperator, TypeKind, | ||||
|     BinaryOperator, TypeDefinition, TypeKind, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| @ -209,12 +209,12 @@ impl<'outer> ScopeTypeRefs<'outer> { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn find_hint(&'outer self, name: &String) -> Option<(bool, TypeRef<'outer>)> { | ||||
|     pub fn find_var(&'outer self, name: &String) -> Option<(bool, TypeRef<'outer>)> { | ||||
|         self.variables | ||||
|             .borrow() | ||||
|             .get(name) | ||||
|             .map(|(mutable, idx)| (*mutable, TypeRef(idx.clone(), self))) | ||||
|             .or(self.outer.map(|o| o.find_hint(name)).flatten()) | ||||
|             .or(self.outer.map(|o| o.find_var(name)).flatten()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn binop( | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
| use crate::util::try_all; | ||||
| 
 | ||||
| use super::*; | ||||
| @ -29,6 +31,16 @@ impl TypeKind { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl StructType { | ||||
|     pub fn get_field_ty(&self, name: &String) -> Option<&TypeKind> { | ||||
|         self.0.iter().find(|(n, _)| n == name).map(|(_, ty)| ty) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_field_ty_mut(&mut self, name: &String) -> Option<&mut TypeKind> { | ||||
|         self.0.iter_mut().find(|(n, _)| n == name).map(|(_, ty)| ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait ReturnType { | ||||
|     /// Return the return type of this node
 | ||||
|     fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther>; | ||||
| @ -108,18 +120,8 @@ impl ReturnType for Expression { | ||||
|                     TypeKind::Array(Box::new(first.1), expressions.len() as u64), | ||||
|                 )) | ||||
|             } | ||||
|             StructIndex(expression, type_kind, _) => todo!("todo return type for struct index"), | ||||
|             Struct(name, items) => { | ||||
|                 let f_types = try_all(items.iter().map(|e| e.1.return_type()).collect()) | ||||
|                     .map_err(|e| unsafe { e.get_unchecked(0).clone() })? | ||||
|                     .iter() | ||||
|                     .map(|r| r.1.clone()) | ||||
|                     .collect(); | ||||
|                 Ok(( | ||||
|                     ReturnKind::Soft, | ||||
|                     TypeKind::CustomType(name.clone(), CustomTypeKind::Struct(f_types)), | ||||
|                 )) | ||||
|             } | ||||
|             StructIndex(_, type_kind, _) => Ok((ReturnKind::Soft, type_kind.clone())), | ||||
|             Struct(name, _) => Ok((ReturnKind::Soft, TypeKind::CustomType(name.clone()))), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -11,5 +11,5 @@ fn main() -> u32 { | ||||
|         second: 3, | ||||
|     }; | ||||
| 
 | ||||
|     return Test.second; | ||||
|     return value.second; | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user