From 57dc0222411f98aa6558563d2655a4e59bf77269 Mon Sep 17 00:00:00 2001 From: Sofia Date: Mon, 13 Apr 2026 23:16:39 +0300 Subject: [PATCH] Improve binop typechecking --- src/ast.cpp | 2 +- src/main.cpp | 3 ++- src/typechecker.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/ast.cpp b/src/ast.cpp index 484879e..c17ae76 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -44,7 +44,7 @@ namespace AST { std::string CastExpression::formatted() { std::stringstream out{ "" }; - out << "(" << this->m_ty->formatted() << ") "; + out << "(" << this->m_ty->formatted() << ")"; out << this->m_expr->formatted(); return out.str(); } diff --git a/src/main.cpp b/src/main.cpp index b0a6c99..a56c040 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -117,6 +117,8 @@ std::optional compile(std::string_view in_filename) { return {}; } + // Compile parsed output + codegen::Scope cg_scope{ .binops = typecheck_state.binops, .casts = typecheck_state.casts, @@ -124,7 +126,6 @@ std::optional compile(std::string_view in_filename) { .is_lvalue = false, }; - // Compile parsed output try { for (auto& tls : statements) { std::cout << tls->formatted() << std::endl; diff --git a/src/typechecker.cpp b/src/typechecker.cpp index 61e349d..a047a4a 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -111,7 +111,7 @@ namespace AST { std::shared_ptr BinaryOperationExpression::typecheck( typecheck::State& state, typecheck::Scope& scope, - std::optional> + std::optional> expected_ty ) { auto lhs_ty = this->m_lhs->typecheck(state, scope, {}); auto rhs_ty = this->m_rhs->typecheck(state, scope, {}); @@ -119,9 +119,12 @@ namespace AST { if (this->m_binop == types::BinOp::Assignment) { // Re-typecheck rhs to actually match lhs auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty); + auto rhs_ty_res = check_type(state, rhs_ty, lhs_ty); + this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state); return lhs_ty; } + // Try to find a binop that matches exactly auto binop = types::find_binop( state.binops, lhs_ty, @@ -133,8 +136,53 @@ namespace AST { return binop->result; } - // TODO check for binops that may be implicitly castable + // If that fails, try to find binop that matches on one side perfectly + // and is castable on the other side, and would also be perfectly + // assignable to the expected value. + for (auto& binop : state.binops) { + if (expected_ty) { + // Skip any binops that would not be immediately assignable to + // the expected type + if (!types::types_equal(binop.result, *expected_ty)) { + continue; + } + } + if (types::types_equal(binop.lhs, lhs_ty)) { + auto rhs_res = check_type(state, rhs_ty, binop.rhs); + if (!rhs_res.ok()) + // Skip if not implicitly castable to lhs + continue; + this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state); + return binop.result; + } + else if (types::types_equal(binop.rhs, rhs_ty)) { + auto lhs_res = check_type(state, lhs_ty, binop.lhs); + if (!lhs_res.ok()) + // Skip if not implicitly castable to rhs + continue; + this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state); + return binop.result; + } + } + // Finally check for any binop that allows the result to be implicitly + // casted to the result + for (auto& binop : state.binops) { + if (expected_ty) { + // Skip any binops that would not even be implicitly castable to + // the expected result + auto result_res = check_type(state, binop.result, *expected_ty); + if (!result_res.ok()) + continue; + } + auto lhs_result = check_type(state, lhs_ty, binop.lhs); + auto rhs_result = check_type(state, rhs_ty, binop.rhs); + this->m_lhs = handle_res(std::move(this->m_lhs), lhs_result, state); + this->m_rhs = handle_res(std::move(this->m_rhs), lhs_result, state); + return binop.result; + } + + // No suitable binops found :( state.errors.push_back(CompileError( "No suitable binop between " + lhs_ty->formatted() + " "