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 { | fn main() -> u32 { | ||||||
|     let otus = Otus {}; |     let otus = Otus { field: 17 }; | ||||||
|     return Otus::test(&otus); |     return Otus::test(&otus); | ||||||
| } | } | ||||||
|  | |||||||
| @ -443,7 +443,7 @@ impl Expression { | |||||||
|                 }, |                 }, | ||||||
|                 Err(_) => Ok((ReturnKind::Soft, type_kind.clone())), |                 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::FunctionCall(_) => None, | ||||||
|             ExprKind::If(_) => None, |             ExprKind::If(_) => None, | ||||||
|             ExprKind::CastTo(expression, _) => expression.backing_var(), |             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 struct Scope<Data: Clone + Default> { | ||||||
|     pub module_id: Option<SourceModuleId>, |     pub module_id: Option<SourceModuleId>, | ||||||
|     pub binops: BinopMap, |     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 variables: Storage<String, ScopeVariable>, | ||||||
|     pub types: Storage<CustomTypeKey, TypeDefinition>, |     pub types: Storage<CustomTypeKey, TypeDefinition>, | ||||||
|     /// Hard Return type of this scope, if inside a function
 |     /// 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> { |     pub fn inner(&self) -> Scope<Data> { | ||||||
|         Scope { |         Scope { | ||||||
|             module_id: self.module_id, |             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(), |             variables: self.variables.clone(), | ||||||
|             binops: self.binops.clone(), |             binops: self.binops.clone(), | ||||||
|             types: self.types.clone(), |             types: self.types.clone(), | ||||||
| @ -181,6 +183,9 @@ pub struct ScopeVariable { | |||||||
|     pub mutable: bool, |     pub mutable: bool, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||||
|  | pub struct AssociatedFunctionKey(pub TypeKind, pub String); | ||||||
|  | 
 | ||||||
| #[derive(Clone, Debug, Eq)] | #[derive(Clone, Debug, Eq)] | ||||||
| pub struct BinopKey { | pub struct BinopKey { | ||||||
|     pub params: (TypeKind, TypeKind), |     pub params: (TypeKind, TypeKind), | ||||||
| @ -389,7 +394,7 @@ impl Module { | |||||||
| 
 | 
 | ||||||
|         for function in &self.functions { |         for function in &self.functions { | ||||||
|             scope |             scope | ||||||
|                 .function_returns |                 .functions | ||||||
|                 .set( |                 .set( | ||||||
|                     function.name.clone(), |                     function.name.clone(), | ||||||
|                     ScopeFunction { |                     ScopeFunction { | ||||||
| @ -400,6 +405,19 @@ impl Module { | |||||||
|                 .ok(); |                 .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)))?; |         pass.module(self, PassState::from(state, scope, Some(self.module_id)))?; | ||||||
| 
 | 
 | ||||||
|         for function in &mut self.functions { |         for function in &mut self.functions { | ||||||
|  | |||||||
| @ -24,12 +24,16 @@ pub enum ErrorKind { | |||||||
|     TypesIncompatible(TypeKind, TypeKind), |     TypesIncompatible(TypeKind, TypeKind), | ||||||
|     #[error("Variable not defined: {0}")] |     #[error("Variable not defined: {0}")] | ||||||
|     VariableNotDefined(String), |     VariableNotDefined(String), | ||||||
|     #[error("Function not defined: {0}")] |     #[error("Function {0} not defined")] | ||||||
|     FunctionNotDefined(String), |     FunctionNotDefined(String), | ||||||
|  |     #[error("Function {0} not defined for type {1}")] | ||||||
|  |     AssocFunctionNotDefined(String, TypeKind), | ||||||
|     #[error("Expected a return type of {0}, got {1} instead")] |     #[error("Expected a return type of {0}, got {1} instead")] | ||||||
|     ReturnTypeMismatch(TypeKind, TypeKind), |     ReturnTypeMismatch(TypeKind, TypeKind), | ||||||
|     #[error("Function {0} already defined {1}")] |     #[error("Function {0} already defined {1}")] | ||||||
|     FunctionAlreadyDefined(String, ErrorTypedefKind), |     FunctionAlreadyDefined(String, ErrorTypedefKind), | ||||||
|  |     #[error("Function {0}::{1} already defined {2}")] | ||||||
|  |     AssocFunctionAlreadyDefined(TypeKind, String, ErrorTypedefKind), | ||||||
|     #[error("Variable already defined: {0}")] |     #[error("Variable already defined: {0}")] | ||||||
|     VariableAlreadyDefined(String), |     VariableAlreadyDefined(String), | ||||||
|     #[error("Variable {0} is not declared as mutable")] |     #[error("Variable {0} is not declared as mutable")] | ||||||
|  | |||||||
| @ -443,7 +443,7 @@ impl Expression { | |||||||
|             ExprKind::FunctionCall(function_call) => { |             ExprKind::FunctionCall(function_call) => { | ||||||
|                 let true_function = state |                 let true_function = state | ||||||
|                     .scope |                     .scope | ||||||
|                     .function_returns |                     .functions | ||||||
|                     .get(&function_call.name) |                     .get(&function_call.name) | ||||||
|                     .cloned() |                     .cloned() | ||||||
|                     .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone())); |                     .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone())); | ||||||
| @ -724,7 +724,56 @@ impl Expression { | |||||||
|                 let expr = expression.typecheck(state, typerefs, HintKind::Default)?; |                 let expr = expression.typecheck(state, typerefs, HintKind::Default)?; | ||||||
|                 expr.resolve_ref(typerefs).cast_into(type_kind) |                 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::{ | use crate::{ | ||||||
|     mir::{ |     mir::{ | ||||||
|         BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind, |         pass::AssociatedFunctionKey, BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, | ||||||
|         IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement, |         FunctionDefinitionKind, IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement, | ||||||
|     }, |     }, | ||||||
|     util::try_all, |     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(); |         let mut seen_binops = HashSet::new(); | ||||||
|         for binop in &module.binop_defs { |         for binop in &module.binop_defs { | ||||||
|             let binop_key = BinopKey { |             let binop_key = BinopKey { | ||||||
| @ -365,7 +388,7 @@ impl Expression { | |||||||
|                 // Get function definition and types
 |                 // Get function definition and types
 | ||||||
|                 let fn_call = state |                 let fn_call = state | ||||||
|                     .scope |                     .scope | ||||||
|                     .function_returns |                     .functions | ||||||
|                     .get(&function_call.name) |                     .get(&function_call.name) | ||||||
|                     .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()))? |                     .ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()))? | ||||||
|                     .clone(); |                     .clone(); | ||||||
| @ -566,7 +589,33 @@ impl Expression { | |||||||
|                 expression.infer_types(state, type_refs)?; |                 expression.infer_types(state, type_refs)?; | ||||||
|                 Ok(type_refs.from_type(type_kind).unwrap()) |                 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