From d57934973d93e90ccf8ed4812febd263b2a544de Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 25 Mar 2025 11:29:29 +0100 Subject: [PATCH] Type check the return statement --- boot/ast.cc | 191 ++++++++++++------------------ boot/parser.yy | 7 +- gcc/elna-generic.cc | 38 ++++-- include/elna/boot/ast.h | 199 +++++++------------------------- include/elna/gcc/elna-generic.h | 2 +- 5 files changed, 152 insertions(+), 285 deletions(-) diff --git a/boot/ast.cc b/boot/ast.cc index 89a1ad0..89ee549 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -33,14 +33,6 @@ namespace elna::boot return this->source_position; } - statement::statement() - { - } - - statement::~statement() - { - } - assign_statement *statement::is_assign() { return nullptr; @@ -71,14 +63,6 @@ namespace elna::boot return nullptr; } - expression::expression() - { - } - - expression::~expression() - { - } - cast_expression *expression::is_cast() { return nullptr; @@ -114,39 +98,6 @@ namespace elna::boot return nullptr; } - void expression::accept(parser_visitor *visitor) - { - if (cast_expression *node = is_cast()) - { - return node->accept(visitor); - } - else if (traits_expression *node = is_traits()) - { - return node->accept(visitor); - } - else if (binary_expression *node = is_binary()) - { - return node->accept(visitor); - } - else if (unary_expression *node = is_unary()) - { - return node->accept(visitor); - } - else if (designator_expression *node = is_designator()) - { - return node->accept(visitor); - } - else if (procedure_call *node = is_call_expression()) - { - return node->accept(visitor); - } - else if (literal_expression *node = is_literal()) - { - return node->accept(visitor); - } - __builtin_unreachable(); - } - type_expression::type_expression(const struct position position) : node(position) { @@ -182,35 +133,6 @@ namespace elna::boot return nullptr; } - void type_expression::accept(parser_visitor *visitor) - { - if (std::shared_ptr node = is_primitive()) - { - return node->accept(visitor); - } - else if (std::shared_ptr node = is_array()) - { - return node->accept(visitor); - } - else if (std::shared_ptr node = is_pointer()) - { - return node->accept(visitor); - } - else if (std::shared_ptr node = is_record()) - { - return node->accept(visitor); - } - else if (std::shared_ptr node = is_union()) - { - return node->accept(visitor); - } - else if (std::shared_ptr node = is_procedure()) - { - return node->accept(visitor); - } - __builtin_unreachable(); - } - primitive_type_expression::primitive_type_expression(const struct position position, const std::string& name) : type_expression(position), name(name) { @@ -301,8 +223,8 @@ namespace elna::boot } variable_declaration::variable_declaration(const struct position position, const std::string& identifier, - std::shared_ptr type, const bool exported) - : definition(position, identifier, exported), m_type(type) + std::shared_ptr variable_type, const bool exported) + : definition(position, identifier, exported), m_variable_type(variable_type) { } @@ -313,7 +235,7 @@ namespace elna::boot type_expression& variable_declaration::variable_type() { - return *m_type; + return *m_variable_type; } definition::definition(const struct position position, const std::string& identifier, const bool exported) @@ -462,47 +384,16 @@ namespace elna::boot return this; } - void literal_expression::accept(parser_visitor *visitor) - { - if (literal *node = is_int()) - { - return node->accept(visitor); - } - else if (literal *node = is_word()) - { - return node->accept(visitor); - } - else if (literal *node = is_float()) - { - return node->accept(visitor); - } - else if (literal *node = is_bool()) - { - return node->accept(visitor); - } - else if (literal *node = is_char()) - { - return node->accept(visitor); - } - else if (literal *node = is_nil()) - { - return node->accept(visitor); - } - else if (literal *node = is_string()) - { - return node->accept(visitor); - } - else - { - __builtin_unreachable(); - } - } - defer_statement::defer_statement(const struct position position) : node(position) { } + void defer_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + defer_statement *defer_statement::is_defer() { return this; @@ -529,11 +420,37 @@ namespace elna::boot return this; } + void designator_expression::accept(parser_visitor *visitor) + { + if (variable_expression *node = is_variable()) + { + return visitor->visit(node); + } + else if (array_access_expression *node = is_array_access()) + { + return visitor->visit(node); + } + else if (field_access_expression *node = is_field_access()) + { + return visitor->visit(node); + } + else if (dereference_expression *node = is_dereference()) + { + return visitor->visit(node); + } + __builtin_unreachable(); + } + variable_expression::variable_expression(const struct position position, const std::string& name) : node(position), name(name) { } + void variable_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + variable_expression *variable_expression::is_variable() { return this; @@ -545,6 +462,11 @@ namespace elna::boot { } + void array_access_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + expression& array_access_expression::index() { return *m_index; @@ -572,6 +494,11 @@ namespace elna::boot { } + void field_access_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + expression& field_access_expression::base() { return *m_base; @@ -598,6 +525,11 @@ namespace elna::boot { } + void dereference_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + expression& dereference_expression::base() { return *m_base; @@ -686,6 +618,11 @@ namespace elna::boot { } + void procedure_call::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + procedure_call *procedure_call::is_call_statement() { return this; @@ -780,6 +717,11 @@ namespace elna::boot { } + void return_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + return_statement *return_statement::is_return() { return this; @@ -801,6 +743,11 @@ namespace elna::boot { } + void assign_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + assign_statement *assign_statement::is_assign() { return this; @@ -847,6 +794,11 @@ namespace elna::boot { } + void if_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + if_statement *if_statement::is_if() { return this; @@ -877,6 +829,11 @@ namespace elna::boot { } + void while_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + while_statement *while_statement::is_while() { return this; diff --git a/boot/parser.yy b/boot/parser.yy index e0dbfaf..bdd629e 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -286,10 +286,15 @@ if_statement: $$ = new boot::if_statement(boot::make_position(@1), then, _else); std::swap($5, $$->branches); } -return_statement: "return" expression +return_statement: + "return" expression { $$ = new boot::return_statement(boot::make_position(@1), $2); } + | "return" + { + $$ = new boot::return_statement(boot::make_position(@1)); + } defer_statement: DEFER statements "end" { $$ = new boot::defer_statement(boot::make_position(@1)); diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 284ac5d..ff8c836 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -1225,18 +1225,42 @@ namespace elna::gcc void generic_visitor::visit(boot::return_statement *statement) { boot::expression *return_expression = statement->return_expression(); + location_t statement_position = get_location(&statement->position()); + tree set_result{ NULL_TREE }; + tree return_type = TREE_TYPE(TREE_TYPE(current_function_decl)); - if (return_expression == nullptr) + if (TREE_THIS_VOLATILE(current_function_decl) == 1) { + error_at(statement_position, "This procedure is not allowed to return"); return; } - return_expression->accept(this); - - tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl), - this->current_expression); - tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); - append_statement(return_stmt); + if (return_expression != nullptr) + { + return_expression->accept(this); + set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl), + this->current_expression); + } + if (return_type == void_type_node && set_result != NULL_TREE) + { + error_at(statement_position, "Proper procedure is not allowed to return a value"); + } + else if (return_type != void_type_node && set_result == NULL_TREE) + { + error_at(statement_position, "Procedure is expected to return a value of type '%s'", + print_type(return_type).c_str()); + } + else if (return_type != void_type_node && !is_assignable_from(return_type, this->current_expression)) + { + error_at(statement_position, "Cannot return '%s' from a procedure returning '%s'", + print_type(return_type).c_str(), + print_type(TREE_TYPE(this->current_expression)).c_str()); + } + else + { + tree return_stmt = build1_loc(statement_position, RETURN_EXPR, void_type_node, set_result); + append_statement(return_stmt); + } this->current_expression = NULL_TREE; } diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 59de3fc..a3ef2ca 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -138,6 +138,7 @@ namespace elna::boot explicit node(const position position); public: + virtual void accept(parser_visitor *visitor) = 0; virtual ~node() = 0; /** @@ -155,41 +156,6 @@ namespace elna::boot virtual return_statement *is_return(); virtual defer_statement *is_defer(); virtual procedure_call *is_call_statement(); - - template - void accept(V *visitor) - { - if (assign_statement *node = is_assign()) - { - return visitor->visit(node); - } - else if (if_statement *node = is_if()) - { - return visitor->visit(node); - } - else if (while_statement *node = is_while()) - { - return visitor->visit(node); - } - else if (return_statement *node = is_return()) - { - return visitor->visit(node); - } - else if (defer_statement *node = is_defer()) - { - return visitor->visit(node); - } - else if (procedure_call *node = is_call_statement()) - { - return visitor->visit(node); - } - __builtin_unreachable(); - } - - ~statement() = 0; - - protected: - statement(); }; class expression : public virtual node @@ -202,12 +168,6 @@ namespace elna::boot virtual designator_expression *is_designator(); virtual procedure_call *is_call_expression(); virtual literal_expression *is_literal(); - - void accept(parser_visitor *visitor); - ~expression() = 0; - - protected: - expression(); }; /** @@ -236,8 +196,6 @@ namespace elna::boot virtual std::shared_ptr is_union(); virtual std::shared_ptr is_procedure(); - void accept(parser_visitor *visitor); - protected: type_expression(const struct position position); }; @@ -251,7 +209,7 @@ namespace elna::boot const std::string name; primitive_type_expression(const struct position position, const std::string& name); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; std::shared_ptr is_primitive() override; }; @@ -264,7 +222,7 @@ namespace elna::boot array_type_expression(const struct position position, std::shared_ptr base, const std::uint32_t size); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; std::shared_ptr is_array() override; type_expression& base(); @@ -276,7 +234,7 @@ namespace elna::boot public: pointer_type_expression(const struct position position, std::shared_ptr base); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; std::shared_ptr is_pointer() override; type_expression& base(); @@ -291,7 +249,7 @@ namespace elna::boot record_type_expression(const struct position position, std::vector&& fields); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; std::shared_ptr is_record() override; }; @@ -302,7 +260,7 @@ namespace elna::boot union_type_expression(const struct position position, std::vector&& fields); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; std::shared_ptr is_union() override; }; @@ -311,12 +269,12 @@ namespace elna::boot */ class variable_declaration : public definition { - std::shared_ptr m_type; + std::shared_ptr m_variable_type; public: variable_declaration(const struct position position, const std::string& identifier, - std::shared_ptr type, const bool exported = false); - void accept(parser_visitor *visitor); + std::shared_ptr variable_type, const bool exported = false); + void accept(parser_visitor *visitor) override; type_expression& variable_type(); }; @@ -336,7 +294,6 @@ namespace elna::boot virtual literal *is_string() = 0; literal_expression *is_literal() override; - void accept(parser_visitor *visitor); protected: literal_expression(); @@ -350,14 +307,9 @@ namespace elna::boot literal_expression *m_body; public: - /** - * \param position Source code position. - * \param identifier Constant name. - * \param body Constant value. - */ constant_definition(const struct position position, const std::string& identifier, const bool exported, literal_expression *body); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; literal_expression& body(); @@ -389,7 +341,7 @@ namespace elna::boot procedure_type_expression(const struct position position, return_declaration return_type = return_declaration()); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; std::shared_ptr is_procedure() override; }; @@ -406,7 +358,7 @@ namespace elna::boot procedure_definition(const struct position position, const std::string& identifier, const bool exported, std::shared_ptr heading, block *body = nullptr); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; procedure_type_expression& heading(); @@ -423,7 +375,7 @@ namespace elna::boot public: type_definition(const struct position position, const std::string& identifier, const bool exported, std::shared_ptr expression); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; type_expression& body(); }; @@ -438,7 +390,7 @@ namespace elna::boot public: cast_expression(const struct position position, std::shared_ptr target, expression *value); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; cast_expression *is_cast() override; type_expression& target(); @@ -454,7 +406,7 @@ namespace elna::boot const std::string name; traits_expression(const struct position position, const std::string& name); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; traits_expression *is_traits() override; }; @@ -480,17 +432,12 @@ namespace elna::boot expression *m_return_expression{ nullptr }; public: - return_statement(const struct position position, expression *return_expression); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + return_statement(const struct position position, expression *return_expression = nullptr); + void accept(parser_visitor *visitor) override; + virtual return_statement *is_return() override; expression *return_expression(); - virtual return_statement *is_return() override; virtual ~return_statement() override; }; @@ -502,29 +449,8 @@ namespace elna::boot virtual field_access_expression *is_field_access(); virtual dereference_expression *is_dereference(); - template - void accept(V *visitor) - { - if (variable_expression *node = is_variable()) - { - return visitor->visit(node); - } - else if (array_access_expression *node = is_array_access()) - { - return visitor->visit(node); - } - else if (field_access_expression *node = is_field_access()) - { - return visitor->visit(node); - } - else if (dereference_expression *node = is_dereference()) - { - return visitor->visit(node); - } - __builtin_unreachable(); - } - designator_expression *is_designator() override; + void accept(parser_visitor *visitor); ~designator_expression() = 0; protected: @@ -537,12 +463,7 @@ namespace elna::boot const std::string name; variable_expression(const struct position position, const std::string& name); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; variable_expression *is_variable() override; }; @@ -554,12 +475,7 @@ namespace elna::boot public: array_access_expression(const struct position position, expression *base, expression *index); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; expression& base(); expression& index(); @@ -577,12 +493,7 @@ namespace elna::boot public: field_access_expression(const struct position position, expression *base, const std::string& field); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; expression& base(); std::string& field(); @@ -598,16 +509,12 @@ namespace elna::boot public: dereference_expression(const struct position position, expression *base); - - template - void accept(parser_visitor *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; expression& base(); dereference_expression *is_dereference() override; + ~dereference_expression() override; }; @@ -622,17 +529,12 @@ namespace elna::boot std::vector arguments; procedure_call(const struct position position, designator_expression *callable); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; + virtual procedure_call *is_call_statement() override; + virtual procedure_call *is_call_expression() override; designator_expression& callable(); - virtual procedure_call *is_call_statement() override; - virtual procedure_call *is_call_expression() override; virtual ~procedure_call() override; }; @@ -649,18 +551,13 @@ namespace elna::boot */ assign_statement(const struct position position, designator_expression *lvalue, expression *rvalue); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; designator_expression& lvalue(); expression& rvalue(); - assign_statement *is_assign() override; virtual ~assign_statement() override; + assign_statement *is_assign() override; }; /** @@ -676,17 +573,12 @@ namespace elna::boot if_statement(const struct position position, conditional_statements *body, std::vector *alternative = nullptr); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; + virtual if_statement *is_if() override; conditional_statements& body(); std::vector *alternative(); - virtual if_statement *is_if() override; virtual ~if_statement() override; }; @@ -700,16 +592,11 @@ namespace elna::boot public: std::vector branches; while_statement(const struct position position, conditional_statements *body); - - template - void accept(V *visitor) - { - visitor->visit(this); - } + void accept(parser_visitor *visitor) override; + while_statement *is_while() override; conditional_statements& body(); - while_statement *is_while() override; virtual ~while_statement() override; }; @@ -721,7 +608,7 @@ namespace elna::boot std::vector body; block(const struct position position); - void accept(parser_visitor *visitor); + virtual void accept(parser_visitor *visitor) override; virtual ~block() override; }; @@ -733,7 +620,7 @@ namespace elna::boot std::vector procedures; program(const struct position position); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; virtual ~program() override; }; @@ -833,8 +720,7 @@ namespace elna::boot } } - template - void accept(V *visitor) + void accept(parser_visitor *visitor) override { visitor->visit(this); } @@ -846,14 +732,9 @@ namespace elna::boot std::vector statements; defer_statement(const struct position position); - - template - void accept(V *visitor) - { - visitor->visit(this); - } - + void accept(parser_visitor *visitor) override; defer_statement *is_defer() override; + virtual ~defer_statement() override; }; @@ -867,7 +748,7 @@ namespace elna::boot binary_expression(const struct position position, expression *lhs, expression *rhs, const binary_operator operation); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; binary_expression *is_binary() override; expression& lhs(); @@ -886,7 +767,7 @@ namespace elna::boot unary_expression(const struct position position, expression *operand, const unary_operator operation); - void accept(parser_visitor *visitor); + void accept(parser_visitor *visitor) override; unary_expression *is_unary() override; expression& operand(); diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 1a85f68..e9b2db0 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -68,7 +68,7 @@ namespace elna::gcc void visit_statements(const std::vector& statements); public: - generic_visitor(std::shared_ptr symbol_table); + explicit generic_visitor(std::shared_ptr symbol_table); void visit(boot::program *program) override; void visit(boot::procedure_definition *definition) override;