From 310a1213740523e43230b7cbe28271d3c2bb6a71 Mon Sep 17 00:00:00 2001
From: Eugen Wissner <belka@caraus.de>
Date: Tue, 25 Mar 2025 11:29:29 +0100
Subject: [PATCH] Type check the return statement

---
 boot/ast.cc                     | 191 ++++++++++++------------------
 boot/parser.yy                  |   7 +-
 boot/semantic.cc                | 157 ++++++++++++++++++-------
 gcc/elna-generic.cc             |  93 +++++++++++----
 gcc/elna1.cc                    |   6 +-
 include/elna/boot/ast.h         | 199 +++++++-------------------------
 include/elna/boot/semantic.h    |  32 ++---
 include/elna/gcc/elna-generic.h |   9 +-
 8 files changed, 330 insertions(+), 364 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<primitive_type_expression> node = is_primitive())
-        {
-            return node->accept(visitor);
-        }
-        else if (std::shared_ptr<array_type_expression> node = is_array())
-        {
-            return node->accept(visitor);
-        }
-        else if (std::shared_ptr<pointer_type_expression> node = is_pointer())
-        {
-            return node->accept(visitor);
-        }
-        else if (std::shared_ptr<record_type_expression> node = is_record())
-        {
-            return node->accept(visitor);
-        }
-        else if (std::shared_ptr<union_type_expression> node = is_union())
-        {
-            return node->accept(visitor);
-        }
-        else if (std::shared_ptr<procedure_type_expression> 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_expression> type, const bool exported)
-        : definition(position, identifier, exported), m_type(type)
+            std::shared_ptr<type_expression> 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<std::int32_t> *node = is_int())
-        {
-            return node->accept(visitor);
-        }
-        else if (literal<std::uint32_t> *node = is_word())
-        {
-            return node->accept(visitor);
-        }
-        else if (literal<double> *node = is_float())
-        {
-            return node->accept(visitor);
-        }
-        else if (literal<bool> *node = is_bool())
-        {
-            return node->accept(visitor);
-        }
-        else if (literal<unsigned char> *node = is_char())
-        {
-            return node->accept(visitor);
-        }
-        else if (literal<std::nullptr_t> *node = is_nil())
-        {
-            return node->accept(visitor);
-        }
-        else if (literal<std::string> *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/boot/semantic.cc b/boot/semantic.cc
index 7211103..9670449 100644
--- a/boot/semantic.cc
+++ b/boot/semantic.cc
@@ -65,6 +65,18 @@ namespace elna::boot
             auto info = std::make_shared<type_info>(type_info(type(unresolved.second)));
             this->symbols->enter(std::move(unresolved.first), info);
         }
+        for (variable_declaration *const variable : program->variables)
+        {
+            variable->accept(this);
+        }
+        for (procedure_definition *const procedure : program->procedures)
+        {
+            procedure->accept(this);
+        }
+        for (statement *const statement : program->body)
+        {
+            statement->accept(this);
+        }
     }
 
     void declaration_visitor::visit(type_definition *definition)
@@ -120,128 +132,191 @@ namespace elna::boot
     {
     }
 
-    void declaration_visitor::visit(variable_declaration *)
+    void declaration_visitor::visit(variable_declaration *declaration)
     {
-        __builtin_unreachable();
+        declaration->variable_type().accept(this);
     }
 
     void declaration_visitor::visit(constant_definition *)
     {
-        __builtin_unreachable();
     }
 
-    void declaration_visitor::visit(procedure_definition *)
+    void declaration_visitor::visit(procedure_definition *definition)
     {
-        __builtin_unreachable();
+        for (auto heading_parameter : definition->heading().parameters)
+        {
+            heading_parameter->accept(this);
+        }
+        if (definition->heading().return_type.type != nullptr)
+        {
+            definition->heading().return_type.type->accept(this);
+        }
+        if (definition->body != nullptr)
+        {
+            definition->body->accept(this);
+        }
     }
 
-    void declaration_visitor::visit(assign_statement *)
+    void declaration_visitor::visit(assign_statement *statement)
     {
-        __builtin_unreachable();
+        statement->lvalue().accept(this);
+        statement->rvalue().accept(this);
     }
 
