Implement typechecking and inference logic for binops
This commit is contained in:
parent
2cb61de294
commit
7ba3204803
@ -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()))
|
||||
// }
|
||||
// }
|
||||
|
@ -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]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) => {
|
||||
|
@ -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()?)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user