Implement typechecking and inference logic for binops

This commit is contained in:
Sofia 2025-07-25 18:10:22 +03:00
parent 2cb61de294
commit 7ba3204803
8 changed files with 337 additions and 207 deletions

View File

@ -1,8 +1,10 @@
use std::marker::PhantomData;
use reid_lib::{builder::InstructionValue, Instr};
use crate::{
codegen::{ErrorKind, StackValueKind},
mir::{BinopDefinition, FunctionDefinition, TypeKind},
mir::{BinaryOperator, BinopDefinition, FunctionDefinition, FunctionDefinitionKind, TypeKind},
};
use super::scope::{Scope, StackValue};
@ -16,6 +18,72 @@ pub fn form_intrinsics() -> Vec<FunctionDefinition> {
pub fn form_intrinsic_binops() -> Vec<BinopDefinition> {
let mut intrinsics = Vec::new();
intrinsics.push(BinopDefinition {
lhs: ("lhs".to_owned(), TypeKind::U32),
op: BinaryOperator::Add,
rhs: ("rhs".to_owned(), TypeKind::U32),
return_type: TypeKind::U32,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(
|scope, lhs, rhs| scope.block.build(Instr::Add(lhs, rhs)).unwrap(),
))),
meta: Default::default(),
});
intrinsics.push(BinopDefinition {
lhs: ("lhs".to_owned(), TypeKind::U16),
op: BinaryOperator::Add,
rhs: ("rhs".to_owned(), TypeKind::U16),
return_type: TypeKind::U16,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(
|scope, lhs, rhs| scope.block.build(Instr::Add(lhs, rhs)).unwrap(),
))),
meta: Default::default(),
});
intrinsics.push(BinopDefinition {
lhs: ("lhs".to_owned(), TypeKind::U32),
op: BinaryOperator::Mult,
rhs: ("rhs".to_owned(), TypeKind::U32),
return_type: TypeKind::U32,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(
|scope, lhs, rhs| scope.block.build(Instr::Mul(lhs, rhs)).unwrap(),
))),
meta: Default::default(),
});
intrinsics.push(BinopDefinition {
lhs: ("lhs".to_owned(), TypeKind::U16),
op: BinaryOperator::Mult,
rhs: ("rhs".to_owned(), TypeKind::U16),
return_type: TypeKind::U16,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(
|scope, lhs, rhs| scope.block.build(Instr::Mul(lhs, rhs)).unwrap(),
))),
meta: Default::default(),
});
intrinsics.push(BinopDefinition {
lhs: ("lhs".to_owned(), TypeKind::U32),
op: BinaryOperator::Minus,
rhs: ("rhs".to_owned(), TypeKind::U32),
return_type: TypeKind::U32,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(
|scope, lhs, rhs| scope.block.build(Instr::Sub(lhs, rhs)).unwrap(),
))),
meta: Default::default(),
});
intrinsics.push(BinopDefinition {
lhs: ("lhs".to_owned(), TypeKind::U16),
op: BinaryOperator::Minus,
rhs: ("rhs".to_owned(), TypeKind::U16),
return_type: TypeKind::U16,
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(
|scope, lhs, rhs| scope.block.build(Instr::Sub(lhs, rhs)).unwrap(),
))),
meta: Default::default(),
});
intrinsics
}
@ -23,56 +91,99 @@ pub trait IntrinsicFunction: std::fmt::Debug {
fn codegen<'ctx, 'a>(
&self,
scope: &mut Scope<'ctx, 'a>,
params: &[InstructionValue],
params: &[&StackValue],
) -> Result<StackValue, ErrorKind>;
}
#[derive(Debug, Clone)]
pub struct IntrinsicIAdd(TypeKind);
#[derive(Clone)]
pub struct IntrinsicSimpleInstr<T>(T)
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
impl IntrinsicFunction for IntrinsicIAdd {
fn codegen<'ctx, 'a>(
&self,
scope: &mut Scope<'ctx, 'a>,
params: &[InstructionValue],
) -> Result<StackValue, ErrorKind> {
let lhs = params.get(0).unwrap();
let rhs = params.get(1).unwrap();
let add = scope.block.build(Instr::Add(*lhs, *rhs)).unwrap();
Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
impl<T> std::fmt::Debug for IntrinsicSimpleInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IntrinsicSimpleInstr").finish()
}
}
#[derive(Debug, Clone)]
pub struct IntrinsicUDiv(TypeKind);
impl IntrinsicFunction for IntrinsicUDiv {
fn codegen<'ctx, 'a>(
impl<T: Clone> IntrinsicFunction for IntrinsicSimpleInstr<T>
where
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
{
fn codegen<'b, 'c>(
&self,
scope: &mut Scope<'ctx, 'a>,
params: &[InstructionValue],
scope: &mut Scope<'b, 'c>,
params: &[&StackValue],
) -> Result<StackValue, ErrorKind> {
let lhs = params.get(0).unwrap();
let rhs = params.get(1).unwrap();
let add = scope.block.build(Instr::UDiv(*lhs, *rhs)).unwrap();
Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
let instr = self.clone().0(scope, lhs.instr(), rhs.instr());
Ok(StackValue(StackValueKind::Literal(instr), lhs.1.clone()))
}
}
#[derive(Debug, Clone)]
pub struct IntrinsicUMod(TypeKind);
// impl IntrinsicFunction for IntrinsicIAdd {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let add = scope.block.build(Instr::Add(*lhs, *rhs)).unwrap();
// Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
// }
// }
impl IntrinsicFunction for IntrinsicUMod {
fn codegen<'ctx, 'a>(
&self,
scope: &mut Scope<'ctx, 'a>,
params: &[InstructionValue],
) -> Result<StackValue, ErrorKind> {
let lhs = params.get(0).unwrap();
let rhs = params.get(1).unwrap();
let div = scope.block.build(Instr::UDiv(*lhs, *rhs)).unwrap();
let mul = scope.block.build(Instr::Mul(*rhs, div)).unwrap();
let sub = scope.block.build(Instr::Sub(*lhs, mul)).unwrap();
Ok(StackValue(StackValueKind::Literal(sub), self.0.clone()))
}
}
// #[derive(Debug, Clone)]
// pub struct IntrinsicIAdd(TypeKind);
// impl IntrinsicFunction for IntrinsicIAdd {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let add = scope.block.build(Instr::Add(*lhs, *rhs)).unwrap();
// Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
// }
// }
// #[derive(Debug, Clone)]
// pub struct IntrinsicUDiv(TypeKind);
// impl IntrinsicFunction for IntrinsicUDiv {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let add = scope.block.build(Instr::UDiv(*lhs, *rhs)).unwrap();
// Ok(StackValue(StackValueKind::Literal(add), self.0.clone()))
// }
// }
// #[derive(Debug, Clone)]
// pub struct IntrinsicUMod(TypeKind);
// impl IntrinsicFunction for IntrinsicUMod {
// fn codegen<'ctx, 'a>(
// &self,
// scope: &mut Scope<'ctx, 'a>,
// params: &[InstructionValue],
// ) -> Result<StackValue, ErrorKind> {
// let lhs = params.get(0).unwrap();
// let rhs = params.get(1).unwrap();
// let div = scope.block.build(Instr::UDiv(*lhs, *rhs)).unwrap();
// let mul = scope.block.build(Instr::Mul(*rhs, div)).unwrap();
// let sub = scope.block.build(Instr::Sub(*lhs, mul)).unwrap();
// Ok(StackValue(StackValueKind::Literal(sub), self.0.clone()))
// }
// }

View File

@ -160,9 +160,7 @@ impl<'ctx> StackBinopDefinition<'ctx> {
self.return_ty.clone(),
))
}
StackBinopFunctionKind::Intrinsic(fun) => {
fun.codegen(scope, &[lhs.instr(), rhs.instr()])
}
StackBinopFunctionKind::Intrinsic(fun) => fun.codegen(scope, &[&lhs, &rhs]),
}
}
}

