From fa01b22b816bce62844a2432bcd7995f0895b323 Mon Sep 17 00:00:00 2001 From: Sofia Date: Mon, 13 Apr 2026 21:08:57 +0300 Subject: [PATCH] Add compilation of casts --- CMakeLists.txt | 1 + src/ast.cpp | 7 +++++++ src/ast.h | 23 +++++++++++++++++++++++ src/casting.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/casting.h | 23 +++++++++++++++++++++++ src/codegen.cpp | 14 +++++++++++++- src/codegen.h | 2 ++ src/main.cpp | 2 ++ src/typechecker.cpp | 19 +++++++++++++++++++ src/typechecker.h | 2 ++ test.c | 2 +- 11 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 src/casting.cpp create mode 100644 src/casting.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a281ec6..ab26fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(${PROJECT_NAME} src/types.cpp src/typechecker.cpp src/binops.cpp + src/casting.cpp ) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20) target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weffc++ -Wextra -Wpedantic -Werror) diff --git a/src/ast.cpp b/src/ast.cpp index 6ca32d1..484879e 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -42,6 +42,13 @@ namespace AST { return out.str(); } + std::string CastExpression::formatted() { + std::stringstream out{ "" }; + out << "(" << this->m_ty->formatted() << ") "; + out << this->m_expr->formatted(); + return out.str(); + } + std::string ExpressionStatement::formatted() { std::stringstream out{ "" }; out << this->m_expr->formatted(); diff --git a/src/ast.h b/src/ast.h index ad0c410..a5234b7 100644 --- a/src/ast.h +++ b/src/ast.h @@ -140,6 +140,29 @@ namespace AST { ) override; }; + class CastExpression : public Expression { + private: + std::shared_ptr m_ty; + std::unique_ptr m_expr; + public: + CastExpression( + token::Metadata meta, + std::shared_ptr type, + std::unique_ptr expr) + : Expression{ meta } + , m_ty{ type } + , m_expr{ std::move(expr) } { + } + virtual ~CastExpression() override = default; + virtual std::string formatted() override; + virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override; + virtual std::shared_ptr typecheck( + typecheck::State& state, + typecheck::Scope& scope, + std::optional> expected_ty + ) override; + }; + class ReturnStatement : public Statement { private: diff --git a/src/casting.cpp b/src/casting.cpp new file mode 100644 index 0000000..9c43c86 --- /dev/null +++ b/src/casting.cpp @@ -0,0 +1,41 @@ + +#include "casting.h" + +namespace types { + std::vector create_casts() { + std::vector casts{}; + + auto int_ty = std::shared_ptr{ + new FundamentalType{ FundamentalTypeKind::Int } }; + auto char_ty = std::shared_ptr{ + new FundamentalType{ FundamentalTypeKind::Char } }; + auto bool_ty = std::shared_ptr{ + new FundamentalType{ FundamentalTypeKind::Bool } }; + + for (auto& source_ty : { int_ty, char_ty, bool_ty }) { + for (auto& target_ty : { int_ty, char_ty, bool_ty }) { + casts.push_back(CastDefinition{ source_ty, target_ty, false, + [](codegen::Builder& builder, std::shared_ptr target, llvm::Value* value) { + return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder), "cast"); + } }); + } + } + + return casts; + } + + + std::optional find_cast( + std::vector& casts, + std::shared_ptr casted_ty, + std::shared_ptr target_ty) { + + for (auto& cast : casts) { + if (types_equal(cast.casted_ty, casted_ty) && types_equal(cast.target_ty, target_ty)) { + return cast; + } + } + return {}; + + } +} \ No newline at end of file diff --git a/src/casting.h b/src/casting.h new file mode 100644 index 0000000..c2d2715 --- /dev/null +++ b/src/casting.h @@ -0,0 +1,23 @@ +#ifndef CASTING_H +#define CASTING_H + +#include "types.h" + +namespace types { + struct CastDefinition { + std::shared_ptr casted_ty; + std::shared_ptr target_ty; + bool allow_implicit; + + llvm::Value* (*codegen)(codegen::Builder& builder, std::shared_ptr target, llvm::Value* value); + }; + + std::vector create_casts(); + + std::optional find_cast( + std::vector& casts, + std::shared_ptr casted_ty, + std::shared_ptr target_ty); +} + +#endif \ No newline at end of file diff --git a/src/codegen.cpp b/src/codegen.cpp index e596281..b2f536a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -11,7 +11,7 @@ namespace codegen { Scope Scope::with_lvalue() { - return Scope{ this->binops, this->values, true }; + return Scope{ this->binops, this->casts, this->values, true }; } } @@ -99,6 +99,18 @@ namespace AST { }; } + codegen::StackValue CastExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) { + auto expr = this->m_expr->codegen(builder, scope); + auto cast = types::find_cast(scope.casts, expr.ty, this->m_ty); + if (cast) { + return codegen::StackValue{ + cast->codegen(builder, cast->target_ty, expr.value), + cast->target_ty + }; + } + return expr; + } + void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) { if (!builder.block) return; diff --git a/src/codegen.h b/src/codegen.h index 1bbdba2..f15989c 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -9,6 +9,7 @@ #include "builder.h" #include "types.h" #include "binops.h" +#include "casting.h" #include "tokens.h" namespace codegen { @@ -19,6 +20,7 @@ namespace codegen { struct Scope { std::vector& binops; + std::vector& casts; std::map values; bool is_lvalue; diff --git a/src/main.cpp b/src/main.cpp index 79eea6a..b0a6c99 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -99,6 +99,7 @@ std::optional compile(std::string_view in_filename) { typecheck::State typecheck_state{}; typecheck_state.binops = types::create_binops(); + typecheck_state.casts = types::create_casts(); typecheck::Scope typecheck_scope{}; for (auto& tls : statements) { @@ -118,6 +119,7 @@ std::optional compile(std::string_view in_filename) { codegen::Scope cg_scope{ .binops = typecheck_state.binops, + .casts = typecheck_state.casts, .values = {}, .is_lvalue = false, }; diff --git a/src/typechecker.cpp b/src/typechecker.cpp index e0cc6a4..91a4203 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -166,6 +166,25 @@ namespace AST { return fn_ty->m_ret_ty; } + + std::shared_ptr CastExpression::typecheck( + typecheck::State& state, + typecheck::Scope& scope, + std::optional> + ) { + auto expr_ty = this->m_expr->typecheck(state, scope, {}); + auto cast = types::find_cast(state.casts, expr_ty, this->m_ty); + if (cast) { + return cast->target_ty; + } + state.errors.push_back(CompileError("Cast from type " + + expr_ty->formatted() + "to type " + this->m_ty->formatted() + + " is not permitted", this->m_meta)); + return std::shared_ptr { new types::FundamentalType{ + types::FundamentalTypeKind::Void + } }; + } + void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) { auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty); if (scope.return_ty) { diff --git a/src/typechecker.h b/src/typechecker.h index e234aea..089f79c 100644 --- a/src/typechecker.h +++ b/src/typechecker.h @@ -5,6 +5,7 @@ #include "types.h" #include "binops.h" +#include "casting.h" #include "errors.h" namespace typecheck { @@ -15,6 +16,7 @@ namespace typecheck { struct State { std::vector binops; + std::vector casts; std::vector errors; }; } diff --git a/test.c b/test.c index c12287e..f0f797c 100644 --- a/test.c +++ b/test.c @@ -9,5 +9,5 @@ int fibonacci(int n) { int main() { printf("10th fibonacci number is %d!", fibonacci(10)); char res = 0; - return res; + return (int)res; } \ No newline at end of file