From 9f2c5ad9e3e7f0bc22ee53a3e0ec4f447732cd57 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 16 May 2024 21:01:11 +0200 Subject: [PATCH] Type check pointer dereferencing --- TODO | 1 + cli/cl.cpp | 4 +- include/elna/source/parser.hpp | 2 + include/elna/source/result.hpp | 27 +++++++++++ include/elna/source/semantic.hpp | 39 +++++++++++++++- include/elna/source/types.hpp | 27 ++++++++++- source/result.cpp | 12 ++++- source/semantic.cpp | 77 ++++++++++++++++++++++++++++++-- source/types.cpp | 5 +++ 9 files changed, 185 insertions(+), 9 deletions(-) diff --git a/TODO b/TODO index 5dbf403..2bc1002 100644 --- a/TODO +++ b/TODO @@ -12,6 +12,7 @@ - Syscalls. - Error message with an empty file wrongly says that a ")" is expected. - Support any expressions for constants. +- Name analysis should fail if there are undefined symbols. # Toolchain - Try to guess the build platform (x86_64-slackware-linux is hard coded). diff --git a/cli/cl.cpp b/cli/cl.cpp index d25c634..0c95133 100644 --- a/cli/cl.cpp +++ b/cli/cl.cpp @@ -53,8 +53,8 @@ namespace elna::cli return 2; } auto global_scope = add_builtin_symbols(); - source::name_analysis_visitor(global_scope).visit(ast.get()); - source::type_analysis_visitor().visit(ast.get()); + source::name_analysis_visitor(global_scope, in_file).visit(ast.get()); + source::type_analysis_visitor(global_scope, in_file, 4).visit(ast.get()); source::allocator_visitor(global_scope).visit(ast.get()); source::intermediate_code_generator intermediate_code_generator{ global_scope }; diff --git a/include/elna/source/parser.hpp b/include/elna/source/parser.hpp index 5410846..d32d078 100644 --- a/include/elna/source/parser.hpp +++ b/include/elna/source/parser.hpp @@ -3,6 +3,7 @@ #include #include #include +#include "elna/source/types.hpp" namespace elna::source { @@ -173,6 +174,7 @@ namespace elna::source { public: std::shared_ptr place; + std::shared_ptr data_type; protected: /** diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp index 4ba775a..d735945 100644 --- a/include/elna/source/result.hpp +++ b/include/elna/source/result.hpp @@ -7,6 +7,7 @@ #include #include #include +#include "elna/source/types.hpp" namespace elna::source { @@ -123,6 +124,32 @@ namespace elna::source std::string what() const override; }; + struct type_mismatch final : public error + { + /** + * Kind of the operation on the type. + */ + enum class operation + { + dereference, + }; + + /** + * \param name Given type. + * \param kind Kind of the operation on the type. + * \param path Source file name. + * \param position Operation position. + */ + type_mismatch(std::shared_ptr got, operation kind, const std::filesystem::path& path, + const struct position position); + + std::string what() const override; + + private: + std::shared_ptr got; + operation kind; + }; + template struct writer { diff --git a/include/elna/source/semantic.hpp b/include/elna/source/semantic.hpp index 6e6e42b..66c37dd 100644 --- a/include/elna/source/semantic.hpp +++ b/include/elna/source/semantic.hpp @@ -7,12 +7,23 @@ namespace elna::source { class name_analysis_visitor final : public empty_visitor { - std::shared_ptr table = std::make_shared(); + std::shared_ptr table; + const std::filesystem::path filename; + std::list> m_errors; std::shared_ptr convert_declaration_type(const type_expression& ast_type) const; public: - name_analysis_visitor(std::shared_ptr table); + /** + * \param table Symbol table. + * \param path Source filename. + */ + name_analysis_visitor(std::shared_ptr table, const std::filesystem::path& filename); + + /** + * \return Collected errors. + */ + const std::list>& errors() const noexcept; void visit(constant_definition *definition) override; void visit(declaration *declaration) override; @@ -44,5 +55,29 @@ namespace elna::source */ class type_analysis_visitor final : public empty_visitor { + std::shared_ptr table; + const std::filesystem::path filename; + const std::size_t pointer_size; + std::list> m_errors; + + public: + /** + * \param table Symbol table. + * \param path Source filename. + * \param target_pointer_size Pointer size on the target platform. + */ + type_analysis_visitor(std::shared_ptr table, const std::filesystem::path& filename, + std::size_t target_pointer_size); + + /** + * \return Collected errors. + */ + const std::list>& errors() const noexcept; + + void visit(program *program) override; + void visit(procedure_definition *definition) override; + void visit(integer_literal *literal) override; + void visit(boolean_literal *literal) override; + void visit(unary_expression *expression) override; }; } diff --git a/include/elna/source/types.hpp b/include/elna/source/types.hpp index 2e7043d..132b92c 100644 --- a/include/elna/source/types.hpp +++ b/include/elna/source/types.hpp @@ -8,12 +8,23 @@ namespace elna::source /** * Type representation. */ - struct type + class type { const std::size_t byte_size; protected: + /** + * Constructor. + * + * \param byte_size The type size in bytes. + */ explicit type(const std::size_t byte_size); + + public: + /** + * \return The type size in bytes. + */ + virtual std::size_t size() const noexcept; }; /** @@ -21,8 +32,15 @@ namespace elna::source */ struct primitive_type : public type { + /// Type name. const std::string type_name; + /** + * Constructor. + * + * \param type_name Type name. + * \param byte_size The type size in bytes. + */ primitive_type(const std::string& type_name, const std::size_t byte_size); }; @@ -31,8 +49,15 @@ namespace elna::source */ struct pointer_type : public type { + /// Pointer target type. std::shared_ptr base_type; + /** + * Constructor. + * + * \param base_type Pointer target type. + * \param byte_size The type size in bytes. + */ pointer_type(std::shared_ptr base_type, const std::size_t byte_size); }; diff --git a/source/result.cpp b/source/result.cpp index 3740254..f79e85f 100644 --- a/source/result.cpp +++ b/source/result.cpp @@ -1,5 +1,4 @@ #include "elna/source/result.hpp" -#include "elna/source/types.hpp" namespace elna::source { @@ -33,4 +32,15 @@ namespace elna::source { return "Name '" + name + "' was already defined"; } + + type_mismatch::type_mismatch(std::shared_ptr got, operation kind, const std::filesystem::path& path, + const struct position position) + : error(path, position), kind(kind), got(got) + { + } + + std::string type_mismatch::what() const + { + return "Type cannot be used here."; + } } diff --git a/source/semantic.cpp b/source/semantic.cpp index 2b694ef..525e636 100644 --- a/source/semantic.cpp +++ b/source/semantic.cpp @@ -1,11 +1,12 @@ #include "elna/source/semantic.hpp" -#include "elna/source/types.hpp" +#include "elna/source/result.hpp" #include namespace elna::source { - name_analysis_visitor::name_analysis_visitor(std::shared_ptr table) - : table(table) + name_analysis_visitor::name_analysis_visitor(std::shared_ptr table, + const std::filesystem::path& filename) + : table(table), filename(filename) { } @@ -63,6 +64,11 @@ namespace elna::source this->table = info->scope()->scope(); } + const std::list>& name_analysis_visitor::errors() const noexcept + { + return m_errors; + } + allocator_visitor::allocator_visitor(std::shared_ptr table) : table(table) { @@ -115,4 +121,69 @@ namespace elna::source this->argument_offset = std::max(static_cast(this->argument_offset), call_info->parameter_stack_size()); } + + type_analysis_visitor::type_analysis_visitor(std::shared_ptr table, + const std::filesystem::path& filename, std::size_t target_pointer_size) + : table(table), filename(filename), pointer_size(target_pointer_size) + { + } + + void type_analysis_visitor::visit(program *program) + { + for (auto& definition : program->definitions()) + { + if (dynamic_cast(definition.get()) != nullptr) + { + definition->accept(this); + } + } + program->body().accept(this); + } + + void type_analysis_visitor::visit(procedure_definition *definition) + { + definition->body().accept(this); + } + + void type_analysis_visitor::visit(integer_literal *literal) + { + literal->data_type = std::dynamic_pointer_cast(table->lookup("Int"))->type(); + } + + void type_analysis_visitor::visit(boolean_literal *literal) + { + literal->data_type = std::dynamic_pointer_cast(table->lookup("Boolean"))->type(); + } + + void type_analysis_visitor::visit(unary_expression *expression) + { + empty_visitor::visit(expression); + + switch (expression->operation()) + { + case unary_operator::reference: + expression->data_type = std::make_shared(expression->operand().data_type, + this->pointer_size); + break; + case unary_operator::dereference: + auto operand_type = expression->operand().data_type; + + if (auto referenced_type = std::dynamic_pointer_cast(operand_type)) + { + expression->data_type = referenced_type; + } + else if (operand_type != nullptr) + { + auto new_error = std::make_unique(operand_type, + type_mismatch::operation::dereference, this->filename, expression->position()); + m_errors.push_back(std::move(new_error)); + } + break; + } + } + + const std::list>& type_analysis_visitor::errors() const noexcept + { + return m_errors; + } } diff --git a/source/types.cpp b/source/types.cpp index 1daa363..af53626 100644 --- a/source/types.cpp +++ b/source/types.cpp @@ -7,6 +7,11 @@ namespace elna::source { } + std::size_t type::size() const noexcept + { + return this->byte_size; + } + primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size) : type(byte_size), type_name(type_name) {