-    void declaration_visitor::visit(if_statement *)
+    void declaration_visitor::visit(if_statement *statement)
     {
-        __builtin_unreachable();
+        statement->body().prerequisite().accept(this);
+        for (struct statement *const statement : statement->body().statements)
+        {
+            statement->accept(this);
+        }
+        for (const auto branch : statement->branches)
+        {
+            branch->prerequisite().accept(this);
+
+            for (struct statement *const statement : branch->statements)
+            {
+                statement->accept(this);
+            }
+        }
+        if (statement->alternative() != nullptr)
+        {
+            for (struct statement *const statement : *statement->alternative())
+            {
+                statement->accept(this);
+            }
+        }
     }
 
-    void declaration_visitor::visit(while_statement *)
+    void declaration_visitor::visit(while_statement *statement)
     {
-        __builtin_unreachable();
+        statement->body().prerequisite().accept(this);
+        for (struct statement *const statement : statement->body().statements)
+        {
+            statement->accept(this);
+        }
+        for (const auto branch : statement->branches)
+        {
+            branch->prerequisite().accept(this);
+
+            for (struct statement *const statement : branch->statements)
+            {
+                statement->accept(this);
+            }
+        }
     }
 
-    void declaration_visitor::visit(return_statement *)
+    void declaration_visitor::visit(return_statement *statement)
     {
-        __builtin_unreachable();
+        if (statement->return_expression() != nullptr)
+        {
+            statement->return_expression()->accept(this);
+        }
     }
 
-    void declaration_visitor::visit(defer_statement *)
+    void declaration_visitor::visit(defer_statement *statement)
     {
-        __builtin_unreachable();
+        for (struct statement *const statement : statement->statements)
+        {
+            statement->accept(this);
+        }
     }
 
-    void declaration_visitor::visit(procedure_call *)
+    void declaration_visitor::visit(procedure_call *call)
     {
-        __builtin_unreachable();
+        call->callable().accept(this);
+        for (expression *const argument: call->arguments)
+        {
+            argument->accept(this);
+        }
     }
 
-    void declaration_visitor::visit(block *)
+    void declaration_visitor::visit(block *block)
     {
-        __builtin_unreachable();
+        for (constant_definition *const constant : block->constants)
+        {
+            constant->accept(this);
+        }
+        for (variable_declaration *const variable : block->variables)
+        {
+            variable->accept(this);
+        }
+        for (statement *const statement : block->body)
+        {
+            statement->accept(this);
+        }
     }
 
-    void declaration_visitor::visit(traits_expression *)
+    void declaration_visitor::visit(traits_expression *trait)
     {
-        __builtin_unreachable();
+        if (!trait->parameters.empty())
+        {
+            trait->parameters.front()->accept(this);
+        }
     }
 
-    void declaration_visitor::visit(cast_expression *)
+    void declaration_visitor::visit(cast_expression *expression)
     {
-        __builtin_unreachable();
+        expression->value().accept(this);
+        expression->target().accept(this);
     }
 
-    void declaration_visitor::visit(binary_expression *)
+    void declaration_visitor::visit(binary_expression *expression)
     {
-        __builtin_unreachable();
+        expression->lhs().accept(this);
+        expression->rhs().accept(this);
     }
 
-    void declaration_visitor::visit(unary_expression *)
+    void declaration_visitor::visit(unary_expression *expression)
     {
-        __builtin_unreachable();
+        expression->operand().accept(this);
     }
 
     void declaration_visitor::visit(variable_expression *)
     {
-        __builtin_unreachable();
     }
 
-    void declaration_visitor::visit(array_access_expression *)
+    void declaration_visitor::visit(array_access_expression *expression)
     {
-        __builtin_unreachable();
+        expression->base().accept(this);
+        expression->index().accept(this);
     }
 
-    void declaration_visitor::visit(field_access_expression *)
+    void declaration_visitor::visit(field_access_expression *expression)
     {
-        __builtin_unreachable();
+        expression->base().accept(this);
     }
 
-    void declaration_visitor::visit(dereference_expression *)
+    void declaration_visitor::visit(dereference_expression *expression)
     {
-        __builtin_unreachable();
+        expression->base().accept(this);
     }
 
     void declaration_visitor::visit(literal<std::int32_t> *)
     {
-        __builtin_unreachable();
     }
 
     void declaration_visitor::visit(literal<std::uint32_t> *)
     {
-        __builtin_unreachable();
     }
 
     void declaration_visitor::visit(literal<double> *)
     {
-        __builtin_unreachable();
     }
 
     void declaration_visitor::visit(literal<bool> *)
     {
-        __builtin_unreachable();
     }
 
     void declaration_visitor::visit(literal<unsigned char> *)
     {
-        __builtin_unreachable();
     }
 
     void declaration_visitor::visit(literal<std::nullptr_t> *)
     {
-        __builtin_unreachable();
     }
 
     void declaration_visitor::visit(literal<std::string> *)
     {
-        __builtin_unreachable();
     }
 }
diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc
index 284ac5d..2e445d9 100644
--- a/gcc/elna-generic.cc
+++ b/gcc/elna-generic.cc
@@ -36,11 +36,20 @@ along with GCC; see the file COPYING3.  If not see
 
 namespace elna::gcc
 {
-    tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols)
+    tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols,
+            std::unordered_map<std::string, tree>& unresolved)
     {
         if (auto reference = type.get<boot::primitive_type>())
         {
-            return symbols->lookup(reference->identifier);
+            auto looked_up = unresolved.find(reference->identifier);
+            if (looked_up == unresolved.cend())
+            {
+                return symbols->lookup(reference->identifier);
+            }
+            else
+            {
+                return looked_up->second;
+            }
         }
         else if (auto reference = type.get<boot::record_type>())
         {
@@ -52,7 +61,7 @@ namespace elna::gcc
         }
         else if (auto reference = type.get<boot::pointer_type>())
         {
-            return build_pointer_type_for_mode(get_inner_alias(reference->base, symbols), VOIDmode, true);
+            return build_pointer_type_for_mode(get_inner_alias(reference->base, symbols, unresolved), VOIDmode, true);
         }
         else if (auto reference = type.get<boot::array_type>())
         {
@@ -60,30 +69,31 @@ namespace elna::gcc
             tree upper_bound = build_int_cst_type(integer_type_node, reference->size);
             tree range_type = build_range_type(integer_type_node, lower_bound, upper_bound);
 
-            return build_array_type(get_inner_alias(reference->base, symbols), range_type);
+            return build_array_type(get_inner_alias(reference->base, symbols, unresolved), range_type);
         }
         else if (auto reference = type.get<boot::alias_type>())
         {
-            return handle_symbol(reference->name, reference->reference, symbols);
+            return handle_symbol(reference->name, reference->reference, symbols, unresolved);
         }
         return error_mark_node;
     }
 
-    tree handle_symbol(const std::string& symbol_name, const boot::type& type, std::shared_ptr<symbol_table> symbols)
+    tree handle_symbol(const std::string& symbol_name, const boot::type& type,
+            std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved)
     {
         auto looked_up = symbols->lookup(symbol_name);
 
         if (looked_up == NULL_TREE)
         {
-            looked_up = get_inner_alias(type, symbols);
-            symbols->enter(symbol_name, looked_up);
+            looked_up = get_inner_alias(type, symbols, unresolved);
+            unresolved.insert({ symbol_name, looked_up });
         }
         return looked_up;
     }
 
     std::deque<std::unique_ptr<boot::error>> do_semantic_analysis(const char *path,
             std::unique_ptr<boot::program>& ast, std::shared_ptr<boot::symbol_table> info_table,
-            std::shared_ptr<symbol_table> symbols)
+            std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved)
     {
         boot::declaration_visitor declaration_visitor(path, info_table);
 
@@ -93,15 +103,16 @@ namespace elna::gcc
         {
             for (auto& [symbol_name, symbol_info] : declaration_visitor.unresolved)
             {
-                handle_symbol(symbol_name, boot::type(symbol_info), symbols);
+                handle_symbol(symbol_name, boot::type(symbol_info), symbols, unresolved);
             }
         }
         return std::move(declaration_visitor.errors());
     }
 
