Implement a bunch of intrinsic binops
This commit is contained in:
parent
7ba3204803
commit
63f48f7df9
@ -0,0 +1 @@
|
||||
max_width = 120
|
@ -1,98 +1,161 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use reid_lib::{builder::InstructionValue, Instr};
|
||||
use reid_lib::{builder::InstructionValue, CmpPredicate, Instr};
|
||||
|
||||
use crate::{
|
||||
codegen::{ErrorKind, StackValueKind},
|
||||
mir::{BinaryOperator, BinopDefinition, FunctionDefinition, FunctionDefinitionKind, TypeKind},
|
||||
mir::{BinaryOperator, BinopDefinition, CmpOperator, FunctionDefinition, FunctionDefinitionKind, TypeKind},
|
||||
};
|
||||
|
||||
use super::scope::{Scope, StackValue};
|
||||
|
||||
const INTEGERS: [TypeKind; 10] = [
|
||||
TypeKind::U8,
|
||||
TypeKind::U16,
|
||||
TypeKind::U32,
|
||||
TypeKind::U64,
|
||||
TypeKind::U128,
|
||||
TypeKind::I8,
|
||||
TypeKind::I16,
|
||||
TypeKind::I32,
|
||||
TypeKind::I64,
|
||||
TypeKind::I128,
|
||||
];
|
||||
|
||||
const FLOATS: [TypeKind; 7] = [
|
||||
TypeKind::F16,
|
||||
TypeKind::F32,
|
||||
TypeKind::F32B,
|
||||
TypeKind::F64,
|
||||
TypeKind::F80,
|
||||
TypeKind::F128,
|
||||
TypeKind::F128PPC,
|
||||
];
|
||||
|
||||
pub fn form_intrinsics() -> Vec<FunctionDefinition> {
|
||||
let intrinsics = Vec::new();
|
||||
|
||||
intrinsics
|
||||
}
|
||||
|
||||
fn simple_binop_def<T: Clone + 'static>(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition
|
||||
where
|
||||
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
|
||||
{
|
||||
BinopDefinition {
|
||||
lhs: ("lhs".to_owned(), ty.clone()),
|
||||
op,
|
||||
rhs: ("rhs".to_owned(), ty.clone()),
|
||||
return_type: ty.clone(),
|
||||
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicSimpleInstr(fun))),
|
||||
meta: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn boolean_binop_def<T: Clone + 'static>(op: BinaryOperator, ty: &TypeKind, fun: T) -> BinopDefinition
|
||||
where
|
||||
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
|
||||
{
|
||||
BinopDefinition {
|
||||
lhs: ("lhs".to_owned(), ty.clone()),
|
||||
op,
|
||||
rhs: ("rhs".to_owned(), ty.clone()),
|
||||
return_type: TypeKind::Bool,
|
||||
fn_kind: FunctionDefinitionKind::Intrinsic(Box::new(IntrinsicBooleanInstr(fun))),
|
||||
meta: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
});
|
||||
use BinaryOperator::*;
|
||||
|
||||
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(),
|
||||
});
|
||||
for ty in INTEGERS {
|
||||
intrinsics.push(simple_binop_def(Add, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::Add(lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(simple_binop_def(Mult, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::Mul(lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(simple_binop_def(Minus, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::Sub(lhs, rhs)).unwrap()
|
||||
}));
|
||||
if ty.signed() {
|
||||
intrinsics.push(simple_binop_def(Div, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::SDiv(lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(simple_binop_def(Mod, &ty, |scope, lhs, rhs| {
|
||||
let div = scope.block.build(Instr::SDiv(lhs, rhs)).unwrap();
|
||||
let mul = scope.block.build(Instr::Mul(rhs, div)).unwrap();
|
||||
scope.block.build(Instr::Sub(lhs, mul)).unwrap()
|
||||
}));
|
||||
} else {
|
||||
intrinsics.push(simple_binop_def(Div, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::UDiv(lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(simple_binop_def(Mod, &ty, |scope, lhs, rhs| {
|
||||
let div = scope.block.build(Instr::UDiv(lhs, rhs)).unwrap();
|
||||
let mul = scope.block.build(Instr::Mul(rhs, div)).unwrap();
|
||||
scope.block.build(Instr::Sub(lhs, mul)).unwrap()
|
||||
}));
|
||||
}
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GT), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::ICmp(CmpPredicate::GT, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GE), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::ICmp(CmpPredicate::GE, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LT), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::ICmp(CmpPredicate::LT, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LE), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::ICmp(CmpPredicate::LE, lhs, rhs)).unwrap()
|
||||
}));
|
||||
}
|
||||
for ty in INTEGERS.iter().chain(&[TypeKind::Bool, TypeKind::Char]) {
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::EQ), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::ICmp(CmpPredicate::EQ, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::NE), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::ICmp(CmpPredicate::NE, lhs, rhs)).unwrap()
|
||||
}));
|
||||
}
|
||||
for ty in FLOATS {
|
||||
intrinsics.push(simple_binop_def(BinaryOperator::Add, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FAdd(lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(simple_binop_def(BinaryOperator::Mult, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FMul(lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(simple_binop_def(BinaryOperator::Minus, &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FSub(lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::EQ), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FCmp(CmpPredicate::EQ, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::NE), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FCmp(CmpPredicate::NE, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GT), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FCmp(CmpPredicate::GT, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::GE), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FCmp(CmpPredicate::GE, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LT), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FCmp(CmpPredicate::LT, lhs, rhs)).unwrap()
|
||||
}));
|
||||
intrinsics.push(boolean_binop_def(Cmp(CmpOperator::LE), &ty, |scope, lhs, rhs| {
|
||||
scope.block.build(Instr::FCmp(CmpPredicate::LE, lhs, rhs)).unwrap()
|
||||
}));
|
||||
}
|
||||
|
||||
intrinsics
|
||||
}
|
||||
|
||||
pub trait IntrinsicFunction: std::fmt::Debug {
|
||||
fn codegen<'ctx, 'a>(
|
||||
&self,
|
||||
scope: &mut Scope<'ctx, 'a>,
|
||||
params: &[&StackValue],
|
||||
) -> Result<StackValue, ErrorKind>;
|
||||
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, params: &[&StackValue]) -> Result<StackValue, ErrorKind>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -113,11 +176,7 @@ impl<T: Clone> IntrinsicFunction for IntrinsicSimpleInstr<T>
|
||||
where
|
||||
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
|
||||
{
|
||||
fn codegen<'b, 'c>(
|
||||
&self,
|
||||
scope: &mut Scope<'b, 'c>,
|
||||
params: &[&StackValue],
|
||||
) -> Result<StackValue, ErrorKind> {
|
||||
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[&StackValue]) -> Result<StackValue, ErrorKind> {
|
||||
let lhs = params.get(0).unwrap();
|
||||
let rhs = params.get(1).unwrap();
|
||||
let instr = self.clone().0(scope, lhs.instr(), rhs.instr());
|
||||
@ -125,6 +184,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IntrinsicBooleanInstr<T>(T)
|
||||
where
|
||||
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue;
|
||||
|
||||
impl<T> std::fmt::Debug for IntrinsicBooleanInstr<T>
|
||||
where
|
||||
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("IntrinsicBooleanInstr").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> IntrinsicFunction for IntrinsicBooleanInstr<T>
|
||||
where
|
||||
T: FnOnce(&mut Scope, InstructionValue, InstructionValue) -> InstructionValue,
|
||||
{
|
||||
fn codegen<'b, 'c>(&self, scope: &mut Scope<'b, 'c>, params: &[&StackValue]) -> Result<StackValue, ErrorKind> {
|
||||
let lhs = params.get(0).unwrap();
|
||||
let rhs = params.get(1).unwrap();
|
||||
let instr = self.clone().0(scope, lhs.instr(), rhs.instr());
|
||||
Ok(StackValue(StackValueKind::Literal(instr), TypeKind::Bool))
|
||||
}
|
||||
}
|
||||
|
||||
// impl IntrinsicFunction for IntrinsicIAdd {
|
||||
// fn codegen<'ctx, 'a>(
|
||||
// &self,
|
||||
|
@ -98,11 +98,7 @@ pub fn compile_module<'map>(
|
||||
let mut statements = Vec::new();
|
||||
|
||||
while !matches!(token_stream.peek().unwrap_or(Token::Eof), Token::Eof) {
|
||||
let statement = ReidError::from_parser(
|
||||
token_stream.parse::<TopLevelStatement>(),
|
||||
map.clone(),
|
||||
module_id,
|
||||
)?;
|
||||
let statement = ReidError::from_parser(token_stream.parse::<TopLevelStatement>(), map.clone(), module_id)?;
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
@ -189,8 +185,6 @@ 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(
|
||||
|
@ -56,10 +56,7 @@ impl<'t> Pass for TypeCheck<'t> {
|
||||
}
|
||||
|
||||
if let Some(_) = defmap.insert(&typedef.name, typedef) {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::DuplicateTypeName(name.clone())),
|
||||
meta.clone(),
|
||||
);
|
||||
state.ok::<_, Infallible>(Err(ErrorKind::DuplicateTypeName(name.clone())), meta.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,10 +91,7 @@ fn check_typedefs_for_recursion<'a, 'b>(
|
||||
if let TypeKind::CustomType(CustomTypeKey(name, _)) = field_ty {
|
||||
if seen.contains(name) {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::RecursiveTypeDefinition(
|
||||
typedef.name.clone(),
|
||||
name.clone(),
|
||||
)),
|
||||
Err(ErrorKind::RecursiveTypeDefinition(typedef.name.clone(), name.clone())),
|
||||
typedef.meta,
|
||||
);
|
||||
} else {
|
||||
@ -113,11 +107,7 @@ fn check_typedefs_for_recursion<'a, 'b>(
|
||||
}
|
||||
|
||||
impl BinopDefinition {
|
||||
fn typecheck(
|
||||
&mut self,
|
||||
typerefs: &TypeRefs,
|
||||
state: &mut TypecheckPassState,
|
||||
) -> Result<TypeKind, ErrorKind> {
|
||||
fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> {
|
||||
for param in vec![&self.lhs, &self.rhs] {
|
||||
let param_t = state.or_else(
|
||||
param.1.assert_known(typerefs, state),
|
||||
@ -141,29 +131,21 @@ impl BinopDefinition {
|
||||
let return_type = self.return_type.clone().assert_known(typerefs, state)?;
|
||||
|
||||
state.scope.return_type_hint = Some(self.return_type.clone());
|
||||
let inferred =
|
||||
self.fn_kind
|
||||
.typecheck(&typerefs, &mut state.inner(), Some(return_type.clone()));
|
||||
let inferred = self
|
||||
.fn_kind
|
||||
.typecheck(&typerefs, &mut state.inner(), Some(return_type.clone()));
|
||||
|
||||
match inferred {
|
||||
Ok(t) => return_type
|
||||
.narrow_into(&t.1)
|
||||
.or(Err(ErrorKind::ReturnTypeMismatch(return_type, t.1))),
|
||||
Err(e) => Ok(state.or_else(
|
||||
Err(e),
|
||||
return_type,
|
||||
self.block_meta().unwrap_or(self.signature()),
|
||||
)),
|
||||
Err(e) => Ok(state.or_else(Err(e), return_type, self.block_meta().unwrap_or(self.signature()))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionDefinition {
|
||||
fn typecheck(
|
||||
&mut self,
|
||||
typerefs: &TypeRefs,
|
||||
state: &mut TypecheckPassState,
|
||||
) -> Result<TypeKind, ErrorKind> {
|
||||
fn typecheck(&mut self, typerefs: &TypeRefs, state: &mut TypecheckPassState) -> Result<TypeKind, ErrorKind> {
|
||||
for param in &self.parameters {
|
||||
let param_t = state.or_else(
|
||||
param.1.assert_known(typerefs, state),
|
||||
@ -185,9 +167,7 @@ impl FunctionDefinition {
|
||||
}
|
||||
|
||||
let return_type = self.return_type.clone().assert_known(typerefs, state)?;
|
||||
let inferred = self
|
||||
.kind
|
||||
.typecheck(typerefs, state, Some(self.return_type.clone()));
|
||||
let inferred = self.kind.typecheck(typerefs, state, Some(self.return_type.clone()));
|
||||
|
||||
match inferred {
|
||||
Ok(t) => return_type
|
||||
@ -210,12 +190,8 @@ impl FunctionDefinitionKind {
|
||||
state.scope.return_type_hint = hint.clone();
|
||||
block.typecheck(&mut state.inner(), &typerefs, hint.as_ref())
|
||||
}
|
||||
FunctionDefinitionKind::Extern(_) => {
|
||||
Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown)))
|
||||
}
|
||||
FunctionDefinitionKind::Intrinsic(..) => {
|
||||
Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown)))
|
||||
}
|
||||
FunctionDefinitionKind::Extern(_) => Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))),
|
||||
FunctionDefinitionKind::Intrinsic(..) => Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -293,9 +269,7 @@ impl Block {
|
||||
mutable: *mutable,
|
||||
},
|
||||
)
|
||||
.or(Err(ErrorKind::VariableAlreadyDefined(
|
||||
variable_reference.1.clone(),
|
||||
)));
|
||||
.or(Err(ErrorKind::VariableAlreadyDefined(variable_reference.1.clone())));
|
||||
state.ok(res, variable_reference.2);
|
||||
None
|
||||
}
|
||||
@ -344,26 +318,16 @@ impl Block {
|
||||
StmtKind::Expression(expression) => {
|
||||
let res = expression.typecheck(&mut state, &typerefs, None);
|
||||
state.or_else(res, TypeKind::Void, expression.1);
|
||||
if let Ok((kind, _)) =
|
||||
expression.return_type(typerefs, state.module_id.unwrap())
|
||||
{
|
||||
if let Ok((kind, _)) = expression.return_type(typerefs, state.module_id.unwrap()) {
|
||||
Some((kind, expression))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
StmtKind::While(WhileStatement {
|
||||
condition,
|
||||
block,
|
||||
meta,
|
||||
}) => {
|
||||
let condition_ty =
|
||||
condition.typecheck(&mut state, typerefs, Some(&TypeKind::Bool))?;
|
||||
StmtKind::While(WhileStatement { condition, block, meta }) => {
|
||||
let condition_ty = condition.typecheck(&mut state, typerefs, Some(&TypeKind::Bool))?;
|
||||
if condition_ty.assert_known(typerefs, &state)? != TypeKind::Bool {
|
||||
state.note_errors(
|
||||
&vec![ErrorKind::TypesIncompatible(condition_ty, TypeKind::Bool)],
|
||||
*meta,
|
||||
);
|
||||
state.note_errors(&vec![ErrorKind::TypesIncompatible(condition_ty, TypeKind::Bool)], *meta);
|
||||
}
|
||||
|
||||
block.typecheck(&mut state, typerefs, None)?;
|
||||
@ -453,93 +417,23 @@ impl Expression {
|
||||
let rhs_res = rhs.typecheck(state, &typerefs, None);
|
||||
let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1);
|
||||
|
||||
if let Some(binop) = state
|
||||
.scope
|
||||
.binops
|
||||
if let Some(binop) = typerefs
|
||||
.binop_types
|
||||
.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 {
|
||||
Err(ErrorKind::InvalidBinop(*op, lhs_type, rhs_type))
|
||||
dbg!(&op, &lhs, &rhs);
|
||||
dbg!(&lhs_type);
|
||||
dbg!(&rhs_type);
|
||||
panic!()
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -571,21 +465,16 @@ impl Expression {
|
||||
.into_iter()
|
||||
.chain(iter::repeat(TypeKind::Vague(Vague::Unknown)));
|
||||
|
||||
for (param, true_param_t) in
|
||||
function_call.parameters.iter_mut().zip(true_params_iter)
|
||||
{
|
||||
for (param, true_param_t) in function_call.parameters.iter_mut().zip(true_params_iter) {
|
||||
// Typecheck every param separately
|
||||
let param_res = param.typecheck(state, &typerefs, Some(&true_param_t));
|
||||
let param_t =
|
||||
state.or_else(param_res, TypeKind::Vague(Vague::Unknown), param.1);
|
||||
let param_t = state.or_else(param_res, TypeKind::Vague(Vague::Unknown), param.1);
|
||||
state.ok(param_t.narrow_into(&true_param_t), param.1);
|
||||
}
|
||||
|
||||
// Make sure function return type is the same as the claimed
|
||||
// return type
|
||||
let ret_t = f
|
||||
.ret
|
||||
.narrow_into(&function_call.return_type.resolve_ref(typerefs))?;
|
||||
let ret_t = f.ret.narrow_into(&function_call.return_type.resolve_ref(typerefs))?;
|
||||
// Update typing to be more accurate
|
||||
function_call.return_type = ret_t.clone();
|
||||
Ok(ret_t.resolve_ref(typerefs))
|
||||
@ -604,8 +493,7 @@ impl Expression {
|
||||
let then_ret_t = state.or_else(then_res, TypeKind::Vague(Vague::Unknown), lhs.1);
|
||||
let else_ret_t = if let Some(else_expr) = rhs.as_mut() {
|
||||
let res = else_expr.typecheck(state, &typerefs, hint_t);
|
||||
let else_ret_t =
|
||||
state.or_else(res, TypeKind::Vague(Vague::Unknown), else_expr.1);
|
||||
let else_ret_t = state.or_else(res, TypeKind::Vague(Vague::Unknown), else_expr.1);
|
||||
|
||||
else_ret_t
|
||||
} else {
|
||||
@ -682,10 +570,7 @@ impl Expression {
|
||||
for other in iter {
|
||||
state.ok(first.narrow_into(other), self.1);
|
||||
}
|
||||
Ok(TypeKind::Array(
|
||||
Box::new(first.clone()),
|
||||
expressions.len() as u64,
|
||||
))
|
||||
Ok(TypeKind::Array(Box::new(first.clone()), expressions.len() as u64))
|
||||
} else {
|
||||
Ok(TypeKind::Array(Box::new(TypeKind::Void), 0))
|
||||
}
|
||||
@ -705,8 +590,7 @@ impl Expression {
|
||||
|
||||
// Typecheck expression
|
||||
let expr_res = expression.typecheck(state, typerefs, Some(&expected_ty));
|
||||
let expr_ty =
|
||||
state.or_else(expr_res, TypeKind::Vague(Vague::Unknown), expression.1);
|
||||
let expr_ty = state.or_else(expr_res, TypeKind::Vague(Vague::Unknown), expression.1);
|
||||
|
||||
if let TypeKind::CustomType(key) = expr_ty {
|
||||
let struct_type = state
|
||||
@ -742,18 +626,14 @@ impl Expression {
|
||||
let expected_ty = state.or_else(
|
||||
struct_def
|
||||
.get_field_ty(field_name)
|
||||
.ok_or(ErrorKind::NoSuchField(format!(
|
||||
"{}.{}",
|
||||
struct_name, field_name
|
||||
))),
|
||||
.ok_or(ErrorKind::NoSuchField(format!("{}.{}", struct_name, field_name))),
|
||||
&TypeKind::Vague(VagueType::Unknown),
|
||||
field_expr.1,
|
||||
);
|
||||
|
||||
// Typecheck the actual expression
|
||||
let expr_res = field_expr.typecheck(state, typerefs, Some(expected_ty));
|
||||
let expr_ty =
|
||||
state.or_else(expr_res, TypeKind::Vague(Vague::Unknown), field_expr.1);
|
||||
let expr_ty = state.or_else(expr_res, TypeKind::Vague(Vague::Unknown), field_expr.1);
|
||||
|
||||
// Make sure both are the same type, report error if not
|
||||
state.ok(expr_ty.narrow_into(&expr_ty), field_expr.1);
|
||||
@ -829,7 +709,6 @@ 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;
|
||||
|
@ -12,9 +12,8 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
mir::{
|
||||
BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition,
|
||||
FunctionDefinitionKind, IfExpression, Module, ReturnKind, StmtKind, TypeKind,
|
||||
WhileStatement,
|
||||
BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind,
|
||||
IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement,
|
||||
},
|
||||
util::try_all,
|
||||
};
|
||||
@ -46,10 +45,7 @@ impl<'t> Pass for TypeInference<'t> {
|
||||
for function in &mut module.functions {
|
||||
if let Some(kind) = seen_functions.get(&function.name) {
|
||||
state.note_errors(
|
||||
&vec![ErrorKind::FunctionAlreadyDefined(
|
||||
function.name.clone(),
|
||||
*kind,
|
||||
)],
|
||||
&vec![ErrorKind::FunctionAlreadyDefined(function.name.clone(), *kind)],
|
||||
function.signature(),
|
||||
);
|
||||
} else {
|
||||
@ -70,8 +66,7 @@ impl<'t> Pass for TypeInference<'t> {
|
||||
params: (binop.lhs.1.clone(), binop.rhs.1.clone()),
|
||||
operator: binop.op,
|
||||
};
|
||||
if seen_binops.contains(&binop_key)
|
||||
|| (binop.lhs == binop.rhs && binop.lhs.1.category().is_simple_maths())
|
||||
if seen_binops.contains(&binop_key) || (binop.lhs == binop.rhs && binop.lhs.1.category().is_simple_maths())
|
||||
{
|
||||
state.note_errors(
|
||||
&vec![ErrorKind::BinaryOpAlreadyDefined(
|
||||
@ -100,18 +95,10 @@ impl<'t> Pass for TypeInference<'t> {
|
||||
}
|
||||
|
||||
impl BinopDefinition {
|
||||
fn infer_types(
|
||||
&mut self,
|
||||
type_refs: &TypeRefs,
|
||||
state: &mut TypecheckPassState,
|
||||
) -> Result<(), ErrorKind> {
|
||||
fn infer_types(&mut self, type_refs: &TypeRefs, state: &mut TypecheckPassState) -> Result<(), ErrorKind> {
|
||||
let scope_hints = ScopeTypeRefs::from(type_refs);
|
||||
|
||||
let lhs_ty = state.or_else(
|
||||
self.lhs.1.assert_unvague(),
|
||||
Vague(Unknown),
|
||||
self.signature(),
|
||||
);
|
||||
let lhs_ty = state.or_else(self.lhs.1.assert_unvague(), Vague(Unknown), self.signature());
|
||||
state.ok(
|
||||
scope_hints
|
||||
.new_var(self.lhs.0.clone(), false, &lhs_ty)
|
||||
@ -119,11 +106,7 @@ impl BinopDefinition {
|
||||
self.signature(),
|
||||
);
|
||||
|
||||
let rhs_ty = state.or_else(
|
||||
self.rhs.1.assert_unvague(),
|
||||
Vague(Unknown),
|
||||
self.signature(),
|
||||
);
|
||||
let rhs_ty = state.or_else(self.rhs.1.assert_unvague(), Vague(Unknown), self.signature());
|
||||
|
||||
state.ok(
|
||||
scope_hints
|
||||
@ -132,9 +115,9 @@ impl BinopDefinition {
|
||||
self.signature(),
|
||||
);
|
||||
|
||||
let ret_ty =
|
||||
self.fn_kind
|
||||
.infer_types(state, &scope_hints, Some(self.return_type.clone()))?;
|
||||
let ret_ty = self
|
||||
.fn_kind
|
||||
.infer_types(state, &scope_hints, Some(self.return_type.clone()))?;
|
||||
if let Some(mut ret_ty) = ret_ty {
|
||||
ret_ty.narrow(&scope_hints.from_type(&self.return_type).unwrap());
|
||||
}
|
||||
@ -144,11 +127,7 @@ impl BinopDefinition {
|
||||
}
|
||||
|
||||
impl FunctionDefinition {
|
||||
fn infer_types(
|
||||
&mut self,
|
||||
type_refs: &TypeRefs,
|
||||
state: &mut TypecheckPassState,
|
||||
) -> Result<(), ErrorKind> {
|
||||
fn infer_types(&mut self, type_refs: &TypeRefs, state: &mut TypecheckPassState) -> Result<(), ErrorKind> {
|
||||
let scope_refs = ScopeTypeRefs::from(type_refs);
|
||||
for param in &self.parameters {
|
||||
let param_t = state.or_else(param.1.assert_unvague(), Vague(Unknown), self.signature());
|
||||
@ -221,8 +200,7 @@ impl Block {
|
||||
match &mut statement.0 {
|
||||
StmtKind::Let(var, mutable, expr) => {
|
||||
// Get the TypeRef for this variable declaration
|
||||
let mut var_ref =
|
||||
state.ok(inner_refs.new_var(var.1.clone(), *mutable, &var.0), var.2);
|
||||
let mut var_ref = state.ok(inner_refs.new_var(var.1.clone(), *mutable, &var.0), var.2);
|
||||
|
||||
// If ok, update the MIR type to this TypeRef
|
||||
if let Some(var_ref) = &var_ref {
|
||||
@ -235,9 +213,7 @@ impl Block {
|
||||
|
||||
// Try to narrow the variable type declaration with the
|
||||
// expression
|
||||
if let (Some(var_ref), Some(expr_ty_ref)) =
|
||||
(var_ref.as_mut(), expr_ty_ref.as_mut())
|
||||
{
|
||||
if let (Some(var_ref), Some(expr_ty_ref)) = (var_ref.as_mut(), expr_ty_ref.as_mut()) {
|
||||
var_ref.narrow(&expr_ty_ref);
|
||||
}
|
||||
}
|
||||
@ -260,9 +236,7 @@ impl Block {
|
||||
let expr_res = expr.infer_types(&mut state, &inner_refs);
|
||||
state.ok(expr_res, expr.1);
|
||||
}
|
||||
StmtKind::While(WhileStatement {
|
||||
condition, block, ..
|
||||
}) => {
|
||||
StmtKind::While(WhileStatement { condition, block, .. }) => {
|
||||
condition.infer_types(&mut state, &inner_refs)?;
|
||||
block.infer_types(&mut state, &inner_refs)?;
|
||||
}
|
||||
@ -336,11 +310,7 @@ impl Expression {
|
||||
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(),
|
||||
))
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
ExprKind::FunctionCall(function_call) => {
|
||||
@ -357,9 +327,7 @@ impl Expression {
|
||||
// many were provided)
|
||||
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)
|
||||
{
|
||||
for (param_expr, param_t) in function_call.parameters.iter_mut().zip(true_params_iter) {
|
||||
let expr_res = param_expr.infer_types(state, type_refs);
|
||||
if let Some(mut param_ref) = state.ok(expr_res, param_expr.1) {
|
||||
param_ref.narrow(&mut type_refs.from_type(param_t).unwrap());
|
||||
@ -390,12 +358,10 @@ impl Expression {
|
||||
|
||||
// Narrow LHS to the same type as RHS and return it's return type
|
||||
if let (Some(mut lhs_hints), Some(mut rhs_hints)) = (lhs_hints, rhs_hints) {
|
||||
lhs_hints
|
||||
.narrow(&mut rhs_hints)
|
||||
.ok_or(ErrorKind::TypesIncompatible(
|
||||
lhs_hints.resolve_deep().unwrap(),
|
||||
rhs_hints.resolve_deep().unwrap(),
|
||||
))
|
||||
lhs_hints.narrow(&mut rhs_hints).ok_or(ErrorKind::TypesIncompatible(
|
||||
lhs_hints.resolve_deep().unwrap(),
|
||||
rhs_hints.resolve_deep().unwrap(),
|
||||
))
|
||||
} else {
|
||||
// Failed to retrieve types from either
|
||||
Ok(type_refs.from_type(&Vague(Unknown)).unwrap())
|
||||
@ -451,15 +417,10 @@ impl Expression {
|
||||
}
|
||||
|
||||
Ok(type_refs
|
||||
.from_type(&Array(
|
||||
Box::new(first.as_type()),
|
||||
expressions.len() as u64,
|
||||
))
|
||||
.from_type(&Array(Box::new(first.as_type()), expressions.len() as u64))
|
||||
.unwrap())
|
||||
} else {
|
||||
Ok(type_refs
|
||||
.from_type(&Array(Box::new(TypeKind::Void), 0))
|
||||
.unwrap())
|
||||
Ok(type_refs.from_type(&Array(Box::new(TypeKind::Void), 0)).unwrap())
|
||||
}
|
||||
}
|
||||
Err(errors) => {
|
||||
@ -503,10 +464,7 @@ impl Expression {
|
||||
let expected_struct_ty = state
|
||||
.scope
|
||||
.get_struct_type(&type_key)
|
||||
.ok_or(ErrorKind::NoSuchType(
|
||||
struct_name.clone(),
|
||||
state.module_id.unwrap(),
|
||||
))?
|
||||
.ok_or(ErrorKind::NoSuchType(struct_name.clone(), state.module_id.unwrap()))?
|
||||
.clone();
|
||||
for field in fields {
|
||||
if let Some(expected_field_ty) = expected_struct_ty.get_field_ty(&field.0) {
|
||||
@ -516,17 +474,12 @@ impl Expression {
|
||||
}
|
||||
} else {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::NoSuchField(format!(
|
||||
"{}.{}",
|
||||
struct_name, field.0
|
||||
))),
|
||||
Err(ErrorKind::NoSuchField(format!("{}.{}", struct_name, field.0))),
|
||||
field.1 .1,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(type_refs
|
||||
.from_type(&TypeKind::CustomType(type_key.clone()))
|
||||
.unwrap())
|
||||
Ok(type_refs.from_type(&TypeKind::CustomType(type_key.clone())).unwrap())
|
||||
}
|
||||
ExprKind::Borrow(var, mutable) => {
|
||||
// Find variable type
|
||||
|
@ -15,10 +15,7 @@ use super::{
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeRef<'scope>(
|
||||
pub(super) TypeIdRef,
|
||||
pub(super) &'scope ScopeTypeRefs<'scope>,
|
||||
);
|
||||
pub struct TypeRef<'scope>(pub(super) TypeIdRef, pub(super) &'scope ScopeTypeRefs<'scope>);
|
||||
|
||||
impl<'scope> TypeRef<'scope> {
|
||||
/// Resolve current type in a weak manner, not resolving any Arrays or
|
||||
@ -99,7 +96,7 @@ pub struct TypeRefs {
|
||||
pub(super) hints: RefCell<Vec<TypeRefKind>>,
|
||||
/// Indirect ID-references, referring to hints-vec
|
||||
pub(super) type_refs: RefCell<Vec<TypeIdRef>>,
|
||||
binop_types: BinopMap,
|
||||
pub(super) binop_types: BinopMap,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TypeRefs {
|
||||
@ -132,9 +129,7 @@ impl TypeRefs {
|
||||
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::Direct(ty.clone()));
|
||||
self.hints.borrow_mut().push(TypeRefKind::Direct(ty.clone()));
|
||||
typecell
|
||||
}
|
||||
|
||||
@ -183,11 +178,7 @@ impl TypeRefs {
|
||||
|
||||
pub fn retrieve_wide_type(&self, idx: usize) -> Option<TypeKind> {
|
||||
let inner_idx = unsafe { *self.recurse_type_ref(idx).borrow() };
|
||||
self.hints
|
||||
.borrow()
|
||||
.get(inner_idx)
|
||||
.cloned()
|
||||
.map(|t| t.widen(self))
|
||||
self.hints.borrow().get(inner_idx).cloned().map(|t| t.widen(self))
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,9 +209,7 @@ impl<'outer> ScopeTypeRefs<'outer> {
|
||||
return Err(ErrorKind::VariableAlreadyDefined(name));
|
||||
}
|
||||
let type_ref = self.from_type(&initial_ty).unwrap();
|
||||
self.variables
|
||||
.borrow_mut()
|
||||
.insert(name, (mutable, type_ref.0.clone()));
|
||||
self.variables.borrow_mut().insert(name, (mutable, type_ref.0.clone()));
|
||||
Ok(type_ref)
|
||||
}
|
||||
|
||||
@ -233,8 +222,7 @@ impl<'outer> ScopeTypeRefs<'outer> {
|
||||
TypeKind::Vague(_) => self.types.new(ty),
|
||||
TypeKind::Array(elem_ty, length) => {
|
||||
let elem_ty = self.from_type(elem_ty)?;
|
||||
self.types
|
||||
.new(&TypeKind::Array(Box::new(elem_ty.as_type()), *length))
|
||||
self.types.new(&TypeKind::Array(Box::new(elem_ty.as_type()), *length))
|
||||
}
|
||||
TypeKind::Borrow(ty, mutable) => {
|
||||
let inner_ty = self.from_type(ty)?;
|
||||
@ -252,12 +240,7 @@ impl<'outer> ScopeTypeRefs<'outer> {
|
||||
Some(TypeRef(idx, self))
|
||||
}
|
||||
|
||||
pub fn from_binop(
|
||||
&'outer self,
|
||||
op: BinaryOperator,
|
||||
lhs: &TypeRef,
|
||||
rhs: &TypeRef,
|
||||
) -> TypeRef<'outer> {
|
||||
pub fn from_binop(&'outer self, op: BinaryOperator, lhs: &TypeRef, rhs: &TypeRef) -> TypeRef<'outer> {
|
||||
TypeRef(self.types.binop(op, lhs, rhs), self)
|
||||
}
|
||||
|
||||
@ -276,10 +259,9 @@ impl<'outer> ScopeTypeRefs<'outer> {
|
||||
.iter()
|
||||
.filter(|b| b.1.operator == *op && b.1.return_ty == *ty);
|
||||
for binop in binops {
|
||||
if let (Ok(lhs_narrow), Ok(rhs_narrow)) = (
|
||||
lhs.narrow_into(&binop.1.hands.0),
|
||||
rhs.narrow_into(&binop.1.hands.1),
|
||||
) {
|
||||
if let (Ok(lhs_narrow), Ok(rhs_narrow)) =
|
||||
(lhs.narrow_into(&binop.1.hands.0), rhs.narrow_into(&binop.1.hands.1))
|
||||
{
|
||||
*lhs = lhs_narrow;
|
||||
*rhs = rhs_narrow
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user