Add typeinference and typechecking for Associated Functions
This commit is contained in:
		
							parent
							
								
									46668b7099
								
							
						
					
					
						commit
						4d7c17a854
					
				| @ -9,6 +9,6 @@ impl Otus { | ||||
| } | ||||
| 
 | ||||
| fn main() -> u32 { | ||||
|     let otus = Otus {}; | ||||
|     let otus = Otus { field: 17 }; | ||||
|     return Otus::test(&otus); | ||||
| } | ||||
|  | ||||
| @ -443,7 +443,7 @@ impl Expression { | ||||
|                 }, | ||||
|                 Err(_) => Ok((ReturnKind::Soft, type_kind.clone())), | ||||
|             }, | ||||
|             AssociatedFunctionCall(type_kind, function_call) => todo!(), | ||||
|             AssociatedFunctionCall(_, fcall) => fcall.return_type(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -462,7 +462,7 @@ impl Expression { | ||||
|             ExprKind::FunctionCall(_) => None, | ||||
|             ExprKind::If(_) => None, | ||||
|             ExprKind::CastTo(expression, _) => expression.backing_var(), | ||||
|             ExprKind::AssociatedFunctionCall(type_kind, function_call) => None, | ||||
|             ExprKind::AssociatedFunctionCall(..) => None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -123,7 +123,8 @@ pub type BinopMap = Storage<BinopKey, ScopeBinopDef>; | ||||
| pub struct Scope<Data: Clone + Default> { | ||||
|     pub module_id: Option<SourceModuleId>, | ||||
|     pub binops: BinopMap, | ||||
|     pub function_returns: Storage<String, ScopeFunction>, | ||||
|     pub associated_functions: Storage<AssociatedFunctionKey, ScopeFunction>, | ||||
|     pub functions: Storage<String, ScopeFunction>, | ||||
|     pub variables: Storage<String, ScopeVariable>, | ||||
|     pub types: Storage<CustomTypeKey, TypeDefinition>, | ||||
|     /// Hard Return type of this scope, if inside a function
 | ||||
| @ -135,7 +136,8 @@ impl<Data: Clone + Default> Scope<Data> { | ||||
|     pub fn inner(&self) -> Scope<Data> { | ||||
|         Scope { | ||||
|             module_id: self.module_id, | ||||
|             function_returns: self.function_returns.clone(), | ||||
|             associated_functions: self.associated_functions.clone(), | ||||
|             functions: self.functions.clone(), | ||||
|             variables: self.variables.clone(), | ||||
|             binops: self.binops.clone(), | ||||
|             types: self.types.clone(), | ||||
| @ -181,6 +183,9 @@ pub struct ScopeVariable { | ||||
|     pub mutable: bool, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||
| pub struct AssociatedFunctionKey(pub TypeKind, pub String); | ||||
| 
 | ||||
| #[derive(Clone, Debug, Eq)] | ||||
| pub struct BinopKey { | ||||
|     pub params: (TypeKind, TypeKind), | ||||
| @ -389,7 +394,7 @@ impl Module { | ||||
| 
 | ||||
|         for function in &self.functions { | ||||
|             scope | ||||
|                 .function_returns | ||||
|                 .functions | ||||
|                 .set( | ||||
|                     function.name.clone(), | ||||
|                     ScopeFunction { | ||||
| @ -400,6 +405,19 @@ impl Module { | ||||
|                 .ok(); | ||||
|         } | ||||
| 
 | ||||
|         for (ty, function) in &self.associated_functions { | ||||
|             scope | ||||
|                 .associated_functions | ||||
|                 .set( | ||||
|                     AssociatedFunctionKey(ty.clone(), function.name.clone()), | ||||
|                     ScopeFunction { | ||||
|                         ret: function.return_type.clone(), | ||||
|                         params: function.parameters.iter().cloned().map(|v| v.1).collect(), | ||||
|                     }, | ||||
|                 ) | ||||
|                 .ok(); | ||||
|         } | ||||
| 
 | ||||
|         pass.module(self, PassState::from(state, scope, Some(self.module_id)))?; | ||||
| 
 | ||||
|         for function in &mut self.functions { | ||||
|  | ||||
| @ -24,12 +24,16 @@ pub enum ErrorKind { | ||||
|     TypesIncompatible(TypeKind, TypeKind), | ||||
|     #[error("Variable not defined: {0}")] | ||||
|     VariableNotDefined(String), | ||||
|     #[error("Function not defined: {0}")] | ||||
|     #[error("Function {0} not defined")] | ||||
|     FunctionNotDefined(String), | ||||
|     #[error("Function {0} not defined for type {1}")] | ||||
|     AssocFunctionNotDefined(String, TypeKind), | ||||
|     #[error("Expected a return type of {0}, got {1} instead")] | ||||
|     ReturnTypeMismatch(TypeKind, TypeKind), | ||||
|     #[error("Function {0} already defined {1}")] | ||||
|     FunctionAlreadyDefined(String, ErrorTypedefKind), | ||||
|     #[error("Function {0}::{1} already defined {2}")] | ||||
|     AssocFunctionAlreadyDefined(TypeKind, String, ErrorTypedefKind), | ||||
|     #[error("Variable already defined: {0}")] | ||||
|     VariableAlreadyDefined(String), | ||||
|     #[error("Variable {0} is not declared as mutable")] | ||||
|  | ||||
| @ -443,7 +443,7 @@ impl Expression { | ||||
|             ExprKind::FunctionCall(function_call) => { | ||||
|                 let true_function = state | ||||
|                     .scope | ||||
|                     .function_returns | ||||
|                     .functions | ||||
|                     .get(&function_call.name) | ||||
|                     .cloned() | ||||
|                     .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone())); | ||||
| @ -724,7 +724,56 @@ impl Expression { | ||||
|                 let expr = expression.typecheck(state, typerefs, HintKind::Default)?; | ||||
|                 expr.resolve_ref(typerefs).cast_into(type_kind) | ||||
|             } | ||||
|             ExprKind::AssociatedFunctionCall(type_kind, function_call) => todo!(), | ||||
|             ExprKind::AssociatedFunctionCall(type_kind, function_call) => { | ||||
|                 let true_function = state | ||||
|                     .scope | ||||
|                     .associated_functions | ||||
|                     .get(&pass::AssociatedFunctionKey( | ||||
|                         type_kind.clone(), | ||||
|                         function_call.name.clone(), | ||||
|                     )) | ||||
|                     .cloned() | ||||
|                     .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone())); | ||||
| 
 | ||||
|                 if let Some(f) = state.ok(true_function, self.1) { | ||||
|                     let param_len_given = function_call.parameters.len(); | ||||
|                     let param_len_expected = f.params.len(); | ||||
| 
 | ||||
|                     // Check that there are the same number of parameters given
 | ||||
|                     // as expected
 | ||||
|                     if param_len_given != param_len_expected { | ||||
|                         state.ok::<_, Infallible>( | ||||
|                             Err(ErrorKind::InvalidAmountParameters( | ||||
|                                 function_call.name.clone(), | ||||
|                                 param_len_given, | ||||
|                                 param_len_expected, | ||||
|                             )), | ||||
|                             self.1, | ||||
|                         ); | ||||
|                     } | ||||
| 
 | ||||
|                     let true_params_iter = f | ||||
|                         .params | ||||
|                         .into_iter() | ||||
|                         .chain(iter::repeat(TypeKind::Vague(Vague::Unknown))); | ||||
| 
 | ||||
|                     for (param, true_param_t) in function_call.parameters.iter_mut().zip(true_params_iter) { | ||||
|                         // Typecheck every param separately
 | ||||
|                         let param_res = param.typecheck(state, &typerefs, HintKind::Coerce(true_param_t.clone())); | ||||
|                         let param_t = state.or_else(param_res, TypeKind::Vague(Vague::Unknown), param.1); | ||||
|                         state.ok(param_t.narrow_into(&true_param_t), param.1); | ||||
|                     } | ||||
| 
 | ||||
|                     // Make sure function return type is the same as the claimed
 | ||||
|                     // return type
 | ||||
|                     let ret_t = f.ret.narrow_into(&function_call.return_type.resolve_ref(typerefs))?; | ||||
|                     // Update typing to be more accurate
 | ||||
|                     function_call.return_type = ret_t.clone(); | ||||
|                     Ok(ret_t.resolve_ref(typerefs)) | ||||
|                 } else { | ||||
|                     Ok(function_call.return_type.clone().resolve_ref(typerefs)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -12,8 +12,8 @@ use std::{ | ||||
| 
 | ||||
| use crate::{ | ||||
|     mir::{ | ||||
|         BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind, | ||||
|         IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement, | ||||
|         pass::AssociatedFunctionKey, BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, | ||||
|         FunctionDefinitionKind, IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement, | ||||
|     }, | ||||
|     util::try_all, | ||||
| }; | ||||
| @ -60,6 +60,29 @@ impl<'t> Pass for TypeInference<'t> { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let mut seen_assoc_functions = HashMap::new(); | ||||
|         for (ty, function) in &mut module.associated_functions { | ||||
|             if let Some(kind) = seen_assoc_functions.get(&(ty.clone(), function.name.clone())) { | ||||
|                 state.note_errors( | ||||
|                     &vec![ErrorKind::AssocFunctionAlreadyDefined( | ||||
|                         ty.clone(), | ||||
|                         function.name.clone(), | ||||
|                         *kind, | ||||
|                     )], | ||||
|                     function.signature(), | ||||
|                 ); | ||||
|             } else { | ||||
|                 seen_assoc_functions.insert( | ||||
|                     (ty.clone(), function.name.clone()), | ||||
|                     match function.kind { | ||||
|                         FunctionDefinitionKind::Local(..) => ErrorTypedefKind::Local, | ||||
|                         FunctionDefinitionKind::Extern(..) => ErrorTypedefKind::Extern, | ||||
|                         FunctionDefinitionKind::Intrinsic(..) => ErrorTypedefKind::Intrinsic, | ||||
|                     }, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let mut seen_binops = HashSet::new(); | ||||
|         for binop in &module.binop_defs { | ||||
|             let binop_key = BinopKey { | ||||
| @ -365,7 +388,7 @@ impl Expression { | ||||
|                 // Get function definition and types
 | ||||
|                 let fn_call = state | ||||
|                     .scope | ||||
|                     .function_returns | ||||
|                     .functions | ||||
|                     .get(&function_call.name) | ||||
|                     .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()))? | ||||
|                     .clone(); | ||||
| @ -566,7 +589,33 @@ impl Expression { | ||||
|                 expression.infer_types(state, type_refs)?; | ||||
|                 Ok(type_refs.from_type(type_kind).unwrap()) | ||||
|             } | ||||
|             ExprKind::AssociatedFunctionCall(type_kind, function_call) => todo!(), | ||||
|             ExprKind::AssociatedFunctionCall(type_kind, function_call) => { | ||||
|                 // Get function definition and types
 | ||||
|                 let fn_call = state | ||||
|                     .scope | ||||
|                     .associated_functions | ||||
|                     .get(&AssociatedFunctionKey(type_kind.clone(), function_call.name.clone())) | ||||
|                     .ok_or(ErrorKind::AssocFunctionNotDefined( | ||||
|                         function_call.name.clone(), | ||||
|                         type_kind.clone(), | ||||
|                     ))? | ||||
|                     .clone(); | ||||
| 
 | ||||
|                 // Infer param expression types and narrow them to the
 | ||||
|                 // expected function parameters (or Unknown types if too
 | ||||
|                 // many were provided)
 | ||||
|                 let true_params_iter = fn_call.params.iter().chain(iter::repeat(&Vague(Unknown))); | ||||
| 
 | ||||
|                 for (param_expr, param_t) in function_call.parameters.iter_mut().zip(true_params_iter) { | ||||
|                     let expr_res = param_expr.infer_types(state, type_refs); | ||||
|                     if let Some(mut param_ref) = state.ok(expr_res, param_expr.1) { | ||||
|                         param_ref.narrow(&mut type_refs.from_type(param_t).unwrap()); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // Provide function return type
 | ||||
|                 Ok(type_refs.from_type(&fn_call.ret).unwrap()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user