Add ScopeHints tentatively
This commit is contained in:
parent
04e0c136df
commit
be7fa71b53
@ -4,7 +4,7 @@ fn indirection() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
fn main() -> u16 {
|
||||
let mut test = 5;
|
||||
|
||||
if indirection() {
|
||||
|
@ -1,13 +1,13 @@
|
||||
//! 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::{cell::RefCell, collections::HashMap, convert::Infallible, iter, marker::PhantomData};
|
||||
|
||||
use crate::{mir::*, util::try_all};
|
||||
use TypeKind::*;
|
||||
use VagueType::*;
|
||||
|
||||
use super::{
|
||||
pass::{Pass, PassState, ScopeFunction, ScopeVariable},
|
||||
pass::{Pass, PassState, ScopeFunction, ScopeVariable, Storage},
|
||||
types::ReturnType,
|
||||
};
|
||||
|
||||
@ -43,6 +43,78 @@ pub enum ErrorKind {
|
||||
/// MIR.
|
||||
pub struct TypeCheck;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeHint<'scope>(usize, &'scope ScopeHints<'scope>);
|
||||
|
||||
impl<'scope> ScopeHint<'scope> {
|
||||
fn resolve(&self) -> TypeKind {
|
||||
let mut scope = self.1;
|
||||
while !scope.type_hints.borrow().contains_key(&self.0) {
|
||||
scope = scope.outer.as_ref().unwrap();
|
||||
}
|
||||
scope.type_hints.borrow().get(&self.0).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ScopeHints<'outer> {
|
||||
outer: Option<&'outer ScopeHints<'outer>>,
|
||||
/// Mapping of what types variables point to
|
||||
variables: RefCell<HashMap<String, usize>>,
|
||||
/// Simple list of types that variables can refrence
|
||||
type_hints: RefCell<HashMap<usize, TypeKind>>,
|
||||
}
|
||||
|
||||
impl<'outer> ScopeHints<'outer> {
|
||||
fn get_idx(&self) -> usize {
|
||||
self.type_hints.borrow().len() + self.outer.as_ref().map(|o| o.get_idx()).unwrap_or(0)
|
||||
}
|
||||
|
||||
fn new_var(&'outer self, name: String, initial_ty: TypeKind) -> ScopeHint<'outer> {
|
||||
if self.variables.borrow().contains_key(&name) {
|
||||
panic!("Variable was already defined!")
|
||||
}
|
||||
let idx = self.get_idx();
|
||||
self.variables.borrow_mut().insert(name, idx);
|
||||
self.type_hints.borrow_mut().insert(idx, initial_ty);
|
||||
ScopeHint(idx, self)
|
||||
}
|
||||
|
||||
fn narrow_hint(
|
||||
&'outer self,
|
||||
hint: &ScopeHint,
|
||||
ty: &TypeKind,
|
||||
) -> Result<ScopeHint<'outer>, ErrorKind> {
|
||||
let mut hints = self.type_hints.borrow_mut();
|
||||
let existing = hints.get_mut(&hint.0).unwrap();
|
||||
*existing = existing.collapse_into(&ty)?;
|
||||
Ok(ScopeHint(hint.0, self))
|
||||
}
|
||||
|
||||
fn combine_vars(
|
||||
&'outer self,
|
||||
hint1: &ScopeHint,
|
||||
hint2: &ScopeHint,
|
||||
) -> Result<ScopeHint<'outer>, ErrorKind> {
|
||||
let ty = self.type_hints.borrow().get(&hint2.0).unwrap().clone();
|
||||
self.narrow_hint(&hint1, &ty)?;
|
||||
for (_, val) in self.variables.borrow_mut().iter_mut() {
|
||||
if *val == hint2.0 {
|
||||
*val = hint1.0;
|
||||
}
|
||||
}
|
||||
Ok(ScopeHint(hint1.0, self))
|
||||
}
|
||||
|
||||
fn inner(&'outer self) -> ScopeHints<'outer> {
|
||||
ScopeHints {
|
||||
outer: Some(self),
|
||||
variables: Default::default(),
|
||||
type_hints: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pass for TypeCheck {
|
||||
type TError = ErrorKind;
|
||||
|
||||
@ -76,7 +148,7 @@ impl FunctionDefinition {
|
||||
let inferred = match &mut self.kind {
|
||||
FunctionDefinitionKind::Local(block, _) => {
|
||||
state.scope.return_type_hint = Some(self.return_type);
|
||||
block.typecheck(state, Some(return_type))
|
||||
block.typecheck(state, &ScopeHints::default(), Some(return_type))
|
||||
}
|
||||
FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
|
||||
};
|
||||
@ -93,16 +165,18 @@ impl Block {
|
||||
fn typecheck(
|
||||
&mut self,
|
||||
state: &mut PassState<ErrorKind>,
|
||||
hints: &ScopeHints,
|
||||
hint_t: Option<TypeKind>,
|
||||
) -> Result<TypeKind, ErrorKind> {
|
||||
let mut state = state.inner();
|
||||
let hints = hints.inner();
|
||||
|
||||
let mut early_return = None;
|
||||
|
||||
for statement in &mut self.statements {
|
||||
let ret = match &mut statement.0 {
|
||||
StmtKind::Let(variable_reference, mutable, expression) => {
|
||||
let res = expression.typecheck(&mut state, Some(variable_reference.0));
|
||||
let res = expression.typecheck(&mut state, &hints, Some(variable_reference.0));
|
||||
|
||||
// If expression resolution itself was erronous, resolve as
|
||||
// Unknown.
|
||||
@ -121,7 +195,7 @@ impl Block {
|
||||
state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2);
|
||||
|
||||
// Re-typecheck and coerce expression to default type
|
||||
let expr_res = expression.typecheck(&mut state, Some(res_t));
|
||||
let expr_res = expression.typecheck(&mut state, &hints, Some(res_t));
|
||||
state.ok(expr_res, expression.1);
|
||||
|
||||
res_t
|
||||
@ -152,7 +226,7 @@ impl Block {
|
||||
StmtKind::Set(variable_reference, expression) => {
|
||||
if let Some(var) = state.scope.variables.get(&variable_reference.1).cloned() {
|
||||
// Typecheck expression and coerce to variable type
|
||||
let res = expression.typecheck(&mut state, Some(var.ty));
|
||||
let res = expression.typecheck(&mut state, &hints, Some(var.ty));
|
||||
|
||||
// If expression resolution itself was erronous, resolve as
|
||||
// Unknown.
|
||||
@ -187,7 +261,7 @@ impl Block {
|
||||
}
|
||||
StmtKind::Import(_) => todo!(), // TODO
|
||||
StmtKind::Expression(expression) => {
|
||||
let res = expression.typecheck(&mut state, None);
|
||||
let res = expression.typecheck(&mut state, &hints, None);
|
||||
state.or_else(res, Void, expression.1);
|
||||
if let Ok((kind, _)) = expression.return_type() {
|
||||
Some((kind, expression))
|
||||
@ -207,7 +281,7 @@ impl Block {
|
||||
// block)
|
||||
if let Some((ReturnKind::Hard, expr)) = early_return {
|
||||
let hint = state.scope.return_type_hint;
|
||||
let res = expr.typecheck(&mut state, hint);
|
||||
let res = expr.typecheck(&mut state, &hints, hint);
|
||||
return Ok(state.or_else(res, Vague(Unknown), expr.1));
|
||||
}
|
||||
|
||||
@ -217,7 +291,7 @@ impl Block {
|
||||
ReturnKind::Hard => state.scope.return_type_hint,
|
||||
ReturnKind::Soft => hint_t,
|
||||
};
|
||||
let res = expr.typecheck(&mut state, ret_hint_t);
|
||||
let res = expr.typecheck(&mut state, &hints, ret_hint_t);
|
||||
Ok(state.or_else(res, Vague(Unknown), expr.1))
|
||||
} else {
|
||||
Ok(Void)
|
||||
@ -229,6 +303,7 @@ impl Expression {
|
||||
fn typecheck(
|
||||
&mut self,
|
||||
state: &mut PassState<ErrorKind>,
|
||||
hints: &ScopeHints,
|
||||
hint_t: Option<TypeKind>,
|
||||
) -> Result<TypeKind, ErrorKind> {
|
||||
match &mut self.0 {
|
||||
@ -261,15 +336,15 @@ impl Expression {
|
||||
ExprKind::BinOp(op, lhs, rhs) => {
|
||||
// TODO make sure lhs and rhs can actually do this binary
|
||||
// operation once relevant
|
||||
let lhs_res = lhs.typecheck(state, None);
|
||||
let lhs_res = lhs.typecheck(state, &hints, None);
|
||||
let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.1);
|
||||
let rhs_res = rhs.typecheck(state, Some(lhs_type));
|
||||
let rhs_res = rhs.typecheck(state, &hints, Some(lhs_type));
|
||||
let rhs_type = state.or_else(rhs_res, Vague(Unknown), rhs.1);
|
||||
|
||||
if let Some(collapsed) = state.ok(rhs_type.collapse_into(&rhs_type), self.1) {
|
||||
// Try to coerce both sides again with collapsed type
|
||||
lhs.typecheck(state, Some(collapsed)).ok();
|
||||
rhs.typecheck(state, Some(collapsed)).ok();
|
||||
lhs.typecheck(state, &hints, Some(collapsed)).ok();
|
||||
rhs.typecheck(state, &hints, Some(collapsed)).ok();
|
||||
}
|
||||
|
||||
let res = lhs_type.binop_type(&op, &rhs_type)?;
|
||||
@ -306,7 +381,7 @@ impl Expression {
|
||||
function_call.parameters.iter_mut().zip(true_params_iter)
|
||||
{
|
||||
// Typecheck every param separately
|
||||
let param_res = param.typecheck(state, Some(true_param_t));
|
||||
let param_res = param.typecheck(state, &hints, Some(true_param_t));
|
||||
let param_t = state.or_else(param_res, Vague(Unknown), param.1);
|
||||
state.ok(param_t.collapse_into(&true_param_t), param.1);
|
||||
}
|
||||
@ -323,16 +398,16 @@ impl Expression {
|
||||
}
|
||||
ExprKind::If(IfExpression(cond, lhs, rhs)) => {
|
||||
// TODO make sure cond_res is Boolean here
|
||||
let cond_res = cond.typecheck(state, Some(Bool));
|
||||
let cond_res = cond.typecheck(state, &hints, Some(Bool));
|
||||
let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1);
|
||||
state.ok(cond_t.collapse_into(&Bool), cond.1);
|
||||
|
||||
// Typecheck then/else return types and make sure they are the
|
||||
// same, if else exists.
|
||||
let then_res = lhs.typecheck(state, hint_t);
|
||||
let then_res = lhs.typecheck(state, &hints, hint_t);
|
||||
let then_ret_t = state.or_else(then_res, Vague(Unknown), lhs.meta);
|
||||
let else_ret_t = if let Some(else_block) = rhs {
|
||||
let res = else_block.typecheck(state, hint_t);
|
||||
let res = else_block.typecheck(state, &hints, hint_t);
|
||||
state.or_else(res, Vague(Unknown), else_block.meta)
|
||||
} else {
|
||||
// TODO assert that then_ret_t is Void
|
||||
@ -343,15 +418,15 @@ impl Expression {
|
||||
if let Some(rhs) = rhs {
|
||||
// If rhs existed, typecheck both sides to perform type
|
||||
// coercion.
|
||||
let lhs_res = lhs.typecheck(state, Some(collapsed));
|
||||
let rhs_res = rhs.typecheck(state, Some(collapsed));
|
||||
let lhs_res = lhs.typecheck(state, &hints, Some(collapsed));
|
||||
let rhs_res = rhs.typecheck(state, &hints, Some(collapsed));
|
||||
state.ok(lhs_res, lhs.meta);
|
||||
state.ok(rhs_res, rhs.meta);
|
||||
}
|
||||
|
||||
Ok(collapsed)
|
||||
}
|
||||
ExprKind::Block(block) => block.typecheck(state, hint_t),
|
||||
ExprKind::Block(block) => block.typecheck(state, &hints, hint_t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user