From 14283afe59cfa71b58769b83f5d45386540968c5 Mon Sep 17 00:00:00 2001 From: sofia Date: Tue, 8 Jul 2025 21:44:04 +0300 Subject: [PATCH] Separate pass-common code to pass.rs --- reid/examples/reid/fibonacci.reid | 2 +- reid/examples/testcodegen.rs | 21 +-- reid/src/ast/process.rs | 10 +- reid/src/codegen.rs | 31 +++- reid/src/lib.rs | 19 +- reid/src/mir/mod.rs | 6 + reid/src/mir/pass.rs | 279 ++++++++++++++++++++++++++++++ reid/src/mir/typecheck.rs | 253 +++++++++------------------ 8 files changed, 429 insertions(+), 192 deletions(-) create mode 100644 reid/src/mir/pass.rs diff --git a/reid/examples/reid/fibonacci.reid b/reid/examples/reid/fibonacci.reid index 2947183..7fc98c8 100644 --- a/reid/examples/reid/fibonacci.reid +++ b/reid/examples/reid/fibonacci.reid @@ -9,5 +9,5 @@ fn fibonacci(value: i32) -> i32 { if value < 3 { return 1; } - return fibonacci(value - 1) + fibonacci(value - 2); + return 5 + fibonacci(value - 2); } diff --git a/reid/examples/testcodegen.rs b/reid/examples/testcodegen.rs index 940edf1..369b63a 100644 --- a/reid/examples/testcodegen.rs +++ b/reid/examples/testcodegen.rs @@ -1,4 +1,4 @@ -use reid::mir::*; +use reid::mir::{self, *}; use reid_lib::Context; fn main() { @@ -152,21 +152,22 @@ fn main() { ), }; + let mir_context = mir::Context { + modules: vec![Module { + name: "test module".to_owned(), + imports: vec![], + functions: vec![fibonacci, main], + }], + }; println!("test1"); - let module = Module { - name: "test module".to_owned(), - imports: vec![], - functions: vec![fibonacci, main], - }; - - println!("test2"); let context = Context::new(); - let codegen_module = module.codegen(&context); + let codegen = mir_context.codegen(&context); + println!("test2"); + codegen.compile(); println!("test3"); - codegen_module.context.compile(); // match codegen_module.module.print_to_string() { // Ok(v) => println!("{}", v), // Err(e) => println!("Err: {:?}", e), diff --git a/reid/src/ast/process.rs b/reid/src/ast/process.rs index faafac1..7009ff2 100644 --- a/reid/src/ast/process.rs +++ b/reid/src/ast/process.rs @@ -3,8 +3,16 @@ use crate::{ mir::{self, StmtKind, VariableReference}, }; +impl mir::Context { + pub fn from(modules: Vec) -> mir::Context { + mir::Context { + modules: modules.iter().map(|m| m.process()).collect(), + } + } +} + impl ast::Module { - pub fn process(&self) -> mir::Module { + fn process(&self) -> mir::Module { let mut imports = Vec::new(); let mut functions = Vec::new(); diff --git a/reid/src/codegen.rs b/reid/src/codegen.rs index c5a0bd0..b49b954 100644 --- a/reid/src/codegen.rs +++ b/reid/src/codegen.rs @@ -7,13 +7,42 @@ use reid_lib::{ use crate::mir::{self, types::ReturnType, TypeKind, VariableReference}; +#[derive(Debug)] +pub struct CodegenContext<'ctx> { + pub modules: Vec>, +} + +impl<'ctx> CodegenContext<'ctx> { + pub fn compile(&self) { + for module in &self.modules { + module.context.compile(); + } + } +} + +impl mir::Context { + pub fn codegen<'ctx>(&self, context: &'ctx Context) -> CodegenContext<'ctx> { + let mut modules = Vec::new(); + for module in &self.modules { + modules.push(module.codegen(context)); + } + CodegenContext { modules } + } +} + pub struct ModuleCodegen<'ctx> { pub context: &'ctx Context, pub module: Module<'ctx>, } +impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.context.fmt(f) + } +} + impl mir::Module { - pub fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> { + fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> { let mut module = context.module(&self.name); let mut functions = HashMap::new(); diff --git a/reid/src/lib.rs b/reid/src/lib.rs index 84c86cc..35ab0aa 100644 --- a/reid/src/lib.rs +++ b/reid/src/lib.rs @@ -1,3 +1,4 @@ +use mir::typecheck::TypeCheck; use reid_lib::Context; use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream}; @@ -22,7 +23,7 @@ pub enum ReidError { #[error(transparent)] ParserError(#[from] token_stream::Error), #[error("Errors during typecheck: {0:?}")] - TypeCheckErrors(Vec), + TypeCheckErrors(Vec>), // #[error(transparent)] // CodegenError(#[from] codegen::Error), } @@ -48,24 +49,24 @@ pub fn compile(source: &str) -> Result { }; dbg!(&ast_module); - let mut mir_module = ast_module.process(); + let mut mir_context = mir::Context::from(vec![ast_module]); - dbg!(&mir_module); + dbg!(&mir_context); - let state = mir_module.typecheck(); - dbg!(&mir_module); + let state = mir_context.pass(&mut TypeCheck {}); + dbg!(&mir_context); dbg!(&state); if !state.errors.is_empty() { return Err(ReidError::TypeCheckErrors(state.errors)); } - dbg!(&mir_module); + dbg!(&mir_context); let mut context = Context::new(); - let codegen_module = mir_module.codegen(&mut context); + let codegen_modules = mir_context.codegen(&mut context); - dbg!(&codegen_module.context); - codegen_module.context.compile(); + dbg!(&codegen_modules); + codegen_modules.compile(); Ok(String::new()) diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index f638a1a..42f2b3e 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -3,6 +3,7 @@ /// type-checked beforehand. use crate::token_stream::TokenRange; +pub mod pass; pub mod typecheck; pub mod types; @@ -215,3 +216,8 @@ pub struct Module { pub imports: Vec, pub functions: Vec, } + +#[derive(Debug)] +pub struct Context { + pub modules: Vec, +} diff --git a/reid/src/mir/pass.rs b/reid/src/mir/pass.rs new file mode 100644 index 0000000..7cba8db --- /dev/null +++ b/reid/src/mir/pass.rs @@ -0,0 +1,279 @@ +use std::collections::HashMap; +use std::error::Error as STDError; + +use super::*; + +#[derive(thiserror::Error, Debug, Clone)] +pub enum SimplePassError { + #[error("Function not defined: {0}")] + FunctionAlreadyDefined(String), + #[error("Variable not defined: {0}")] + VariableAlreadyDefined(String), +} + +#[derive(Debug, Clone)] +pub struct Error { + metadata: Metadata, + kind: TErr, +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error at {}: {}", self.metadata, self.kind) + } +} + +impl STDError for Error { + fn source(&self) -> Option<&(dyn STDError + 'static)> { + self.kind.source() + } +} + +#[derive(Clone, Debug)] +pub struct Storage(HashMap); + +#[derive(Debug)] +pub struct State { + pub errors: Vec>, +} + +impl State { + fn new() -> State { + State { + errors: Default::default(), + } + } + + fn or_else + Clone + Copy>( + &mut self, + result: Result, + default: U, + meta: T, + ) -> U { + match result { + Ok(t) => t, + Err(e) => { + self.errors.push(Error { + metadata: meta.into(), + kind: e, + }); + default + } + } + } + + fn ok + Clone + Copy, U>( + &mut self, + result: Result, + meta: T, + ) -> Option { + match result { + Ok(v) => Some(v), + Err(e) => { + self.errors.push(Error { + metadata: meta.into(), + kind: e, + }); + None + } + } + } +} + +impl Default for Storage { + fn default() -> Self { + Self(Default::default()) + } +} + +impl Storage { + pub fn set(&mut self, key: String, value: T) -> Result { + if let Some(_) = self.0.get(&key) { + Err(()) + } else { + self.0.insert(key, value.clone()); + Ok(value) + } + } + + pub fn get(&self, key: &String) -> Option<&T> { + self.0.get(key) + } +} + +#[derive(Clone, Default, Debug)] +pub struct Scope { + pub function_returns: Storage, + pub variables: Storage, + /// Hard Return type of this scope, if inside a function + pub return_type_hint: Option, +} + +#[derive(Clone, Debug)] +pub struct ScopeFunction { + pub ret: TypeKind, + pub params: Vec, +} + +impl Scope { + pub fn inner(&self) -> Scope { + Scope { + function_returns: self.function_returns.clone(), + variables: self.variables.clone(), + return_type_hint: self.return_type_hint, + } + } +} + +pub struct PassState<'st, 'sc, TError: STDError + Clone> { + state: &'st mut State, + pub scope: &'sc mut Scope, + inner: Vec, +} + +impl<'st, 'sc, TError: STDError + Clone> PassState<'st, 'sc, TError> { + fn from(state: &'st mut State, scope: &'sc mut Scope) -> Self { + PassState { + state, + scope, + inner: Vec::new(), + } + } + + pub fn or_else + Clone + Copy>( + &mut self, + result: Result, + default: U, + meta: TMeta, + ) -> U { + self.state.or_else(result, default, meta) + } + + pub fn ok + Clone + Copy, U>( + &mut self, + result: Result, + meta: TMeta, + ) -> Option { + self.state.ok(result, meta) + } + + pub fn inner(&mut self) -> PassState { + self.inner.push(self.scope.inner()); + let scope = self.inner.last_mut().unwrap(); + PassState { + state: self.state, + scope, + inner: Vec::new(), + } + } +} + +pub trait Pass { + type TError: STDError + Clone; + + fn module(&mut self, _module: &mut Module, mut _state: PassState) {} + fn function( + &mut self, + _function: &mut FunctionDefinition, + mut _state: PassState, + ) { + } + fn block(&mut self, _block: &mut Block, mut _state: PassState) {} + fn stmt(&mut self, _stmt: &mut Statement, mut _state: PassState) {} + fn expr(&mut self, _expr: &mut Expression, mut _state: PassState) {} +} + +impl Context { + pub fn pass(&mut self, pass: &mut T) -> State { + let mut state = State::new(); + let mut scope = Scope::default(); + for module in &mut self.modules { + module.pass(pass, &mut state, &mut scope); + } + state + } +} + +impl Module { + fn pass(&mut self, pass: &mut T, state: &mut State, scope: &mut Scope) { + for function in &self.functions { + scope + .function_returns + .set( + function.name.clone(), + ScopeFunction { + ret: function.return_type, + params: function.parameters.iter().map(|v| v.1).collect(), + }, + ) + .ok(); + } + + pass.module(self, PassState::from(state, scope)); + + for function in &mut self.functions { + function.pass(pass, state, scope); + } + } +} + +impl FunctionDefinition { + fn pass(&mut self, pass: &mut T, state: &mut State, scope: &mut Scope) { + for param in &self.parameters { + scope.variables.set(param.0.clone(), param.1).ok(); + } + + pass.function(self, PassState::from(state, scope)); + + match &mut self.kind { + FunctionDefinitionKind::Local(block, _) => { + scope.return_type_hint = Some(self.return_type); + block.pass(pass, state, scope); + } + FunctionDefinitionKind::Extern => {} + }; + } +} + +impl Block { + fn pass(&mut self, pass: &mut T, state: &mut State, scope: &mut Scope) { + let mut scope = scope.inner(); + + for statement in &mut self.statements { + statement.pass(pass, state, &mut scope); + } + + pass.block(self, PassState::from(state, &mut scope)); + } +} + +impl Statement { + fn pass(&mut self, pass: &mut T, state: &mut State, scope: &mut Scope) { + match &mut self.0 { + StmtKind::Let(_, expression) => { + expression.pass(pass, state, scope); + } + StmtKind::Import(_) => todo!(), + StmtKind::Expression(expression) => { + expression.pass(pass, state, scope); + } + } + + pass.stmt(self, PassState::from(state, scope)); + + match &mut self.0 { + StmtKind::Let(variable_reference, _) => scope + .variables + .set(variable_reference.1.clone(), variable_reference.0) + .ok(), + StmtKind::Import(_) => todo!(), + StmtKind::Expression(_) => None, + }; + } +} + +impl Expression { + fn pass(&mut self, pass: &mut T, state: &mut State, scope: &mut Scope) { + pass.expr(self, PassState::from(state, scope)); + } +} diff --git a/reid/src/mir/typecheck.rs b/reid/src/mir/typecheck.rs index 3ecbd03..e17b218 100644 --- a/reid/src/mir/typecheck.rs +++ b/reid/src/mir/typecheck.rs @@ -1,27 +1,11 @@ -use std::{collections::HashMap, convert::Infallible, iter}; +use std::{convert::Infallible, iter}; /// This module contains code relevant to doing a type checking pass on the MIR. use crate::{mir::*, util::try_all}; use TypeKind::*; use VagueType::*; -#[derive(Debug, Clone)] -pub struct Error { - metadata: Metadata, - kind: ErrorKind, -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Error at {}: {}", self.metadata, self.kind) - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.kind.source() - } -} +use super::pass::{Pass, PassState, ScopeFunction}; #[derive(thiserror::Error, Debug, Clone)] pub enum ErrorKind { @@ -29,6 +13,8 @@ pub enum ErrorKind { Null, #[error("Type is vague: {0}")] TypeIsVague(VagueType), + #[error("Can not coerce {0} to vague type {1}")] + HintIsVague(TypeKind, VagueType), #[error("Types {0} and {1} are incompatible")] TypesIncompatible(TypeKind, TypeKind), #[error("Variable not defined: {0}")] @@ -37,150 +23,42 @@ pub enum ErrorKind { FunctionNotDefined(String), #[error("Type is vague: {0}")] ReturnTypeMismatch(TypeKind, TypeKind), + #[error("Function not defined: {0}")] + FunctionAlreadyDefined(String), + #[error("Variable not defined: {0}")] + VariableAlreadyDefined(String), } -#[derive(Clone)] -pub struct TypeStorage(HashMap); +pub struct TypeCheck {} -impl Default for TypeStorage { - fn default() -> Self { - Self(Default::default()) - } -} +impl Pass for TypeCheck { + type TError = ErrorKind; -impl TypeStorage { - fn set(&mut self, key: String, value: T) -> Result { - if let Some(inner) = self.0.get(&key) { - match value.collapse_into(inner) { - Ok(collapsed) => { - self.0.insert(key, collapsed.clone()); - Ok(collapsed) - } - Err(e) => Err(e), - } - } else { - self.0.insert(key, value.clone()); - Ok(value) - } - } - - fn get(&self, key: &String) -> Option<&T> { - self.0.get(key) - } -} - -#[derive(Debug)] -pub struct State { - pub errors: Vec, -} - -impl State { - fn new() -> State { - State { - errors: Default::default(), - } - } - - fn or_else + Clone + Copy>( - &mut self, - result: Result, - default: TypeKind, - meta: T, - ) -> TypeKind { - match result { - Ok(t) => t, - Err(e) => { - self.errors.push(Error { - metadata: meta.into(), - kind: e, - }); - default - } - } - } - - fn ok + Clone + Copy, U>(&mut self, result: Result, meta: T) { - if let Err(e) = result { - self.errors.push(Error { - metadata: meta.into(), - kind: e, - }); - } - } -} - -#[derive(Clone, Default)] -pub struct Scope { - function_returns: TypeStorage, - variables: TypeStorage, - /// Hard Return type of this scope, if inside a function - return_type_hint: Option, -} - -#[derive(Clone)] -pub struct ScopeFunction { - ret: TypeKind, - params: Vec, -} - -impl Scope { - fn inner(&self) -> Scope { - Scope { - function_returns: self.function_returns.clone(), - variables: self.variables.clone(), - return_type_hint: self.return_type_hint, - } - } -} - -#[derive(Clone)] -pub enum Inferred { - Type(TypeKind), - Unresolved(u32), -} - -impl Module { - pub fn typecheck(&mut self) -> State { - let mut state = State::new(); - let mut scope = Scope::default(); - - for function in &self.functions { - state.ok( - scope.function_returns.set( - function.name.clone(), - ScopeFunction { - ret: function.return_type, - params: function.parameters.iter().map(|v| v.1).collect(), - }, - ), - function.signature(), - ); - } - - for function in &mut self.functions { - let res = function.typecheck(&mut state, &mut scope); + fn module(&mut self, module: &mut Module, mut state: PassState) { + for function in &mut module.functions { + let res = function.typecheck(&mut state); state.ok(res, function.block_meta()); } - - state } } impl FunctionDefinition { - fn typecheck(&mut self, state: &mut State, scope: &mut Scope) -> Result { + fn typecheck(&mut self, state: &mut PassState) -> Result { for param in &self.parameters { let param_t = state.or_else(param.1.assert_known(), Vague(Unknown), self.signature()); - state.ok( - scope.variables.set(param.0.clone(), param_t), - self.signature(), - ); + let res = state + .scope + .variables + .set(param.0.clone(), param_t) + .or(Err(ErrorKind::VariableAlreadyDefined(param.0.clone()))); + state.ok(res, self.signature()); } let return_type = self.return_type.clone(); let inferred = match &mut self.kind { FunctionDefinitionKind::Local(block, _) => { - scope.return_type_hint = Some(self.return_type); - block.typecheck(state, scope, Some(return_type)) + state.scope.return_type_hint = Some(self.return_type); + block.typecheck(state, Some(return_type)) } FunctionDefinitionKind::Extern => Ok(Vague(Unknown)), }; @@ -196,16 +74,15 @@ impl FunctionDefinition { impl Block { fn typecheck( &mut self, - state: &mut State, - scope: &mut Scope, + state: &mut PassState, hint_t: Option, ) -> Result { - let mut scope = scope.inner(); + let mut state = state.inner(); for statement in &mut self.statements { match &mut statement.0 { StmtKind::Let(variable_reference, expression) => { - let res = expression.typecheck(state, &mut scope, Some(variable_reference.0)); + let res = expression.typecheck(&mut state, Some(variable_reference.0)); // If expression resolution itself was erronous, resolve as // Unknown. @@ -220,22 +97,24 @@ impl Block { // Make sure expression/variable type is NOT vague anymore let res_t = - state.or_else(res_t.assert_known(), Vague(Unknown), variable_reference.2); + state.or_else(res_t.or_default(), Vague(Unknown), variable_reference.2); // Update typing to be more accurate variable_reference.0 = res_t; // Variable might already be defined, note error - state.ok( - scope - .variables - .set(variable_reference.1.clone(), variable_reference.0), - variable_reference.2, - ); + let res = state + .scope + .variables + .set(variable_reference.1.clone(), variable_reference.0) + .or(Err(ErrorKind::VariableAlreadyDefined( + variable_reference.1.clone(), + ))); + state.ok(res, variable_reference.2); } StmtKind::Import(_) => todo!(), StmtKind::Expression(expression) => { - let res = expression.typecheck(state, &mut scope, hint_t); + let res = expression.typecheck(&mut state, None); state.ok(res, expression.1); } } @@ -243,10 +122,10 @@ impl Block { if let Some((return_kind, expr)) = &mut self.return_expression { let ret_hint_t = match return_kind { - ReturnKind::Hard => scope.return_type_hint, + ReturnKind::Hard => state.scope.return_type_hint, ReturnKind::Soft => hint_t, }; - let res = expr.typecheck(state, &mut scope, ret_hint_t); + let res = expr.typecheck(&mut state, ret_hint_t); Ok(state.or_else(res, Vague(Unknown), expr.1)) } else { Ok(Void) @@ -257,14 +136,16 @@ impl Block { impl Expression { fn typecheck( &mut self, - state: &mut State, - scope: &mut Scope, + state: &mut PassState, hint_t: Option, ) -> Result { match &mut self.0 { ExprKind::Variable(var_ref) => { + dbg!(&state.scope); + let existing = state.or_else( - scope + state + .scope .variables .get(&var_ref.1) .copied() @@ -289,15 +170,23 @@ impl Expression { ExprKind::BinOp(op, lhs, rhs) => { // TODO make sure lhs and rhs can actually do this binary // operation once relevant - let lhs_res = lhs.typecheck(state, scope, None); // TODO + let lhs_res = lhs.typecheck(state, None); // TODO let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.1); - let rhs_res = rhs.typecheck(state, scope, Some(lhs_type)); // TODO + let rhs_res = rhs.typecheck(state, Some(lhs_type)); // TODO let rhs_type = state.or_else(rhs_res, Vague(Unknown), rhs.1); + + if let Some(collapsed) = state.ok(rhs_type.collapse_into(&rhs_type), self.1) { + // Try to coerce both sides again with collapsed type + lhs.typecheck(state, Some(collapsed)).ok(); + rhs.typecheck(state, Some(collapsed)).ok(); + } + let res = lhs_type.binop_type(&op, &rhs_type)?; Ok(res) } ExprKind::FunctionCall(function_call) => { - let true_function = scope + let true_function = state + .scope .function_returns .get(&function_call.name) .cloned() @@ -313,7 +202,7 @@ impl Expression { for (param, true_param_t) in function_call.parameters.iter_mut().zip(true_params_iter) { - let param_res = param.typecheck(state, scope, Some(true_param_t)); + let param_res = param.typecheck(state, Some(true_param_t)); let param_t = state.or_else(param_res, Vague(Unknown), param.1); state.ok(param_t.collapse_into(&true_param_t), param.1); } @@ -330,21 +219,21 @@ impl Expression { } ExprKind::If(IfExpression(cond, lhs, rhs)) => { // TODO make sure cond_res is Boolean here - let cond_res = cond.typecheck(state, scope, Some(Bool)); + let cond_res = cond.typecheck(state, Some(Bool)); let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1); state.ok(cond_t.collapse_into(&Bool), cond.1); - let lhs_res = lhs.typecheck(state, scope, hint_t); + let lhs_res = lhs.typecheck(state, hint_t); let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.meta); let rhs_type = if let Some(rhs) = rhs { - let res = rhs.typecheck(state, scope, hint_t); + let res = rhs.typecheck(state, hint_t); state.or_else(res, Vague(Unknown), rhs.meta) } else { Vague(Unknown) }; lhs_type.collapse_into(&rhs_type) } - ExprKind::Block(block) => block.typecheck(state, scope, hint_t), + ExprKind::Block(block) => block.typecheck(state, hint_t), } } } @@ -359,6 +248,9 @@ impl Literal { (L::I16(_), I16) => self, (L::Vague(VagueL::Number(v)), I32) => L::I32(v as i32), (L::Vague(VagueL::Number(v)), I16) => L::I16(v as i16), + // Default type for number literal if unable to find true type. + (L::Vague(VagueL::Number(v)), Vague(Number)) => L::I32(v as i32), + (_, Vague(_)) => self, _ => Err(ErrorKind::TypesIncompatible(self.as_type(), hint))?, }) } else { @@ -368,10 +260,24 @@ impl Literal { } impl TypeKind { + /// Assert that a type is already known and not vague. Return said type or + /// error. fn assert_known(&self) -> Result { self.is_known().map_err(ErrorKind::TypeIsVague) } + /// Try to collapse a type on itself producing a default type if one exists, + /// Error if not. + fn or_default(&self) -> Result { + match self { + Vague(vague_type) => match vague_type { + Unknown => Err(ErrorKind::TypeIsVague(*vague_type)), + Number => Ok(TypeKind::I32), + }, + _ => Ok(*self), + } + } + fn binop_type(&self, op: &BinaryOperator, other: &TypeKind) -> Result { let res = self.collapse_into(other)?; Ok(match op { @@ -401,6 +307,13 @@ impl Collapsable for TypeKind { } match (self, other) { + (Vague(Number), other) | (other, Vague(Number)) => match other { + Vague(Unknown) => Ok(Vague(Number)), + Vague(Number) => Ok(Vague(Number)), + I32 => Ok(I32), + I16 => Ok(I16), + _ => Err(ErrorKind::TypesIncompatible(*self, *other)), + }, (Vague(Unknown), other) | (other, Vague(Unknown)) => Ok(other.clone()), _ => Err(ErrorKind::TypesIncompatible(*self, *other)), }