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

View File

@ -41,7 +41,7 @@
//! - Debug Symbols //! - Debug Symbols
//! ``` //! ```
use std::path::PathBuf; use std::{path::PathBuf, thread, time::Duration};
use ast::{ use ast::{
lexer::{self, FullToken, Token}, 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 error_raporting::{ErrorKind as ErrorRapKind, ErrorModules, ReidError};
use mir::{ use mir::{
linker::LinkerPass, linker::LinkerPass,
pass::BinopMap,
typecheck::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs}, typecheck::{typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs},
}; };
use reid_lib::{compile::CompileOutput, Context}; use reid_lib::{compile::CompileOutput, Context};
@ -128,8 +129,22 @@ pub fn perform_all_passes<'map>(
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
dbg!(&context); dbg!(&context);
let mut binops = BinopMap::default();
for module in &mut context.modules { for module in &mut context.modules {
for intrinsic in form_intrinsic_binops() { 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); 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 })?; let state = context.pass(&mut TypeInference { refs: &refs })?;
@ -174,6 +189,8 @@ pub fn perform_all_passes<'map>(
println!("{}", &context); println!("{}", &context);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
dbg!(&state); dbg!(&state);
dbg!("asd!");
thread::sleep(Duration::from_millis(100));
if !state.errors.is_empty() { if !state.errors.is_empty() {
return Err(ReidError::from_kind( 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 /// 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. /// 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> { 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)> { pub fn find(&self, key: &Key) -> Option<(&Key, &T)> {
self.0.iter().find(|(k, _)| *k == key) 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>; pub type BinopMap = Storage<ScopeBinopKey, ScopeBinopDef>;
@ -195,8 +199,12 @@ impl PartialEq for ScopeBinopKey {
if self.operator.is_commutative() != other.operator.is_commutative() { if self.operator.is_commutative() != other.operator.is_commutative() {
return false; 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() { if self.operator.is_commutative() {
operators_eq || swapped_ops_eq operators_eq || swapped_ops_eq
} else { } else {
@ -226,27 +234,11 @@ pub struct ScopeBinopDef {
} }
impl ScopeBinopDef { impl ScopeBinopDef {
pub fn binop_hint_old( pub fn narrow(&self, lhs: &TypeKind, rhs: &TypeKind) -> Option<(TypeKind, TypeKind, TypeKind)> {
&self,
lhs: &TypeKind,
rhs: &TypeKind,
ret_ty: &TypeKind,
) -> Option<(TypeKind, TypeKind)> {
ret_ty.narrow_into(&self.return_ty).ok()?;
let lhs_ty = lhs.narrow_into(&self.hands.0); let lhs_ty = lhs.narrow_into(&self.hands.0);
let rhs_ty = rhs.narrow_into(&self.hands.1); let rhs_ty = rhs.narrow_into(&self.hands.1);
if let (Ok(lhs_ty), Ok(rhs_ty)) = (lhs_ty, rhs_ty) { if let (Ok(lhs_ty), Ok(rhs_ty)) = (lhs_ty, rhs_ty) {
Some((lhs_ty, rhs_ty)) Some((lhs_ty, rhs_ty, self.return_ty.clone()))
} 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())
} else { } else {
None None
} }

View File

@ -453,73 +453,93 @@ impl Expression {
let rhs_res = rhs.typecheck(state, &typerefs, None); let rhs_res = rhs.typecheck(state, &typerefs, None);
let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1); let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1);
let cloned = state.scope.binops.clone(); if let Some(binop) = state
let mut iter = cloned.iter(); .scope
let operator = loop { .binops
let Some((_, binop)) = iter.next() else { .find(&pass::ScopeBinopKey {
break None; params: (lhs_type.clone(), rhs_type.clone()),
}; operator: *op,
if binop.operator != *op { })
continue; .map(|v| (v.1.clone()))
}
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())
} 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 { dbg!(&lhs, &rhs);
return Err(ErrorKind::NegativeUnsignedValue(lhs_type)); 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 {
Err(ErrorKind::InvalidBinop(*op, lhs_type, rhs_type))
} }
if let Some(collapsed) = state.ok(rhs_type.narrow_into(&rhs_type), self.1) { // let cloned = state.scope.binops.clone();
// Try to coerce both sides again with collapsed type // let mut iter = cloned.iter();
lhs.typecheck(state, &typerefs, Some(&collapsed)).ok(); // let operator = loop {
rhs.typecheck(state, &typerefs, Some(&collapsed)).ok(); // 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);
// }
// };
both_t // if let Some(operator) = operator {
.simple_binop_type(op) // // Re-typecheck with found operator hints
.ok_or(ErrorKind::InvalidBinop(*op, lhs_type, rhs_type)) // 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) => { ExprKind::FunctionCall(function_call) => {
let true_function = state let true_function = state
@ -809,6 +829,7 @@ impl Literal {
/// Try to coerce this literal, ie. convert it to a more specific type in /// Try to coerce this literal, ie. convert it to a more specific type in
/// regards to the given hint if any. /// regards to the given hint if any.
fn try_coerce(self, hint: Option<TypeKind>) -> Result<Self, ErrorKind> { fn try_coerce(self, hint: Option<TypeKind>) -> Result<Self, ErrorKind> {
dbg!(&self, &hint);
if let Some(hint) = &hint { if let Some(hint) = &hint {
use Literal as L; use Literal as L;
use VagueLiteral as VagueL; use VagueLiteral as VagueL;

View File

@ -322,37 +322,25 @@ impl Expression {
let mut lhs_ref = lhs.infer_types(state, type_refs)?; let mut lhs_ref = lhs.infer_types(state, type_refs)?;
let mut rhs_ref = rhs.infer_types(state, type_refs)?; let mut rhs_ref = rhs.infer_types(state, type_refs)?;
if let Ok(binop) = type_refs let binops = type_refs.available_binops(op, &mut lhs_ref, &mut rhs_ref);
.binop(op, &mut lhs_ref, &mut rhs_ref, &state.scope.binops)
.ok_or(ErrorKind::TypesIncompatible( 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(), lhs_ref.resolve_deep().unwrap(),
rhs_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) => { ExprKind::FunctionCall(function_call) => {

View File

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