-    generic_visitor::generic_visitor(std::shared_ptr<symbol_table> symbol_table)
+    generic_visitor::generic_visitor(std::shared_ptr<symbol_table> symbol_table,
+            std::unordered_map<std::string, tree>&& unresolved)
+        : symbols(symbol_table), unresolved(std::move(unresolved))
     {
-        this->symbols = symbol_table;
     }
 
     void generic_visitor::build_procedure_call(location_t call_location,
@@ -763,8 +774,7 @@ namespace elna::gcc
         }
         else
         {
-            error_at(definition_location,
-                    "variable '%s' already declared in this scope",
+            error_at(definition_location, "Variable '%s' already declared in this scope",
                     definition->identifier.c_str());
         }
         this->current_expression = NULL_TREE;
@@ -773,12 +783,13 @@ namespace elna::gcc
     void generic_visitor::visit(boot::type_definition *definition)
     {
         location_t definition_location = get_location(&definition->position());
-        this->current_expression = this->symbols->lookup(definition->identifier);
+        this->current_expression = this->unresolved.at(definition->identifier);
         definition->body().accept(this);
 
         tree definition_tree = build_decl(definition_location, TYPE_DECL,
                 get_identifier(definition->identifier.c_str()), this->current_expression);
         auto result = this->symbols->enter(definition->identifier, this->current_expression);
+        gcc_assert(result);
 
         TREE_PUBLIC(definition_tree) = definition->exported;
         TYPE_NAME(this->current_expression) = get_identifier(definition->identifier.c_str());
@@ -1225,29 +1236,61 @@ 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;
     }
 
     void generic_visitor::visit(boot::primitive_type_expression *type)
     {
-        tree symbol = this->symbols->lookup(type->name);
+        auto looked_up = this->unresolved.find(type->name);
+        tree symbol;
 
+        if (looked_up == this->unresolved.cend())
+        {
+            symbol = this->symbols->lookup(type->name);
+        }
+        else
+        {
+            symbol = looked_up->second;
+        }
         if (symbol == NULL_TREE || !TYPE_P(symbol))
         {
-            error_at(get_location(&type->position()),
-                    "type '%s' not declared", type->name.c_str());
+            error_at(get_location(&type->position()), "Type '%s' not declared", type->name.c_str());
 
             this->current_expression = error_mark_node;
         }
diff --git a/gcc/elna1.cc b/gcc/elna1.cc
index 1d45781..8ea613a 100644
--- a/gcc/elna1.cc
+++ b/gcc/elna1.cc
@@ -85,12 +85,14 @@ static void elna_parse_file(const char *filename)
     {
         std::shared_ptr<elna::boot::symbol_table> info_table = elna::boot::builtin_symbol_table();
         std::shared_ptr<elna::gcc::symbol_table> symbol_table = elna::gcc::builtin_symbol_table();
+        std::unordered_map<std::string, tree> unresolved;
 
-        auto semantic_errors = elna::gcc::do_semantic_analysis(filename, driver.tree, info_table, symbol_table);
+        auto semantic_errors = elna::gcc::do_semantic_analysis(filename, driver.tree,
+                info_table, symbol_table, unresolved);
 
         if (semantic_errors.empty())
         {
-            elna::gcc::generic_visitor generic_visitor{ symbol_table };
+            elna::gcc::generic_visitor generic_visitor{ symbol_table, std::move(unresolved) };
             generic_visitor.visit(driver.tree.get());
         }
         else
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<typename V>
-        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<union_type_expression> is_union();
         virtual std::shared_ptr<procedure_type_expression> 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<primitive_type_expression> is_primitive() override;
     };
 
@@ -264,7 +222,7 @@ namespace elna::boot
 
         array_type_expression(const struct position position,
                 std::shared_ptr<type_expression> base, const std::uint32_t size);
-        void accept(parser_visitor *visitor);
+        void accept(parser_visitor *visitor) override;
         std::shared_ptr<array_type_expression> is_array() override;
 
         type_expression& base();
@@ -276,7 +234,7 @@ namespace elna::boot
 
     public:
         pointer_type_expression(const struct position position, std::shared_ptr<type_expression> base);
-        void accept(parser_visitor *visitor);
+        void accept(parser_visitor *visitor) override;
         std::shared_ptr<pointer_type_expression> is_pointer() override;
 
         type_expression& base();
@@ -291,7 +249,7 @@ namespace elna::boot
 
         record_type_expression(const struct position position, std::vector<field_declaration>&& fields);
 
-        void accept(parser_visitor *visitor);
+        void accept(parser_visitor *visitor) override;
         std::shared_ptr<record_type_expression> is_record() override;
     };
 
@@ -302,7 +260,7 @@ namespace elna::boot
 
         union_type_expression(const struct position position, std::vector<field_declaration>&& fields);
 
-        void accept(parser_visitor *visitor);
+        void accept(parser_visitor *visitor) override;
         std::shared_ptr<union_type_expression> is_union() override;
     };
 
@@ -311,12 +269,12 @@ namespace elna::boot
      */
     class variable_declaration : public definition
     {
-        std::shared_ptr<type_expression> m_type;
+        std::shared_ptr<type_expression> m_variable_type;
 
     public:
         variable_declaration(const struct position position, const std::string& identifier,
-                std::shared_ptr<type_expression> type, const bool exported = false);
-        void accept(parser_visitor *visitor);
+                std::shared_ptr<type_expression> 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<std::string> *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<procedure_type_expression> 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<procedure_type_expression> 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<type_expression> 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<type_expression> 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<typename V>
-        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<typename V>
-        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<typename V>
-        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<typename V>
-        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<typename V>
-        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<typename V>
-        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<expression *> arguments;
 
         procedure_call(const struct position position, designator_expression *callable);
-
-        template<typename V>
-        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<typename V>
-        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<statement *> *alternative = nullptr);
-
-        template<typename V>
-        void accept(V *visitor)
-        {
-            visitor->visit(this);
-        }
+        void accept(parser_visitor *visitor) override;
+        virtual if_statement *is_if() override;
 
         conditional_statements& body();
         std::vector<statement *> *alternative();
 