View File

@ -41,7 +41,7 @@
//! - Debug Symbols
//! ```
use std::path::PathBuf;
use std::{path::PathBuf, thread, time::Duration};
use ast::{
lexer::{self, FullToken, Token},
@ -51,6 +51,7 @@ use codegen::intrinsics::{form_intrinsic_binops, form_intrinsics};
use error_raporting::{ErrorKind as ErrorRapKind, ErrorModules, ReidError};
use mir::{
linker::LinkerPass,
pass::BinopMap,
typecheck::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs},
};
use reid_lib::{compile::CompileOutput, Context};
@ -128,8 +129,22 @@ pub fn perform_all_passes<'map>(
#[cfg(debug_assertions)]
dbg!(&context);
let mut binops = BinopMap::default();
for module in &mut context.modules {
for intrinsic in form_intrinsic_binops() {
binops
.set(
mir::pass::ScopeBinopKey {
params: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()),
operator: intrinsic.op,
},
mir::pass::ScopeBinopDef {
hands: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()),
operator: intrinsic.op,
return_ty: intrinsic.return_type.clone(),
},
)
.ok();
module.1.binop_defs.insert(0, intrinsic);
}
}
@ -162,7 +177,7 @@ pub fn perform_all_passes<'map>(
));
}
let refs = TypeRefs::default();
let refs = TypeRefs::with_binops(binops);
let state = context.pass(&mut TypeInference { refs: &refs })?;
@ -174,6 +189,8 @@ pub fn perform_all_passes<'map>(
println!("{}", &context);
#[cfg(debug_assertions)]
dbg!(&state);
dbg!("asd!");
thread::sleep(Duration::from_millis(100));
if !state.errors.is_empty() {
return Err(ReidError::from_kind(

View File

@ -43,20 +43,6 @@ impl TypeKind {
})
}
pub fn binop_type(
lhs: &TypeKind,
rhs: &TypeKind,
binop: &ScopeBinopDef,
) -> Option<(TypeKind, TypeKind, TypeKind)> {
let lhs_ty = lhs.narrow_into(&binop.hands.0);
let rhs_ty = rhs.narrow_into(&binop.hands.1);
if let (Ok(lhs_ty), Ok(rhs_ty)) = (lhs_ty, rhs_ty) {
Some((lhs_ty, rhs_ty, binop.return_ty.clone()))
} else {
None
}
}
/// Reverse of binop_type, where the given hint is the known required output
/// type of the binop, and the output is the hint for the lhs/rhs type.
pub fn simple_binop_hint(&self, op: &BinaryOperator) -> Option<TypeKind> {

View File

@ -119,6 +119,10 @@ impl<Key: std::hash::Hash + Eq, T: Clone + std::fmt::Debug> Storage<Key, T> {
pub fn find(&self, key: &Key) -> Option<(&Key, &T)> {
self.0.iter().find(|(k, _)| *k == key)
}
pub fn filter(&self, key: &Key) -> Vec<(&Key, &T)> {
self.0.iter().filter(|(k, _)| *k == key).collect()
}
}
pub type BinopMap = Storage<ScopeBinopKey, ScopeBinopDef>;
@ -195,8 +199,12 @@ impl PartialEq for ScopeBinopKey {
if self.operator.is_commutative() != other.operator.is_commutative() {
return false;
}
let operators_eq = self.params == other.params;
let swapped_ops_eq = (self.params.1.clone(), self.params.0.clone()) == other.params;
let operators_eq = self.params.0.narrow_into(&other.params.0).is_ok()
&& self.params.1.narrow_into(&other.params.1).is_ok();
let swapped_ops_eq = self.params.0.narrow_into(&other.params.1).is_ok()
&& self.params.1.narrow_into(&other.params.0).is_ok();
if self.operator.is_commutative() {
operators_eq || swapped_ops_eq
} else {
@ -226,27 +234,11 @@ pub struct ScopeBinopDef {
}
impl ScopeBinopDef {
pub fn binop_hint_old(
&self,
lhs: &TypeKind,
rhs: &TypeKind,
ret_ty: &TypeKind,
) -> Option<(TypeKind, TypeKind)> {
ret_ty.narrow_into(&self.return_ty).ok()?;
pub fn narrow(&self, lhs: &TypeKind, rhs: &TypeKind) -> Option<(TypeKind, TypeKind, TypeKind)> {
let lhs_ty = lhs.narrow_into(&self.hands.0);
let rhs_ty = rhs.narrow_into(&self.hands.1);
if let (Ok(lhs_ty), Ok(rhs_ty)) = (lhs_ty, rhs_ty) {
Some((lhs_ty, rhs_ty))
} else {
None
}
}
pub fn binop_ret_ty(&self, lhs: &TypeKind, rhs: &TypeKind) -> Option<TypeKind> {
let lhs_ty = lhs.narrow_into(&self.hands.0);
let rhs_ty = rhs.narrow_into(&self.hands.1);
if let (Ok(_), Ok(_)) = (lhs_ty, rhs_ty) {
Some(self.return_ty.clone())
Some((lhs_ty, rhs_ty, self.return_ty.clone()))
} else {
None
}

View File

@ -453,73 +453,93 @@ impl Expression {
let rhs_res = rhs.typecheck(state, &typerefs, None);
let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1);
let cloned = state.scope.binops.clone();
let mut iter = cloned.iter();
let operator = loop {
let Some((_, binop)) = iter.next() else {
break None;
};
if binop.operator != *op {
continue;
}
if let Some(hint_t) = hint_t {
if binop.return_ty == *hint_t {
if let Some(_) = TypeKind::binop_type(&lhs_type, &rhs_type, binop) {
break Some(binop);
}
} else {
continue;
}
}
if let Some(_) = TypeKind::binop_type(&lhs_type, &rhs_type, binop) {
break Some(binop);
}
};
if let Some(operator) = operator {
// Re-typecheck with found operator hints
let (lhs_ty, rhs_ty) = TypeKind::try_collapse_two(
(&lhs_type, &rhs_type),
(&operator.hands.0, &operator.hands.1),
)
.unwrap();
let lhs_res = lhs.typecheck(state, &typerefs, Some(&lhs_ty));
let rhs_res = rhs.typecheck(state, &typerefs, Some(&rhs_ty));
state.or_else(lhs_res, TypeKind::Vague(Vague::Unknown), lhs.1);
state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1);
Ok(operator.return_ty.clone())
if let Some(binop) = state
.scope
.binops
.find(&pass::ScopeBinopKey {
params: (lhs_type.clone(), rhs_type.clone()),
operator: *op,
})
.map(|v| (v.1.clone()))
{
dbg!(&lhs, &rhs);
dbg!(&lhs_type.resolve_ref(typerefs));
dbg!(&rhs_type.resolve_ref(typerefs));
lhs.typecheck(state, &typerefs, Some(&binop.hands.0))?;
rhs.typecheck(state, &typerefs, Some(&binop.hands.1))?;
Ok(binop.narrow(&lhs_type, &rhs_type).unwrap().2)
} else {
// Re-typecheck with typical everyday binop
let lhs_res = lhs.typecheck(
state,
&typerefs,
hint_t.and_then(|t| t.simple_binop_hint(op)).as_ref(),
);
let lhs_type = state.or_else(lhs_res, TypeKind::Vague(Vague::Unknown), lhs.1);
let rhs_res = rhs.typecheck(state, &typerefs, Some(&lhs_type));
let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1);
let both_t = lhs_type.narrow_into(&rhs_type)?;
if *op == BinaryOperator::Minus && !lhs_type.signed() {
if let (Some(lhs_val), Some(rhs_val)) = (lhs.num_value()?, rhs.num_value()?)
{
if lhs_val < rhs_val {
return Err(ErrorKind::NegativeUnsignedValue(lhs_type));
}
}
}
if let Some(collapsed) = state.ok(rhs_type.narrow_into(&rhs_type), self.1) {
// Try to coerce both sides again with collapsed type
lhs.typecheck(state, &typerefs, Some(&collapsed)).ok();
rhs.typecheck(state, &typerefs, Some(&collapsed)).ok();
}
both_t
.simple_binop_type(op)
.ok_or(ErrorKind::InvalidBinop(*op, lhs_type, rhs_type))
Err(ErrorKind::InvalidBinop(*op, lhs_type, rhs_type))
}
// let cloned = state.scope.binops.clone();
// let mut iter = cloned.iter();
// let operator = loop {
// let Some((_, binop)) = iter.next() else {
// break None;
// };
// if binop.operator != *op {
// continue;
// }
// if let Some(hint_t) = hint_t {
// if binop.return_ty == *hint_t {
// if let Some(_) = TypeKind::narrow_to_binop(&lhs_type, &rhs_type, binop)
// {
// break Some(binop);
// }
// } else {
// continue;
// }
// }
// if let Some(_) = TypeKind::narrow_to_binop(&lhs_type, &rhs_type, binop) {
// break Some(binop);
// }
// };
// if let Some(operator) = operator {
// // Re-typecheck with found operator hints
// let (lhs_ty, rhs_ty) = TypeKind::try_collapse_two(
// (&lhs_type, &rhs_type),
// (&operator.hands.0, &operator.hands.1),
// )
// .unwrap();
// let lhs_res = lhs.typecheck(state, &typerefs, Some(&lhs_ty));
// let rhs_res = rhs.typecheck(state, &typerefs, Some(&rhs_ty));
// state.or_else(lhs_res, TypeKind::Vague(Vague::Unknown), lhs.1);
// state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1);
// Ok(operator.return_ty.clone())
// } else {
// // Re-typecheck with typical everyday binop
// let lhs_res = lhs.typecheck(
// state,
// &typerefs,
// hint_t.and_then(|t| t.simple_binop_hint(op)).as_ref(),
// );
// let lhs_type = state.or_else(lhs_res, TypeKind::Vague(Vague::Unknown), lhs.1);
// let rhs_res = rhs.typecheck(state, &typerefs, Some(&lhs_type));
// let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1);
// let both_t = lhs_type.narrow_into(&rhs_type)?;
// if *op == BinaryOperator::Minus && !lhs_type.signed() {
// if let (Some(lhs_val), Some(rhs_val)) = (lhs.num_value()?, rhs.num_value()?)
// {
// if lhs_val < rhs_val {
// return Err(ErrorKind::NegativeUnsignedValue(lhs_type));
// }
// }
// }
// if let Some(collapsed) = state.ok(rhs_type.narrow_into(&rhs_type), self.1) {
// // Try to coerce both sides again with collapsed type
// lhs.typecheck(state, &typerefs, Some(&collapsed)).ok();
// rhs.typecheck(state, &typerefs, Some(&collapsed)).ok();
// }
// both_t
// .simple_binop_type(op)
// .ok_or(ErrorKind::InvalidBinop(*op, lhs_type, rhs_type))
// }
}
ExprKind::FunctionCall(function_call) => {
let true_function = state
@ -809,6 +829,7 @@ impl Literal {
/// Try to coerce this literal, ie. convert it to a more specific type in
/// regards to the given hint if any.
fn try_coerce(self, hint: Option<TypeKind>) -> Result<Self, ErrorKind> {
dbg!(&self, &hint);
if let Some(hint) = &hint {
use Literal as L;
use VagueLiteral as VagueL;

View File

@ -322,37 +322,25 @@ impl Expression {
let mut lhs_ref = lhs.infer_types(state, type_refs)?;
let mut rhs_ref = rhs.infer_types(state, type_refs)?;
if let Ok(binop) = type_refs
.binop(op, &mut lhs_ref, &mut rhs_ref, &state.scope.binops)
.ok_or(ErrorKind::TypesIncompatible(
let binops = type_refs.available_binops(op, &mut lhs_ref, &mut rhs_ref);
if binops.len() > 0 {
let binop = unsafe { binops.get_unchecked(0) };
let mut widened_lhs = binop.hands.0.clone();
let mut widened_rhs = binop.hands.1.clone();
for binop in binops.iter().skip(1) {
widened_lhs = widened_lhs.widen_into(&binop.hands.0);
widened_rhs = widened_rhs.widen_into(&binop.hands.1);
}
lhs_ref.narrow(&type_refs.from_type(&widened_lhs).unwrap());
rhs_ref.narrow(&type_refs.from_type(&widened_rhs).unwrap());
Ok(type_refs.from_binop(*op, &lhs_ref, &rhs_ref))
} else {
Err(ErrorKind::InvalidBinop(
*op,
lhs_ref.resolve_deep().unwrap(),
rhs_ref.resolve_deep().unwrap(),
))
{
Ok(binop)
} else {
let typeref = state.or_else(
lhs_ref.narrow(&rhs_ref).ok_or(ErrorKind::InvalidBinop(
*op,
lhs_ref.resolve_deep().unwrap(),
rhs_ref.resolve_deep().unwrap(),
)),
type_refs.from_type(&Vague(Unknown)).unwrap(),
self.1,
);
Ok(type_refs
.from_type(
&typeref
.resolve_deep()
.unwrap()
.simple_binop_type(op)
.ok_or(ErrorKind::InvalidBinop(
*op,
lhs_ref.resolve_deep().unwrap(),
rhs_ref.resolve_deep().unwrap(),
))?,
)
.unwrap())
}
}
ExprKind::FunctionCall(function_call) => {

View File

@ -4,7 +4,10 @@ use std::{
rc::Rc,
};
use crate::mir::{pass::BinopMap, BinaryOperator, TypeKind, VagueType};
use crate::{
ast::BinopDefinition,
mir::{pass::BinopMap, BinaryOperator, TypeKind, VagueType},
};
use super::{
super::pass::{ScopeBinopDef, ScopeBinopKey, Storage},
@ -64,33 +67,33 @@ pub enum TypeRefKind {
}
impl TypeRefKind {
pub fn widen(&self, types: &TypeRefs) -> Option<TypeKind> {
pub fn widen(&self, types: &TypeRefs) -> TypeKind {
match self {
TypeRefKind::BinOp(op, lhs, rhs) => {
let mut binops = types
.binop_types
.iter()
.filter(|b| b.1.operator == *op)
.map(|b| b.1.binop_ret_ty(&lhs, &rhs))
.map(|b| b.1.narrow(&lhs, &rhs).map(|b| b.2))
.filter_map(|s| s);
if let Some(mut ty) = binops.next() {
while let Some(other) = binops.next() {
ty = ty.widen_into(&other);
}
Some(ty)
ty
} else {
None
TypeKind::Vague(VagueType::Unknown)
}
}
TypeRefKind::Direct(ty) => match ty {
TypeKind::Vague(VagueType::TypeRef(id)) => types.retrieve_wide_type(*id),
_ => Some(ty.clone()),
TypeKind::Vague(VagueType::TypeRef(id)) => types.retrieve_wide_type(*id).unwrap(),
_ => ty.clone(),
},
}
}
}
#[derive(Debug, Default)]
#[derive(Default, Debug)]
pub struct TypeRefs {
/// Simple list of types that variables can refrence
pub(super) hints: RefCell<Vec<TypeRefKind>>,
@ -117,6 +120,14 @@ impl std::fmt::Display for TypeRefs {
}
impl TypeRefs {
pub fn with_binops(binops: BinopMap) -> TypeRefs {
TypeRefs {
hints: Default::default(),
type_refs: Default::default(),
binop_types: binops,
}
}
pub fn new(&self, ty: &TypeKind) -> TypeIdRef {
let idx = self.hints.borrow().len();
let typecell = Rc::new(RefCell::new(idx));
@ -127,6 +138,16 @@ impl TypeRefs {
typecell
}
pub fn binop(&self, op: BinaryOperator, lhs: &TypeRef, rhs: &TypeRef) -> TypeIdRef {
let idx = self.hints.borrow().len();
let typecell = Rc::new(RefCell::new(idx));
self.type_refs.borrow_mut().push(typecell.clone());
self.hints
.borrow_mut()
.push(TypeRefKind::BinOp(op, lhs.as_type(), rhs.as_type()));
typecell
}
pub fn find(&self, ty: &TypeKind) -> Option<TypeIdRef> {
if ty.known().is_err() {
// Only do this for non-vague types that can not be further narrowed
@ -167,7 +188,6 @@ impl TypeRefs {
.get(inner_idx)
.cloned()
.map(|t| t.widen(self))
.flatten()
}
}
@ -232,6 +252,15 @@ impl<'outer> ScopeTypeRefs<'outer> {
Some(TypeRef(idx, self))
}
pub fn from_binop(
&'outer self,
op: BinaryOperator,
lhs: &TypeRef,
rhs: &TypeRef,
) -> TypeRef<'outer> {
TypeRef(self.types.binop(op, lhs, rhs), self)
}
fn narrow_to_type(&'outer self, hint: &TypeRef, ty: &TypeKind) -> Option<TypeRef<'outer>> {
unsafe {
let mut hints = self.types.hints.borrow_mut();
@ -269,8 +298,7 @@ impl<'outer> ScopeTypeRefs<'outer> {
.borrow()
.get_unchecked(*hint2.0.borrow())
.clone()
.widen(self.types)
.unwrap();
.widen(self.types);
self.narrow_to_type(&hint1, &ty)?;
for idx in self.types.type_refs.borrow_mut().iter_mut() {
if *idx == hint2.0 && idx != &hint1.0 {
@ -297,47 +325,36 @@ impl<'outer> ScopeTypeRefs<'outer> {
.or(self.outer.map(|o| o.find_var(name)).flatten())
}
pub fn binop(
pub fn available_binops(
&'outer self,
op: &BinaryOperator,
lhs: &mut TypeRef<'outer>,
rhs: &mut TypeRef<'outer>,
binops: &Storage<ScopeBinopKey, ScopeBinopDef>,
) -> Option<TypeRef<'outer>> {
if lhs.resolve_deep().unwrap().known().is_err()
&& rhs.resolve_deep().unwrap().known().is_err()
{
return self.from_type(&TypeKind::Vague(VagueType::Unknown));
}
let mut iter = binops.iter();
loop {
let Some((_, binop)) = iter.next() else {
break None;
};
) -> Vec<ScopeBinopDef> {
let mut applying_binops = Vec::new();
for (_, binop) in self.types.binop_types.iter() {
if binop.operator != *op {
continue;
}
if let Some(ret) = try_binop(lhs, rhs, binop) {
break Some(ret);
if let Some(_) = check_binop(lhs, rhs, binop) {
applying_binops.push(binop.clone());
continue;
}
if binop.operator.is_commutative() {
if let Some(ret) = try_binop(rhs, lhs, binop) {
return Some(ret);
if let Some(_) = check_binop(lhs, rhs, binop) {
applying_binops.push(binop.clone());
continue;
}
}
}
applying_binops
}
}
fn try_binop<'o>(
fn check_binop<'o>(
lhs: &mut TypeRef<'o>,
rhs: &mut TypeRef<'o>,
binop: &ScopeBinopDef,
) -> Option<TypeRef<'o>> {
let (lhs_ty, rhs_ty, ret_ty) =
TypeKind::binop_type(&lhs.resolve_deep()?, &rhs.resolve_deep()?, binop)?;
lhs.narrow(&lhs.1.from_type(&lhs_ty).unwrap()).unwrap();
rhs.narrow(&rhs.1.from_type(&rhs_ty).unwrap()).unwrap();
lhs.1.from_type(&ret_ty)
) -> Option<(TypeKind, TypeKind, TypeKind)> {
binop.narrow(&lhs.resolve_deep()?, &rhs.resolve_deep()?)
}