Compare commits
5 Commits
0f424c70d7
...
557d5f9722
Author | SHA1 | Date | |
---|---|---|---|
557d5f9722 | |||
61ab5002e4 | |||
873948a0c4 | |||
be7fa71b53 | |||
04e0c136df |
@ -3,8 +3,6 @@
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use llvm_sys::core::{LLVMBuildAlloca, LLVMBuildLoad2, LLVMBuildStore};
|
||||
|
||||
use crate::{
|
||||
BlockData, ConstValue, FunctionData, Instr, InstructionData, ModuleData, TerminatorKind, Type,
|
||||
util::match_types,
|
||||
|
@ -4,7 +4,7 @@ fn indirection() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
fn main() -> u16 {
|
||||
let mut test = 5;
|
||||
|
||||
if indirection() {
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::{collections::HashMap, mem};
|
||||
|
||||
use llvm_sys::core::LLVMBuildStore;
|
||||
use reid_lib::{
|
||||
builder::InstructionValue, Block, CmpPredicate, ConstValue, Context, Function, Instr, Module,
|
||||
TerminatorKind as Term, Type,
|
||||
|
@ -6,6 +6,7 @@ use crate::token_stream::TokenRange;
|
||||
|
||||
mod display;
|
||||
pub mod pass;
|
||||
mod scopehints;
|
||||
pub mod typecheck;
|
||||
pub mod types;
|
||||
|
||||
@ -66,6 +67,8 @@ pub enum VagueType {
|
||||
Unknown,
|
||||
#[error("Number")]
|
||||
Number,
|
||||
#[error("Hinted({0})")]
|
||||
Hinted(usize),
|
||||
}
|
||||
|
||||
impl TypeKind {
|
||||
@ -157,6 +160,14 @@ impl Literal {
|
||||
}
|
||||
}
|
||||
|
||||
impl VagueLiteral {
|
||||
pub fn as_type(self: &VagueLiteral) -> VagueType {
|
||||
match self {
|
||||
VagueLiteral::Number(_) => VagueType::Number,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BinaryOperator {
|
||||
Add,
|
||||
|
@ -268,10 +268,12 @@ impl Block {
|
||||
impl Statement {
|
||||
fn pass<T: Pass>(&mut self, pass: &mut T, state: &mut State<T::TError>, scope: &mut Scope) {
|
||||
match &mut self.0 {
|
||||
StmtKind::Let(_, mutable, expression) => {
|
||||
StmtKind::Let(_, _, expression) => {
|
||||
expression.pass(pass, state, scope);
|
||||
}
|
||||
StmtKind::Set(_, expression) => {
|
||||
expression.pass(pass, state, scope);
|
||||
}
|
||||
StmtKind::Set(variable_reference, expression) => {} // TODO
|
||||
StmtKind::Import(_) => todo!(),
|
||||
StmtKind::Expression(expression) => {
|
||||
expression.pass(pass, state, scope);
|
||||
@ -281,19 +283,21 @@ impl Statement {
|
||||
pass.stmt(self, PassState::from(state, scope));
|
||||
|
||||
match &mut self.0 {
|
||||
StmtKind::Let(variable_reference, mutable, _) => scope
|
||||
.variables
|
||||
.set(
|
||||
variable_reference.1.clone(),
|
||||
ScopeVariable {
|
||||
ty: variable_reference.0,
|
||||
mutable: *mutable,
|
||||
},
|
||||
)
|
||||
.ok(),
|
||||
StmtKind::Set(variable_reference, expression) => None, // TODO
|
||||
StmtKind::Let(variable_reference, mutable, _) => {
|
||||
scope
|
||||
.variables
|
||||
.set(
|
||||
variable_reference.1.clone(),
|
||||
ScopeVariable {
|
||||
ty: variable_reference.0,
|
||||
mutable: *mutable,
|
||||
},
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
StmtKind::Set(_, _) => {}
|
||||
StmtKind::Import(_) => todo!(),
|
||||
StmtKind::Expression(_) => None,
|
||||
StmtKind::Expression(_) => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
218
reid/src/mir/scopehints.rs
Normal file
218
reid/src/mir/scopehints.rs
Normal file
@ -0,0 +1,218 @@
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use super::{
|
||||
typecheck::{Collapsable, ErrorKind},
|
||||
BinaryOperator, Literal, TypeKind, VagueLiteral, VagueType,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScopeHint<'scope>(TypeIdRef, &'scope ScopeHints<'scope>);
|
||||
|
||||
impl<'scope> ScopeHint<'scope> {
|
||||
pub unsafe fn resolve_type(&self) -> TypeKind {
|
||||
unsafe { *self.1.types.hints.borrow().get_unchecked(*self.0.borrow()) }
|
||||
}
|
||||
|
||||
pub fn narrow(&mut self, ty_ref: &TypeRef) -> Result<ScopeHint<'scope>, ErrorKind> {
|
||||
match ty_ref {
|
||||
TypeRef::Hint(other) => self.1.combine_vars(self, other),
|
||||
TypeRef::Literal(ty) => self.1.narrow_to_type(self, ty),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_type(&self) -> TypeKind {
|
||||
TypeKind::Vague(super::VagueType::Hinted(*self.0.borrow()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'scope> std::fmt::Debug for ScopeHint<'scope> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("Hint")
|
||||
.field(&self.0)
|
||||
.field(unsafe { &self.resolve_type() })
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
type TypeIdRef = Rc<RefCell<usize>>;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TypeHints {
|
||||
/// Simple list of types that variables can refrence
|
||||
hints: RefCell<Vec<TypeKind>>,
|
||||
types: RefCell<Vec<TypeIdRef>>,
|
||||
}
|
||||
|
||||
impl TypeHints {
|
||||
pub fn new(&self, ty: TypeKind) -> TypeIdRef {
|
||||
let idx = self.hints.borrow().len();
|
||||
let typecell = Rc::new(RefCell::new(idx));
|
||||
self.types.borrow_mut().push(typecell.clone());
|
||||
self.hints.borrow_mut().push(ty);
|
||||
typecell
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScopeHints<'outer> {
|
||||
types: &'outer TypeHints,
|
||||
outer: Option<&'outer ScopeHints<'outer>>,
|
||||
/// Mapping of what types variables point to
|
||||
variables: RefCell<HashMap<String, (bool, TypeIdRef)>>,
|
||||
}
|
||||
|
||||
impl<'outer> ScopeHints<'outer> {
|
||||
pub fn from(types: &'outer TypeHints) -> ScopeHints<'outer> {
|
||||
ScopeHints {
|
||||
types,
|
||||
outer: Default::default(),
|
||||
variables: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retrieve_type(&self, mut idx: usize) -> Option<TypeKind> {
|
||||
// Just make sure we have the correct idx
|
||||
let mut inner_idx = self.types.types.borrow().get(idx).map(|i| *i.borrow())?;
|
||||
let mut limit = 50;
|
||||
while inner_idx != idx {
|
||||
idx = inner_idx;
|
||||
inner_idx = self.types.types.borrow().get(idx).map(|i| *i.borrow())?;
|
||||
limit -= 1;
|
||||
if limit < 0 {
|
||||
// Should never happen, but just to avoid infinite loops
|
||||
panic!("Limit reached!");
|
||||
}
|
||||
}
|
||||
self.types.hints.borrow().get(inner_idx).copied()
|
||||
}
|
||||
|
||||
pub fn new_var(
|
||||
&'outer self,
|
||||
name: String,
|
||||
mutable: bool,
|
||||
initial_ty: TypeKind,
|
||||
) -> Result<ScopeHint<'outer>, ErrorKind> {
|
||||
if self.variables.borrow().contains_key(&name) {
|
||||
return Err(ErrorKind::VariableAlreadyDefined(name));
|
||||
}
|
||||
let idx = self.types.new(initial_ty);
|
||||
self.variables
|
||||
.borrow_mut()
|
||||
.insert(name, (mutable, idx.clone()));
|
||||
Ok(ScopeHint(idx, self))
|
||||
}
|
||||
|
||||
fn new_vague(&'outer self, vague: &VagueType) -> ScopeHint<'outer> {
|
||||
let idx = self.types.new(TypeKind::Vague(*vague));
|
||||
ScopeHint(idx, self)
|
||||
}
|
||||
|
||||
fn narrow_to_type(
|
||||
&'outer self,
|
||||
hint: &ScopeHint,
|
||||
ty: &TypeKind,
|
||||
) -> Result<ScopeHint<'outer>, ErrorKind> {
|
||||
unsafe {
|
||||
let mut hints = self.types.hints.borrow_mut();
|
||||
let existing = hints.get_unchecked_mut(*hint.0.borrow());
|
||||
*existing = existing.collapse_into(&ty)?;
|
||||
Ok(ScopeHint(hint.0.clone(), self))
|
||||
}
|
||||
}
|
||||
|
||||
fn combine_vars(
|
||||
&'outer self,
|
||||
hint1: &ScopeHint,
|
||||
hint2: &ScopeHint,
|
||||
) -> Result<ScopeHint<'outer>, ErrorKind> {
|
||||
unsafe {
|
||||
let ty = self
|
||||
.types
|
||||
.hints
|
||||
.borrow()
|
||||
.get_unchecked(*hint2.0.borrow())
|
||||
.clone();
|
||||
self.narrow_to_type(&hint1, &ty)?;
|
||||
for idx in self.types.types.borrow_mut().iter_mut() {
|
||||
if *idx == hint2.0 {
|
||||
*idx.borrow_mut() = *hint1.0.borrow();
|
||||
}
|
||||
}
|
||||
Ok(ScopeHint(hint1.0.clone(), self))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner(&'outer self) -> ScopeHints<'outer> {
|
||||
ScopeHints {
|
||||
types: self.types,
|
||||
outer: Some(self),
|
||||
variables: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_hint(&'outer self, name: &String) -> Option<(bool, ScopeHint<'outer>)> {
|
||||
self.variables
|
||||
.borrow()
|
||||
.get(name)
|
||||
.map(|(mutable, idx)| (*mutable, ScopeHint(idx.clone(), self)))
|
||||
.or(self.outer.map(|o| o.find_hint(name)).flatten())
|
||||
}
|
||||
|
||||
pub fn binop(
|
||||
&'outer self,
|
||||
op: &BinaryOperator,
|
||||
lhs: &mut TypeRef<'outer>,
|
||||
rhs: &mut TypeRef<'outer>,
|
||||
) -> Result<TypeRef<'outer>, ErrorKind> {
|
||||
let ty = lhs.narrow(rhs)?;
|
||||
Ok(match op {
|
||||
BinaryOperator::Add => ty,
|
||||
BinaryOperator::Minus => ty,
|
||||
BinaryOperator::Mult => ty,
|
||||
BinaryOperator::And => TypeRef::Literal(TypeKind::Bool),
|
||||
BinaryOperator::Cmp(_) => TypeRef::Literal(TypeKind::Bool),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TypeRef<'scope> {
|
||||
Hint(ScopeHint<'scope>),
|
||||
Literal(TypeKind),
|
||||
}
|
||||
|
||||
impl<'scope> TypeRef<'scope> {
|
||||
pub fn narrow(&mut self, other: &mut TypeRef<'scope>) -> Result<TypeRef<'scope>, ErrorKind> {
|
||||
match (self, other) {
|
||||
(TypeRef::Hint(hint), unk) | (unk, TypeRef::Hint(hint)) => {
|
||||
Ok(TypeRef::Hint(hint.narrow(unk)?))
|
||||
}
|
||||
(TypeRef::Literal(lit1), TypeRef::Literal(lit2)) => {
|
||||
Ok(TypeRef::Literal(lit1.collapse_into(lit2)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_type(hints: &'scope ScopeHints<'scope>, ty: TypeKind) -> TypeRef<'scope> {
|
||||
match &ty.known() {
|
||||
Ok(ty) => TypeRef::Literal(*ty),
|
||||
Err(vague) => match &vague {
|
||||
super::VagueType::Hinted(idx) => TypeRef::Hint(ScopeHint(
|
||||
unsafe { hints.types.types.borrow().get_unchecked(*idx).clone() },
|
||||
hints,
|
||||
)),
|
||||
_ => TypeRef::Hint(hints.new_vague(vague)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_literal(
|
||||
hints: &'scope ScopeHints<'scope>,
|
||||
lit: Literal,
|
||||
) -> Result<TypeRef<'scope>, ErrorKind> {
|
||||
Ok(match lit {
|
||||
Literal::Vague(vague) => TypeRef::Hint(hints.new_vague(&vague.as_type())),
|
||||
_ => TypeRef::Literal(lit.as_type()),
|
||||
})
|
||||
}
|
||||
}
|
@ -1,14 +1,18 @@
|
||||
//! 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,
|
||||
thread::scope,
|
||||
};
|
||||
|
||||
use crate::{mir::*, util::try_all};
|
||||
use TypeKind::*;
|
||||
use VagueType::*;
|
||||
|
||||
use super::{
|
||||
pass::{Pass, PassState, ScopeFunction, ScopeVariable},
|
||||
types::ReturnType,
|
||||
pass::{Pass, PassState, ScopeFunction, ScopeVariable, Storage},
|
||||
scopehints::{ScopeHints, TypeHints, TypeRef},
|
||||
types::{pick_return, ReturnType},
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone)]
|
||||
@ -76,13 +80,22 @@ 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))
|
||||
|
||||
let types = TypeHints::default();
|
||||
let hints = ScopeHints::from(&types);
|
||||
if let Ok(_) = block.infer_hints(state, &hints) {
|
||||
print!("{}", block);
|
||||
block.typecheck(state, &hints, Some(return_type))
|
||||
} else {
|
||||
Ok(Vague(Unknown))
|
||||
}
|
||||
}
|
||||
FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
|
||||
};
|
||||
|
||||
match inferred {
|
||||
Ok(t) => try_collapse(&return_type, &t)
|
||||
Ok(t) => return_type
|
||||
.collapse_into(&t)
|
||||
.or(Err(ErrorKind::ReturnTypeMismatch(return_type, t))),
|
||||
Err(e) => Ok(state.or_else(Err(e), return_type, self.block_meta())),
|
||||
}
|
||||
@ -90,20 +103,87 @@ impl FunctionDefinition {
|
||||
}
|
||||
|
||||
impl Block {
|
||||
fn infer_hints<'s>(
|
||||
&mut self,
|
||||
state: &mut PassState<ErrorKind>,
|
||||
outer_hints: &'s ScopeHints,
|
||||
) -> Result<(ReturnKind, TypeRef<'s>), ErrorKind> {
|
||||
let mut state = state.inner();
|
||||
let inner_hints = outer_hints.inner();
|
||||
|
||||
for statement in &mut self.statements {
|
||||
match &mut statement.0 {
|
||||
StmtKind::Let(var, mutable, expr) => {
|
||||
let mut var_ref =
|
||||
state.ok(inner_hints.new_var(var.1.clone(), *mutable, var.0), var.2);
|
||||
if let Some(var_ref) = &var_ref {
|
||||
var.0 = var_ref.as_type();
|
||||
}
|
||||
let inferred = expr.infer_hints(&mut state, &inner_hints);
|
||||
let mut expr_ty_ref = state.ok(inferred, expr.1);
|
||||
if let (Some(var_ref), Some(expr_ty_ref)) =
|
||||
(var_ref.as_mut(), expr_ty_ref.as_mut())
|
||||
{
|
||||
state.ok(var_ref.narrow(&expr_ty_ref), var.2 + expr.1);
|
||||
}
|
||||
}
|
||||
StmtKind::Set(var, expr) => {
|
||||
let var_ref = inner_hints.find_hint(&var.1);
|
||||
if let Some((_, var_ref)) = &var_ref {
|
||||
var.0 = var_ref.as_type()
|
||||
}
|
||||
let inferred = expr.infer_hints(&mut state, &inner_hints);
|
||||
let expr_ty_ref = state.ok(inferred, expr.1);
|
||||
if let (Some((_, mut var_ref)), Some(expr_ty_ref)) = (var_ref, expr_ty_ref) {
|
||||
state.ok(var_ref.narrow(&expr_ty_ref), var.2 + expr.1);
|
||||
}
|
||||
}
|
||||
StmtKind::Import(_) => todo!(),
|
||||
StmtKind::Expression(expr) => {
|
||||
let expr_res = expr.infer_hints(&mut state, &inner_hints);
|
||||
state.ok(expr_res, expr.1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(ret_expr) = &mut self.return_expression {
|
||||
let ret_res = ret_expr.1.infer_hints(&mut state, &inner_hints);
|
||||
state.ok(ret_res, ret_expr.1 .1);
|
||||
}
|
||||
|
||||
let (kind, ty) = self.return_type().ok().unwrap_or((ReturnKind::Soft, Void));
|
||||
let mut ret_type_ref = TypeRef::from_type(&outer_hints, ty);
|
||||
|
||||
if kind == ReturnKind::Hard {
|
||||
if let Some(hint) = state.scope.return_type_hint {
|
||||
state.ok(
|
||||
ret_type_ref.narrow(&mut TypeRef::from_type(outer_hints, hint)),
|
||||
self.meta,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok((kind, ret_type_ref))
|
||||
}
|
||||
|
||||
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 {
|
||||
// TODO
|
||||
StmtKind::Let(variable_reference, mutable, expression) => {
|
||||
let res = expression.typecheck(&mut state, Some(variable_reference.0));
|
||||
// Resolve possible hint in var reference
|
||||
let var_t_resolved = variable_reference.0.resolve_hinted(&hints);
|
||||
|
||||
// Typecheck (and coerce) expression with said type
|
||||
let res = expression.typecheck(&mut state, &hints, Some(var_t_resolved));
|
||||
|
||||
// If expression resolution itself was erronous, resolve as
|
||||
// Unknown.
|
||||
@ -111,7 +191,7 @@ impl Block {
|
||||
|
||||
// Make sure the expression and variable type really is the same
|
||||
let res_t = state.or_else(
|
||||
res.collapse_into(&variable_reference.0),
|
||||
res.collapse_into(&var_t_resolved),
|
||||
Vague(Unknown),
|
||||
variable_reference.2 + expression.1,
|
||||
);
|
||||
@ -122,7 +202,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
|
||||
@ -153,7 +233,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.
|
||||
@ -162,7 +242,7 @@ impl Block {
|
||||
// Make sure the expression and variable type to really
|
||||
// be the same
|
||||
let res_t = state.or_else(
|
||||
expr_ty.collapse_into(&variable_reference.0),
|
||||
expr_ty.collapse_into(&variable_reference.0.resolve_hinted(&hints)),
|
||||
Vague(Unknown),
|
||||
variable_reference.2 + expression.1,
|
||||
);
|
||||
@ -188,7 +268,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))
|
||||
@ -208,7 +288,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));
|
||||
}
|
||||
|
||||
@ -218,7 +298,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)
|
||||
@ -227,9 +307,96 @@ impl Block {
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
fn infer_hints<'s>(
|
||||
&mut self,
|
||||
state: &mut PassState<ErrorKind>,
|
||||
hints: &'s ScopeHints<'s>,
|
||||
) -> Result<TypeRef<'s>, ErrorKind> {
|
||||
match &mut self.0 {
|
||||
ExprKind::Variable(var) => {
|
||||
let hint = hints
|
||||
.find_hint(&var.1)
|
||||
.map(|(_, hint)| hint)
|
||||
.ok_or(ErrorKind::VariableNotDefined(var.1.clone()));
|
||||
if let Ok(hint) = &hint {
|
||||
var.0 = hint.as_type()
|
||||
}
|
||||
Ok(TypeRef::Hint(hint?))
|
||||
}
|
||||
ExprKind::Literal(literal) => TypeRef::from_literal(hints, *literal),
|
||||
ExprKind::BinOp(op, lhs, rhs) => {
|
||||
let mut lhs_ref = lhs.infer_hints(state, hints)?;
|
||||
let mut rhs_ref = rhs.infer_hints(state, hints)?;
|
||||
hints.binop(op, &mut lhs_ref, &mut rhs_ref)
|
||||
}
|
||||
ExprKind::FunctionCall(function_call) => {
|
||||
let fn_call = state
|
||||
.scope
|
||||
.function_returns
|
||||
.get(&function_call.name)
|
||||
.ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()))?
|
||||
.clone();
|
||||
|
||||
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_hints(state, hints);
|
||||
if let Some(mut param_ref) = state.ok(expr_res, param_expr.1) {
|
||||
state.ok(
|
||||
param_ref.narrow(&mut TypeRef::from_type(hints, *param_t)),
|
||||
param_expr.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TypeRef::from_type(hints, fn_call.ret))
|
||||
}
|
||||
ExprKind::If(IfExpression(cond, lhs, rhs)) => {
|
||||
let cond_res = cond.infer_hints(state, hints);
|
||||
let cond_hints = state.ok(cond_res, cond.1);
|
||||
|
||||
if let Some(mut cond_hints) = cond_hints {
|
||||
state.ok(cond_hints.narrow(&mut TypeRef::Literal(Bool)), cond.1);
|
||||
}
|
||||
|
||||
let lhs_res = lhs.infer_hints(state, hints);
|
||||
let lhs_hints = state.ok(lhs_res, cond.1);
|
||||
|
||||
if let Some(rhs) = rhs {
|
||||
let rhs_res = rhs.infer_hints(state, hints);
|
||||
let rhs_hints = state.ok(rhs_res, cond.1);
|
||||
|
||||
if let (Some(mut lhs_hints), Some(mut rhs_hints)) = (lhs_hints, rhs_hints) {
|
||||
state.ok(lhs_hints.1.narrow(&mut rhs_hints.1), self.1);
|
||||
Ok(pick_return(lhs_hints, rhs_hints).1)
|
||||
} else {
|
||||
// Failed to retrieve types from either
|
||||
Ok(TypeRef::from_type(hints, Vague(Unknown)))
|
||||
}
|
||||
} else {
|
||||
if let Some((_, type_ref)) = lhs_hints {
|
||||
Ok(type_ref)
|
||||
} else {
|
||||
Ok(TypeRef::from_type(hints, Vague(Unknown)))
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Block(block) => {
|
||||
let block_ref = block.infer_hints(state, hints)?;
|
||||
match block_ref.0 {
|
||||
ReturnKind::Hard => Ok(TypeRef::from_type(hints, Void)),
|
||||
ReturnKind::Soft => Ok(block_ref.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn typecheck(
|
||||
&mut self,
|
||||
state: &mut PassState<ErrorKind>,
|
||||
hints: &ScopeHints,
|
||||
hint_t: Option<TypeKind>,
|
||||
) -> Result<TypeKind, ErrorKind> {
|
||||
match &mut self.0 {
|
||||
@ -248,7 +415,7 @@ impl Expression {
|
||||
|
||||
// Update typing to be more accurate
|
||||
var_ref.0 = state.or_else(
|
||||
var_ref.0.collapse_into(&existing),
|
||||
var_ref.0.resolve_hinted(hints).collapse_into(&existing),
|
||||
Vague(Unknown),
|
||||
var_ref.2,
|
||||
);
|
||||
@ -262,15 +429,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)?;
|
||||
@ -307,14 +474,16 @@ 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);
|
||||
}
|
||||
|
||||
// Make sure function return type is the same as the claimed
|
||||
// return type
|
||||
let ret_t = try_collapse(&f.ret, &function_call.return_type)?;
|
||||
let ret_t = f
|
||||
.ret
|
||||
.collapse_into(&function_call.return_type.resolve_hinted(hints))?;
|
||||
// Update typing to be more accurate
|
||||
function_call.return_type = ret_t;
|
||||
Ok(ret_t)
|
||||
@ -324,16 +493,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
|
||||
@ -344,15 +513,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -411,6 +580,7 @@ impl TypeKind {
|
||||
Vague(vague_type) => match vague_type {
|
||||
Unknown => Err(ErrorKind::TypeIsVague(*vague_type)),
|
||||
Number => Ok(TypeKind::I32),
|
||||
Hinted(_) => panic!("Hinted default!"),
|
||||
},
|
||||
_ => Ok(*self),
|
||||
}
|
||||
@ -426,12 +596,13 @@ impl TypeKind {
|
||||
BinaryOperator::Cmp(_) => Bool,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn try_collapse(lhs: &TypeKind, rhs: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||
lhs.collapse_into(rhs)
|
||||
.or(rhs.collapse_into(lhs))
|
||||
.or(Err(ErrorKind::TypesIncompatible(*lhs, *rhs)))
|
||||
fn resolve_hinted(&self, hints: &ScopeHints) -> TypeKind {
|
||||
match self {
|
||||
Vague(Hinted(idx)) => hints.retrieve_type(*idx).unwrap(),
|
||||
_ => *self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Collapsable: Sized + Clone {
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::mir::typecheck::Collapsable;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ReturnTypeOther {
|
||||
Import(TokenRange),
|
||||
Let(TokenRange),
|
||||
EmptyBlock(TokenRange),
|
||||
NoBlockReturn(TokenRange),
|
||||
Import(Metadata),
|
||||
Let(Metadata),
|
||||
Set(Metadata),
|
||||
EmptyBlock(Metadata),
|
||||
NoBlockReturn(Metadata),
|
||||
}
|
||||
|
||||
pub trait ReturnType {
|
||||
@ -14,9 +17,25 @@ pub trait ReturnType {
|
||||
|
||||
impl ReturnType for Block {
|
||||
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
let mut early_return = None;
|
||||
|
||||
for statement in &self.statements {
|
||||
let ret = statement.return_type();
|
||||
if let Ok((ReturnKind::Hard, _)) = ret {
|
||||
early_return = early_return.or(ret.ok());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO should actually probably prune all instructions after this one
|
||||
// as to not cause problems in codegen later (when unable to delete the
|
||||
// block)
|
||||
if let Some((ReturnKind::Hard, ret_ty)) = early_return {
|
||||
return Ok((ReturnKind::Hard, ret_ty));
|
||||
}
|
||||
|
||||
self.return_expression
|
||||
.as_ref()
|
||||
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta.range))
|
||||
.ok_or(ReturnTypeOther::NoBlockReturn(self.meta))
|
||||
.and_then(|(kind, stmt)| Ok((*kind, stmt.return_type()?.1)))
|
||||
}
|
||||
}
|
||||
@ -25,10 +44,16 @@ impl ReturnType for Statement {
|
||||
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther> {
|
||||
use StmtKind::*;
|
||||
match &self.0 {
|
||||
Expression(e) => e.return_type(),
|
||||
Set(_, _) => todo!(),
|
||||
Import(_) => Err(ReturnTypeOther::Import(self.1.range)),
|
||||
Let(_, _, _) => Err(ReturnTypeOther::Let(self.1.range)),
|
||||
Let(var, _, expr) => if_hard(
|
||||
expr.return_type()?,
|
||||
Err(ReturnTypeOther::Let(var.2 + expr.1)),
|
||||
),
|
||||
Set(var, expr) => if_hard(
|
||||
expr.return_type()?,
|
||||
Err(ReturnTypeOther::Set(var.2 + expr.1)),
|
||||
),
|
||||
Import(_) => todo!(),
|
||||
Expression(expression) => expression.return_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,12 +68,7 @@ impl ReturnType for Expression {
|
||||
let then_r = then_e.return_type()?;
|
||||
let else_r = else_e.return_type()?;
|
||||
|
||||
let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
|
||||
ReturnKind::Hard
|
||||
} else {
|
||||
ReturnKind::Soft
|
||||
};
|
||||
Ok((kind, then_r.1))
|
||||
Ok(pick_return(then_r, else_r))
|
||||
}
|
||||
Block(block) => block.return_type(),
|
||||
FunctionCall(fcall) => fcall.return_type(),
|
||||
@ -62,7 +82,6 @@ impl ReturnType for IfExpression {
|
||||
let then_r = self.1.return_type()?;
|
||||
if let Some(else_b) = &self.2 {
|
||||
let else_r = else_b.return_type()?;
|
||||
dbg!(&then_r, &else_r);
|
||||
|
||||
let kind = if then_r.0 == ReturnKind::Hard && else_r.0 == ReturnKind::Hard {
|
||||
ReturnKind::Hard
|
||||
@ -87,3 +106,24 @@ impl ReturnType for FunctionCall {
|
||||
Ok((ReturnKind::Soft, self.return_type.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn if_hard<TErr>(
|
||||
return_type: (ReturnKind, TypeKind),
|
||||
default: Result<(ReturnKind, TypeKind), TErr>,
|
||||
) -> Result<(ReturnKind, TypeKind), TErr> {
|
||||
if let (ReturnKind::Hard, _) = return_type {
|
||||
Ok(return_type)
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pick_return<T>(lhs: (ReturnKind, T), rhs: (ReturnKind, T)) -> (ReturnKind, T) {
|
||||
use ReturnKind::*;
|
||||
match (lhs.0, rhs.0) {
|
||||
(Hard, Hard) => (Hard, lhs.1),
|
||||
(Hard, Soft) => (Soft, rhs.1),
|
||||
(Soft, Hard) => (Soft, lhs.1),
|
||||
(_, _) => (Soft, lhs.1),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user