From 0ea13482bfbe67e19df1369df7a4afed3879ab45 Mon Sep 17 00:00:00 2001 From: Sofia Date: Sun, 10 May 2026 19:56:09 +0300 Subject: [PATCH] Implement assignment binops a little differently --- src/binops.cpp | 26 +++++++++--- src/binops.h | 1 + src/codegen.cpp | 28 +++++------- src/typechecker.cpp | 101 ++++++++++++++++++++++++++++++++------------ test.c | 2 - 5 files changed, 107 insertions(+), 51 deletions(-) diff --git a/src/binops.cpp b/src/binops.cpp index 257daab..6af608a 100644 --- a/src/binops.cpp +++ b/src/binops.cpp @@ -35,14 +35,14 @@ namespace types { bool_ty }) { definitions.push_back(BinopDefinition{ - ty, types::BinOp::Add, ty, ty, + ty, types::BinOp::Add, ty, ty, false, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) { return builder.builder->CreateAdd(lhs, rhs, "add"); }, }); definitions.push_back(BinopDefinition{ - ty, types::BinOp::Sub, ty, ty, + ty, types::BinOp::Sub, ty, ty, false, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) { return builder.builder->CreateSub(lhs, rhs, "sub"); }, @@ -54,14 +54,14 @@ namespace types { short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty, }) { definitions.push_back(BinopDefinition{ - ty, types::BinOp::LessThan, ty, bool_ty, + ty, types::BinOp::LessThan, ty, bool_ty, false, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) { return builder.builder->CreateICmpSLT(lhs, rhs, "icmpslt"); }, }); definitions.push_back(BinopDefinition{ - ty, types::BinOp::GreaterThan, ty, bool_ty, + ty, types::BinOp::GreaterThan, ty, bool_ty, false, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) { return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt"); }, @@ -74,14 +74,14 @@ namespace types { bool_ty }) { definitions.push_back(BinopDefinition{ - ty, types::BinOp::LessThan, ty, bool_ty, + ty, types::BinOp::LessThan, ty, bool_ty, false, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) { return builder.builder->CreateICmpULT(lhs, rhs, "icmpult"); }, }); definitions.push_back(BinopDefinition{ - ty, types::BinOp::GreaterThan, ty, bool_ty, + ty, types::BinOp::GreaterThan, ty, bool_ty, false, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) { return builder.builder->CreateICmpUGT(lhs, rhs, "icmpugt"); }, @@ -106,6 +106,20 @@ namespace types { return binop; } + + if (op == BinOp::Assignment) { + auto void_ty = std::shared_ptr{ + new types::FundamentalType{ false, types::FundamentalTypeKind::Void } };; + + return BinopDefinition{ + lhs, types::BinOp::Assignment, lhs, void_ty, true, + [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) { + builder.builder->CreateStore(rhs, lhs); + return llvm::dyn_cast(llvm::ConstantInt::get(llvm::IntegerType::get(*builder.context, 1), 0)); + }, + }; + } + return {}; } diff --git a/src/binops.h b/src/binops.h index 20c5301..ea4c33d 100644 --- a/src/binops.h +++ b/src/binops.h @@ -33,6 +33,7 @@ namespace types { BinOp op; std::shared_ptr rhs; std::shared_ptr result; + bool lhs_lvalue; llvm::Value* (*codegen)(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs); }; diff --git a/src/codegen.cpp b/src/codegen.cpp index d60d04d..ff00e2e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -135,24 +135,18 @@ namespace AST { auto lhs = this->m_lhs->codegen(builder, this->m_binop == types::BinOp::Assignment ? lvalued : scope, allocator); auto rhs = this->m_rhs->codegen(builder, scope, allocator); try { - switch (this->m_binop) { - case types::BinOp::Assignment: - builder.builder->CreateStore(rhs.value, lhs.value, false); - return rhs; - default: - auto binop = types::find_binop( - scope.binops, - lhs.ty, - this->m_binop, - rhs.ty); - if (binop) { - return codegen::StackValue{ - binop->codegen(builder, lhs.value, rhs.value), - binop->result - }; - } - throw CompileError("invalid binop", this->m_meta); + auto binop = types::find_binop( + scope.binops, + lhs.ty, + this->m_binop, + rhs.ty); + if (binop) { + return codegen::StackValue{ + binop->codegen(builder, lhs.value, rhs.value), + binop->result + }; } + throw CompileError("invalid binop", this->m_meta); } catch (std::runtime_error& error) { throw CompileError(error.what(), this->m_meta); diff --git a/src/typechecker.cpp b/src/typechecker.cpp index d3b102e..2210511 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -181,21 +181,6 @@ namespace AST { auto lhs_ty = lhs_res.type; auto rhs_ty = this->m_rhs->typecheck(state, scope, {}).type; - if (this->m_binop == types::BinOp::Assignment) { - if (!lhs_res.lvalue) { - state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); - } - else if (lhs_ty->m_const) { - state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); - } - - // Re-typecheck rhs to actually match lhs - auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty).type; - 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, false, false }; - } - // Try to find a binop that matches exactly auto binop = types::find_binop( state.binops, @@ -205,6 +190,24 @@ namespace AST { ); if (binop) { + if (binop->lhs_lvalue) { + if (!lhs_res.lvalue) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + else if (lhs_ty->m_const) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + } + + auto lhs_res = this->m_lhs->typecheck(state, scope, binop->lhs); + auto lhs_ty = lhs_res.type; + auto rhs_ty = this->m_rhs->typecheck(state, scope, binop->rhs).type; + + auto lhs_ty_res = check_type(state, lhs_ty, binop->lhs); + auto rhs_ty_res = check_type(state, rhs_ty, binop->rhs); + this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state); + this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state); + return { binop->result, false, false }; } @@ -212,6 +215,9 @@ namespace AST { // and is castable on the other side, and would also be perfectly // assignable to the expected value. for (auto& binop : state.binops) { + if (binop.op != this->m_binop) + continue; + if (expected_ty) { // Skip any binops that would not be immediately assignable to // the expected type @@ -225,6 +231,15 @@ namespace AST { // Skip if not implicitly castable to lhs continue; + if (binop.lhs_lvalue) { + if (!lhs_res.lvalue) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + else if (lhs_ty->m_const) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + } + rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type; rhs_res = check_type(state, rhs_ty, binop.rhs); @@ -232,14 +247,23 @@ namespace AST { return { binop.result, false, false }; } else if (types::types_equal(binop.rhs, rhs_ty)) { - auto lhs_res = check_type(state, lhs_ty, binop.lhs); - if (!lhs_res.ok()) + auto lhs_ty_res = check_type(state, lhs_ty, binop.lhs); + if (!lhs_ty_res.ok()) // Skip if not implicitly castable to rhs continue; + if (binop.lhs_lvalue) { + if (!lhs_res.lvalue) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + else if (lhs_ty->m_const) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + } + lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type; - lhs_res = check_type(state, lhs_ty, binop.lhs); - this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state); + lhs_ty_res = check_type(state, lhs_ty, binop.lhs); + this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state); return { binop.result, false, false }; } } @@ -247,6 +271,9 @@ namespace AST { // if that fails, accept binops that match the result type perfectly and // is able to cast both types successfully for (auto& binop : state.binops) { + if (binop.op != this->m_binop) + continue; + if (expected_ty) { // Skip any binops that would not be immediately assignable to // the expected type @@ -255,23 +282,35 @@ namespace AST { } } - auto rhs_res = check_type(state, rhs_ty, binop.rhs); - auto lhs_res = check_type(state, lhs_ty, binop.lhs); - if (!rhs_res.ok() || !lhs_res.ok()) + auto rhs_ty_res = check_type(state, rhs_ty, binop.rhs); + auto lhs_ty_res = check_type(state, lhs_ty, binop.lhs); + if (!rhs_ty_res.ok() || !lhs_ty_res.ok()) continue; + if (binop.lhs_lvalue) { + if (!lhs_res.lvalue) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + else if (lhs_ty->m_const) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + } + lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type; rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type; - lhs_res = check_type(state, lhs_ty, binop.lhs); - rhs_res = check_type(state, rhs_ty, binop.rhs); - this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state); - this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state); + lhs_ty_res = check_type(state, lhs_ty, binop.lhs); + rhs_ty_res = check_type(state, rhs_ty, binop.rhs); + this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state); + this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state); return { binop.result, false, false }; } // Finally check for any binop that allows the result to be implicitly // casted to the result for (auto& binop : state.binops) { + if (binop.op != this->m_binop) + continue; + if (expected_ty) { // Skip any binops that would not even be implicitly castable to // the expected result @@ -279,6 +318,16 @@ namespace AST { if (!result_res.ok()) continue; } + + if (binop.lhs_lvalue) { + if (!lhs_res.lvalue) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + else if (lhs_ty->m_const) { + state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta)); + } + } + lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type; rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type; auto lhs_result = check_type(state, lhs_ty, binop.lhs); diff --git a/test.c b/test.c index f7af84c..c6eaefe 100644 --- a/test.c +++ b/test.c @@ -72,8 +72,6 @@ long long int main() { } short int sh = 123 + 5; - long int lg = 456; - long long int longer = 789; return sh; } \ No newline at end of file