Add ScopeHints tentatively

This commit is contained in:
Sofia 2025-07-12 15:32:29 +03:00
parent 04e0c136df
commit be7fa71b53
2 changed files with 96 additions and 21 deletions

View File

@ -4,7 +4,7 @@ fn indirection() -> bool {
return true; return true;
} }
fn main() -> i32 { fn main() -> u16 {
let mut test = 5; let mut test = 5;
if indirection() { if indirection() {

View File

@ -1,13 +1,13 @@
//! This module contains code relevant to doing a type checking pass on the MIR. //! This module contains code relevant to doing a type checking pass on the MIR.
//! During typechecking relevant types are also coerced if possible. //! 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 crate::{mir::*, util::try_all};
use TypeKind::*; use TypeKind::*;
use VagueType::*; use VagueType::*;
use super::{ use super::{
pass::{Pass, PassState, ScopeFunction, ScopeVariable}, pass::{Pass, PassState, ScopeFunction, ScopeVariable, Storage},
types::ReturnType, types::ReturnType,
}; };
@ -43,6 +43,78 @@ pub enum ErrorKind {
/// MIR. /// MIR.
pub struct TypeCheck; 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 { impl Pass for TypeCheck {
type TError = ErrorKind; type TError = ErrorKind;
@ -76,7 +148,7 @@ impl FunctionDefinition {
let inferred = match &mut self.kind { let inferred = match &mut self.kind {
FunctionDefinitionKind::Local(block, _) => { FunctionDefinitionKind::Local(block, _) => {
state.scope.return_type_hint = Some(self.return_type); 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)), FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
}; };
@ -93,16 +165,18 @@ impl Block {
fn typecheck( fn typecheck(
&mut self, &mut self,
state: &mut PassState<ErrorKind>, state: &mut PassState<ErrorKind>,
hints: &ScopeHints,
hint_t: Option<TypeKind>, hint_t: Option<TypeKind>,
) -> Result<TypeKind, ErrorKind> { ) -> Result<TypeKind, ErrorKind> {
let mut state = state.inner(); let mut state = state.inner();
let hints = hints.inner();
let mut early_return = None; let mut early_return = None;
for statement in &mut self.statements { for statement in &mut self.statements {
let ret = match &mut statement.0 { let ret = match &mut statement.0 {
StmtKind::Let(variable_reference, mutable, expression) => { 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 // If expression resolution itself was erronous, resolve as
// Unknown. // Unknown.
@ -121,7 +195,7 @@ impl Block {
state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2); state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2);
// Re-typecheck and coerce expression to default type // 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); state.ok(expr_res, expression.1);
res_t res_t
@ -152,7 +226,7 @@ impl Block {
StmtKind::Set(variable_reference, expression) => { StmtKind::Set(variable_reference, expression) => {
if let Some(var) = state.scope.variables.get(&variable_reference.1).cloned() { if let Some(var) = state.scope.variables.get(&variable_reference.1).cloned() {
// Typecheck expression and coerce to variable type // 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 // If expression resolution itself was erronous, resolve as
// Unknown. // Unknown.
@ -187,7 +261,7 @@ impl Block {
} }
StmtKind::Import(_) => todo!(), // TODO StmtKind::Import(_) => todo!(), // TODO
StmtKind::Expression(expression) => { 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); state.or_else(res, Void, expression.1);
if let Ok((kind, _)) = expression.return_type() { if let Ok((kind, _)) = expression.return_type() {
Some((kind, expression)) Some((kind, expression))
@ -207,7 +281,7 @@ impl Block {
// block) // block)
if let Some((ReturnKind::Hard, expr)) = early_return { if let Some((ReturnKind::Hard, expr)) = early_return {
let hint = state.scope.return_type_hint; 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)); return Ok(state.or_else(res, Vague(Unknown), expr.1));
} }
@ -217,7 +291,7 @@ impl Block {
ReturnKind::Hard => state.scope.return_type_hint, ReturnKind::Hard => state.scope.return_type_hint,
ReturnKind::Soft => hint_t, 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)) Ok(state.or_else(res, Vague(Unknown), expr.1))
} else { } else {
Ok(Void) Ok(Void)
@ -229,6 +303,7 @@ impl Expression {
fn typecheck( fn typecheck(
&mut self, &mut self,
state: &mut PassState<ErrorKind>, state: &mut PassState<ErrorKind>,
hints: &ScopeHints,
hint_t: Option<TypeKind>, hint_t: Option<TypeKind>,
) -> Result<TypeKind, ErrorKind> { ) -> Result<TypeKind, ErrorKind> {
match &mut self.0 { match &mut self.0 {
@ -261,15 +336,15 @@ impl Expression {
ExprKind::BinOp(op, lhs, rhs) => { ExprKind::BinOp(op, lhs, rhs) => {
// TODO make sure lhs and rhs can actually do this binary // TODO make sure lhs and rhs can actually do this binary
// operation once relevant // 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 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); 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) { if let Some(collapsed) = state.ok(rhs_type.collapse_into(&rhs_type), self.1) {
// Try to coerce both sides again with collapsed type // Try to coerce both sides again with collapsed type
lhs.typecheck(state, Some(collapsed)).ok(); lhs.typecheck(state, &hints, Some(collapsed)).ok();
rhs.typecheck(state, Some(collapsed)).ok(); rhs.typecheck(state, &hints, Some(collapsed)).ok();
} }
let res = lhs_type.binop_type(&op, &rhs_type)?; let res = lhs_type.binop_type(&op, &rhs_type)?;
@ -306,7 +381,7 @@ impl Expression {
function_call.parameters.iter_mut().zip(true_params_iter) function_call.parameters.iter_mut().zip(true_params_iter)
{ {
// Typecheck every param separately // 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); let param_t = state.or_else(param_res, Vague(Unknown), param.1);
state.ok(param_t.collapse_into(&true_param_t), 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)) => { ExprKind::If(IfExpression(cond, lhs, rhs)) => {
// TODO make sure cond_res is Boolean here // 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); let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1);
state.ok(cond_t.collapse_into(&Bool), cond.1); state.ok(cond_t.collapse_into(&Bool), cond.1);
// Typecheck then/else return types and make sure they are the // Typecheck then/else return types and make sure they are the
// same, if else exists. // 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 then_ret_t = state.or_else(then_res, Vague(Unknown), lhs.meta);
let else_ret_t = if let Some(else_block) = rhs { 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) state.or_else(res, Vague(Unknown), else_block.meta)
} else { } else {
// TODO assert that then_ret_t is Void // TODO assert that then_ret_t is Void
@ -343,15 +418,15 @@ impl Expression {
if let Some(rhs) = rhs { if let Some(rhs) = rhs {
// If rhs existed, typecheck both sides to perform type // If rhs existed, typecheck both sides to perform type
// coercion. // coercion.
let lhs_res = lhs.typecheck(state, Some(collapsed)); let lhs_res = lhs.typecheck(state, &hints, Some(collapsed));
let rhs_res = rhs.typecheck(state, Some(collapsed)); let rhs_res = rhs.typecheck(state, &hints, Some(collapsed));
state.ok(lhs_res, lhs.meta); state.ok(lhs_res, lhs.meta);
state.ok(rhs_res, rhs.meta); state.ok(rhs_res, rhs.meta);
} }
Ok(collapsed) Ok(collapsed)
} }
ExprKind::Block(block) => block.typecheck(state, hint_t), ExprKind::Block(block) => block.typecheck(state, &hints, hint_t),
} }
} }
} }