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