-        virtual if_statement *is_if() override;
         virtual ~if_statement() override;
     };
 
@@ -700,16 +592,11 @@ namespace elna::boot
     public:
         std::vector<conditional_statements *> branches;
         while_statement(const struct position position, conditional_statements *body);
-
-        template<typename V>
-        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<statement *> 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<procedure_definition *> 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<typename V>
-        void accept(V *visitor)
+        void accept(parser_visitor *visitor) override
         {
             visitor->visit(this);
         }
@@ -846,14 +732,9 @@ namespace elna::boot
         std::vector<statement *> statements;
 
         defer_statement(const struct position position);
-
-        template<typename V>
-        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/boot/semantic.h b/include/elna/boot/semantic.h
index a682325..5fef097 100644
--- a/include/elna/boot/semantic.h
+++ b/include/elna/boot/semantic.h
@@ -65,24 +65,24 @@ namespace elna::boot
         void visit(union_type_expression *) override;
         void visit(procedure_type_expression *) override;
 
-        void visit(variable_declaration *) override;
+        void visit(variable_declaration *declaration) override;
         void visit(constant_definition *) override;
-        void visit(procedure_definition *) override;
-        void visit(assign_statement *) override;
-        void visit(if_statement *) override;
-        void visit(while_statement *) override;
-        void visit(return_statement *) override;
-        void visit(defer_statement *) override;
-        void visit(procedure_call *) override;
-        void visit(block *) override;
-        void visit(cast_expression *) override;
-        void visit(traits_expression *) override;
-        void visit(binary_expression *) override;
-        void visit(unary_expression *) override;
+        void visit(procedure_definition *definition) override;
+        void visit(assign_statement *statement) override;
+        void visit(if_statement *statement) override;
+        void visit(while_statement *statement) override;
+        void visit(return_statement *statement) override;
+        void visit(defer_statement *statement) override;
+        void visit(procedure_call *call) override;
+        void visit(block *block) override;
+        void visit(cast_expression *expression) override;
+        void visit(traits_expression *trait) override;
+        void visit(binary_expression *expression) override;
+        void visit(unary_expression *expression) override;
         void visit(variable_expression *) override;
-        void visit(array_access_expression *) override;
-        void visit(field_access_expression *) override;
-        void visit(dereference_expression *) override;
+        void visit(array_access_expression *expression) override;
+        void visit(field_access_expression *expression) override;
+        void visit(dereference_expression *expression) override;
         void visit(literal<std::int32_t> *) override;
         void visit(literal<std::uint32_t> *) override;
         void visit(literal<double> *) override;
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index 1a85f68..95ee7d7 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -34,13 +34,15 @@ namespace elna::gcc
 {
     std::deque<std::unique_ptr<boot::error>> do_semantic_analysis(const char *path,
             std::unique_ptr<boot::program>& ast, std::shared_ptr<boot::symbol_table> info_table,
-            std::shared_ptr<symbol_table> symbols);
-    tree handle_symbol(const std::string& symbol_name, const boot::type& type, std::shared_ptr<symbol_table> symbols);
+            std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved);
+    tree handle_symbol(const std::string& symbol_name, const boot::type& type,
+            std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved);
 
     class generic_visitor final : public boot::parser_visitor
     {
         tree current_expression{ NULL_TREE };
         std::shared_ptr<symbol_table> symbols;
+        std::unordered_map<std::string, tree> unresolved;
 
         tree build_label_decl(const char *name, location_t loc);
         tree build_procedure_type(boot::procedure_type_expression& type);
@@ -68,7 +70,8 @@ namespace elna::gcc
         void visit_statements(const std::vector<boot::statement *>& statements);
 
     public:
-        generic_visitor(std::shared_ptr<symbol_table> symbol_table);
+        generic_visitor(std::shared_ptr<symbol_table> symbol_table,
+                std::unordered_map<std::string, tree>&& unresolved);
 
         void visit(boot::program *program) override;
         void visit(boot::procedure_definition *definition) override;