From a187e5e62ad7a114cac23bdfd24eee372590b994 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 21 Jun 2025 22:32:34 +0200 Subject: [PATCH] Detect alias cycles --- boot/ast.cc | 4 +-- boot/parser.yy | 4 +-- boot/semantic.cc | 52 ++++++++++++++++++++++++++------ gcc/elna-builtins.cc | 45 +++++++++++---------------- gcc/elna-diagnostic.cc | 6 +++- gcc/elna-generic.cc | 19 +++--------- gcc/elna1.cc | 2 +- include/elna/boot/ast.h | 3 +- include/elna/boot/semantic.h | 8 +++-- include/elna/gcc/elna-builtins.h | 7 ++--- 10 files changed, 84 insertions(+), 66 deletions(-) diff --git a/boot/ast.cc b/boot/ast.cc index 1707960..5609939 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -914,8 +914,8 @@ namespace elna::boot } while_statement::while_statement(const struct position position, conditional_statements *body, - std::vector&& branches, std::vector *alternative) - : node(position), m_body(body), branches(std::move(branches)), alternative(alternative) + std::vector&& branches) + : node(position), m_body(body), branches(std::move(branches)) { } diff --git a/boot/parser.yy b/boot/parser.yy index 3971b9b..ab17bc9 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -410,10 +410,10 @@ designator_expression: statement: designator_expression ":=" expression { $$ = new boot::assign_statement(boot::make_position(@1), $1, $3); } - | "while" expression "do" optional_statements elsif_do_statements else_statements "end" + | "while" expression "do" optional_statements elsif_do_statements "end" { boot::conditional_statements *body = new boot::conditional_statements($2, std::move($4)); - $$ = new boot::while_statement(boot::make_position(@1), body, std::move($5), $6); + $$ = new boot::while_statement(boot::make_position(@1), body, std::move($5)); } | "if" expression "then" optional_statements elsif_then_statements else_statements "end" { diff --git a/boot/semantic.cc b/boot/semantic.cc index 81e85ed..3ebccde 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -17,6 +17,7 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/semantic.h" +#include #include namespace elna::boot @@ -44,7 +45,7 @@ namespace elna::boot } field_duplication_error::field_duplication_error(const std::string& field_name, - const char *path, const struct position) + const char *path, const struct position position) : error(path, position), field_name(field_name) { } @@ -55,14 +56,22 @@ namespace elna::boot } cyclic_declaration_error::cyclic_declaration_error(const std::vector& cycle, - const char *path, const struct position) + const char *path, const struct position position) : error(path, position), cycle(cycle) { } std::string cyclic_declaration_error::what() const { - return "Type declaration forms a cycle"; + auto segment = std::cbegin(this->cycle); + std::string message = "Type declaration forms a cycle: " + *segment; + + ++segment; + for (; segment != std::cend(this->cycle); ++segment) + { + message += " -> " + *segment; + } + return message; } name_analysis_visitor::name_analysis_visitor(const char *path, std::shared_ptr symbols, @@ -348,6 +357,22 @@ namespace elna::boot } } + bool name_analysis_visitor::check_unresolved_symbol(std::shared_ptr alias, + std::vector& path) + { + if (std::find(std::cbegin(path), std::cend(path), alias->name) != std::cend(path)) + { + return false; + } + path.push_back(alias->name); + + if (auto another_alias = alias->reference.get()) + { + return check_unresolved_symbol(another_alias, path); + } + return true; + } + void name_analysis_visitor::visit(unit *unit) { for (type_declaration *const type : unit->types) @@ -356,8 +381,17 @@ namespace elna::boot } for (auto& unresolved : this->unresolved) { - auto info = std::make_shared(type_info(type(unresolved.second))); - this->symbols->enter(std::move(unresolved.first), info); + std::vector path; + + if (check_unresolved_symbol(unresolved.second, path)) + { + auto info = std::make_shared(type_info(type(unresolved.second))); + this->symbols->enter(std::move(unresolved.first), info); + } + else + { + add_error(path, this->input_file, position{ 0, 0 }); + } } for (variable_declaration *const variable : unit->variables) { @@ -539,6 +573,10 @@ namespace elna::boot void declaration_visitor::visit(unit *unit) { + for (import_declaration *const _import : unit->imports) + { + _import->accept(this); + } for (type_declaration *const type : unit->types) { const std::string& type_identifier = type->identifier.identifier; @@ -547,10 +585,6 @@ namespace elna::boot { add_error(type->identifier.identifier, this->input_file, type->position()); } - else - { - type->accept(this); - } } } diff --git a/gcc/elna-builtins.cc b/gcc/elna-builtins.cc index 8c63405..232cf28 100644 --- a/gcc/elna-builtins.cc +++ b/gcc/elna-builtins.cc @@ -101,11 +101,11 @@ namespace elna::gcc } tree build_composite_type(const std::vector& fields, tree composite_type_node, - std::shared_ptr symbols, std::vector& path) + std::shared_ptr symbols) { for (auto& field : fields) { - tree rewritten_field = get_inner_alias(field.second, symbols, path); + tree rewritten_field = get_inner_alias(field.second, symbols); tree field_declaration = build_field(UNKNOWN_LOCATION, composite_type_node, field.first, rewritten_field); TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), field_declaration); @@ -114,25 +114,24 @@ namespace elna::gcc return composite_type_node; } - tree build_procedure_type(const boot::procedure_type& procedure, std::shared_ptr symbols, - std::vector& path) + tree build_procedure_type(const boot::procedure_type& procedure, std::shared_ptr symbols) { std::vector parameter_types(procedure.parameters.size()); for (std::size_t i = 0; i < procedure.parameters.size(); ++i) { - parameter_types[i] = get_inner_alias(procedure.parameters.at(i), symbols, path); + parameter_types[i] = get_inner_alias(procedure.parameters.at(i), symbols); } tree return_type = void_type_node; if (!procedure.return_type.proper_type.empty()) { - return_type = get_inner_alias(procedure.return_type.proper_type, symbols, path); + return_type = get_inner_alias(procedure.return_type.proper_type, symbols); } return build_function_type_array(return_type, procedure.parameters.size(), parameter_types.data()); } - tree get_inner_alias(const boot::type& type, std::shared_ptr symbols, std::vector& path) + tree get_inner_alias(const boot::type& type, std::shared_ptr symbols) { if (auto reference = type.get()) { @@ -145,7 +144,7 @@ namespace elna::gcc { tree composite_type_node = make_node(RECORD_TYPE); - build_composite_type(reference->fields, composite_type_node, symbols, path); + build_composite_type(reference->fields, composite_type_node, symbols); return composite_type_node; } @@ -153,7 +152,7 @@ namespace elna::gcc { tree composite_type_node = make_node(UNION_TYPE); - build_composite_type(reference->fields, composite_type_node, symbols, path); + build_composite_type(reference->fields, composite_type_node, symbols); return composite_type_node; } @@ -163,42 +162,35 @@ namespace elna::gcc } else if (auto reference = type.get()) { - return build_global_pointer_type(get_inner_alias(reference->base, symbols, path)); + return build_global_pointer_type(get_inner_alias(reference->base, symbols)); } else if (auto reference = type.get()) { - tree base = get_inner_alias(reference->base, symbols, path); + tree base = get_inner_alias(reference->base, symbols); return build_static_array_type(base, reference->size); } else if (auto reference = type.get()) { - auto procedure = build_procedure_type(*reference, symbols, path); + auto procedure = build_procedure_type(*reference, symbols); return build_global_pointer_type(procedure); } else if (auto reference = type.get()) { - return handle_symbol(reference->name, reference, symbols, path); + return handle_symbol(reference->name, reference, symbols); } return error_mark_node; } tree handle_symbol(const std::string& symbol_name, std::shared_ptr reference, - std::shared_ptr symbols, std::vector& path) + std::shared_ptr symbols) { - path.push_back(symbol_name); - std::vector::const_iterator head_peace = std::cbegin(path); - - if (std::find(std::next(head_peace), std::cend(path), *head_peace) != std::cend(path)) - { - return error_mark_node; - } tree looked_up = symbols->lookup(symbol_name); if (looked_up == NULL_TREE) { - looked_up = get_inner_alias(reference->reference, symbols, path); + looked_up = get_inner_alias(reference->reference, symbols); symbols->enter(symbol_name, build_type_declaration(symbol_name, looked_up)); } @@ -212,8 +204,7 @@ namespace elna::gcc void declare_procedure(const std::string& name, const boot::procedure_info& info, std::shared_ptr symbols) { - std::vector path; - tree declaration_type = gcc::build_procedure_type(info.symbol, symbols, path); + tree declaration_type = gcc::build_procedure_type(info.symbol, symbols); tree fndecl = build_fn_decl(name.c_str(), declaration_type); symbols->enter(name, fndecl); @@ -247,18 +238,16 @@ namespace elna::gcc DECL_EXTERNAL(fndecl) = info.symbols == nullptr; } - void do_semantic_analysis(std::shared_ptr info_table, std::shared_ptr symbols) + void rewrite_symbol_table(std::shared_ptr info_table, std::shared_ptr symbols) { for (auto& [symbol_name, symbol_info] : *info_table) { - std::vector type_path; - if (auto type_info = symbol_info->is_type()) { // The top level symbol table has basic (builtin) types in it which are not aliases. if (auto alias_type = type_info->symbol.get()) { - handle_symbol(symbol_name, alias_type, symbols, type_path); + handle_symbol(symbol_name, alias_type, symbols); } } else if (auto procedure_info = symbol_info->is_procedure()) diff --git a/gcc/elna-diagnostic.cc b/gcc/elna-diagnostic.cc index 2acef82..5455ce7 100644 --- a/gcc/elna-diagnostic.cc +++ b/gcc/elna-diagnostic.cc @@ -145,8 +145,12 @@ namespace elna::gcc { for (const auto& error : errors) { - auto gcc_location = elna::gcc::get_location(&error->position); + location_t gcc_location{ UNKNOWN_LOCATION }; + if (error->position.line != 0 || error->position.column != 0) + { + gcc_location = elna::gcc::get_location(&error->position); + } error_at(gcc_location, error->what().c_str()); } } diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 00203af..056f950 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -217,8 +217,7 @@ namespace elna::gcc void generic_visitor::visit(boot::cast_expression *expression) { - std::vector path; - tree cast_target = get_inner_alias(expression->expression_type, this->symbols->scope(), path); + tree cast_target = get_inner_alias(expression->expression_type, this->symbols->scope()); expression->value().accept(this); tree cast_source = TREE_TYPE(this->current_expression); @@ -746,10 +745,9 @@ namespace elna::gcc void generic_visitor::visit(boot::variable_declaration *declaration) { - std::vector path; this->current_expression = get_inner_alias( this->info_table->lookup(declaration->identifier.identifier)->is_variable()->symbol, - this->symbols->scope(), path); + this->symbols->scope()); location_t declaration_location = get_location(&declaration->position()); tree declaration_tree = build_decl(declaration_location, VAR_DECL, @@ -854,8 +852,7 @@ namespace elna::gcc this->current_expression = error_mark_node; return false; } - std::vector path; - this->current_expression = get_inner_alias(trait->types.front(), this->symbols, path); + this->current_expression = get_inner_alias(trait->types.front(), this->symbols); return this->current_expression != error_mark_node; } @@ -919,8 +916,7 @@ namespace elna::gcc this->current_expression = error_mark_node; return; } - std::vector path; - this->current_expression = get_inner_alias(trait->types.front(), this->symbols, path); + this->current_expression = get_inner_alias(trait->types.front(), this->symbols); auto field_type = trait->parameters.at(1)->is_named(); if (field_type == nullptr) @@ -1128,13 +1124,6 @@ namespace elna::gcc { make_if_branch(*branch, goto_check); } - if (statement->alternative != nullptr) - { - enter_scope(); - visit_statements(*statement->alternative); - tree mapping = leave_scope(); - append_statement(mapping); - } append_statement(branch_end_expression); this->current_expression = NULL_TREE; } diff --git a/gcc/elna1.cc b/gcc/elna1.cc index 533d4c1..ef13c57 100644 --- a/gcc/elna1.cc +++ b/gcc/elna1.cc @@ -95,7 +95,7 @@ static void elna_parse_file(const char *filename) if (name_analysis_visitor.errors().empty()) { - elna::gcc::do_semantic_analysis(info_table, symbol_table); + elna::gcc::rewrite_symbol_table(info_table, symbol_table); elna::gcc::generic_visitor generic_visitor{ symbol_table, info_table }; generic_visitor.visit(module_tree.get()); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index a27318d..28241c5 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -659,10 +659,9 @@ namespace elna::boot public: const std::vector branches; - const std::vector *alternative; while_statement(const struct position position, conditional_statements *body, - std::vector&& branches, std::vector *alternative = nullptr); + std::vector&& branches); void accept(parser_visitor *visitor) override; conditional_statements& body(); diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index cc4fab8..d8d87d6 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -53,7 +53,7 @@ namespace elna::boot const std::string field_name; public: - field_duplication_error(const std::string& field_name, const char *path, const struct position); + field_duplication_error(const std::string& field_name, const char *path, const struct position position); std::string what() const override; }; @@ -63,7 +63,8 @@ namespace elna::boot const std::vector cycle; public: - cyclic_declaration_error(const std::vector& cycle, const char *path, const struct position); + cyclic_declaration_error(const std::vector& cycle, + const char *path, const struct position position); std::string what() const override; }; @@ -82,6 +83,9 @@ namespace elna::boot procedure_type build_procedure(procedure_type_expression& type_expression); std::vector build_composite_type(const std::vector& fields); + bool check_unresolved_symbol(std::shared_ptr alias, + std::vector& path); + public: explicit name_analysis_visitor(const char *path, std::shared_ptr symbols, std::unordered_map>&& unresolved); diff --git a/include/elna/gcc/elna-builtins.h b/include/elna/gcc/elna-builtins.h index 1cf80dc..1fe11e4 100644 --- a/include/elna/gcc/elna-builtins.h +++ b/include/elna/gcc/elna-builtins.h @@ -30,11 +30,10 @@ namespace elna::gcc void init_ttree(); std::shared_ptr builtin_symbol_table(); - void do_semantic_analysis(std::shared_ptr info_table, std::shared_ptr symbols); + void rewrite_symbol_table(std::shared_ptr info_table, std::shared_ptr symbols); tree handle_symbol(const std::string& symbol_name, std::shared_ptr reference, - std::shared_ptr symbols, std::vector& path); - tree get_inner_alias(const boot::type& type, std::shared_ptr symbols, - std::vector& path); + std::shared_ptr symbols); + tree get_inner_alias(const boot::type& type, std::shared_ptr symbols); void declare_procedure(const std::string& name, const boot::procedure_info& info, std::shared_ptr symbols); }