/* Name analysis. Copyright (C) 2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "elna/boot/semantic.h" #include #include namespace elna::boot { undeclared_error::undeclared_error(const std::string& identifier, const struct position position) : error(position), identifier(identifier) { } std::string undeclared_error::what() const { return "Type '" + identifier + "' not declared"; } already_declared_error::already_declared_error(const std::string& identifier, const struct position position) : error(position), identifier(identifier) { } std::string already_declared_error::what() const { return "Symbol '" + identifier + "' has been already declared"; } field_duplication_error::field_duplication_error(const std::string& field_name, const struct position position) : error(position), field_name(field_name) { } std::string field_duplication_error::what() const { return "Repeated field name '" + field_name + "'"; } cyclic_declaration_error::cyclic_declaration_error(const std::vector& cycle, const struct position position) : error(position), cycle(cycle) { } std::string cyclic_declaration_error::what() const { 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; } return_error::return_error(const std::string& identifier, const struct position position) : error(position), identifier(identifier) { } std::string return_error::what() const { return "Procedure '" + this->identifier + "' is expected to return, but does not have a return statement"; } base_type_error::base_type_error(const std::string& base, const struct position position) : error(position), base(base) { } std::string base_type_error::what() const { return "Record base type '" + this->base + "' is not a record."; } type_analysis_visitor::type_analysis_visitor(symbol_bag bag) : error_container(), bag(bag) { } void type_analysis_visitor::visit(program *program) { visit(static_cast(program)); } void type_analysis_visitor::visit(procedure_declaration *definition) { if (definition->body.has_value() && definition->heading().return_type.proper_type != nullptr) { for (statement *const statement : definition->body.value().body()) { statement->accept(this); } if (!this->returns) { add_error(definition->identifier.name, definition->position()); } } } void type_analysis_visitor::visit(assign_statement *) { } void type_analysis_visitor::visit(if_statement *) { } void type_analysis_visitor::visit(while_statement *) { } void type_analysis_visitor::visit(return_statement *) { this->returns = true; } void type_analysis_visitor::visit(defer_statement *) { } void type_analysis_visitor::visit(empty_statement *) { } void type_analysis_visitor::visit(case_statement *) { } void type_analysis_visitor::visit(procedure_call *) { } bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr alias, std::vector& alias_path) { if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) { return false; } alias_path.push_back(alias->name); if (auto another_alias = alias->reference.get()) { return check_unresolved_symbol(another_alias, alias_path); } return true; } void type_analysis_visitor::visit(unit *unit) { for (type_declaration *const type : unit->types) { type->accept(this); } for (procedure_declaration *const procedure : unit->procedures) { this->returns = false; procedure->accept(this); } } void type_analysis_visitor::visit(type_declaration *definition) { std::vector alias_path; auto unresolved_type = this->bag.lookup(definition->identifier.name)->is_type()->symbol.get(); if (!check_unresolved_symbol(unresolved_type, alias_path)) { add_error(alias_path, definition->position()); } } name_analysis_visitor::name_analysis_visitor(symbol_bag bag) : error_container(), bag(bag) { } std::pair> name_analysis_visitor::build_procedure( procedure_type_expression& type_expression) { procedure_type::return_t result_return; if (type_expression.return_type.no_return) { result_return = procedure_type::return_t(std::monostate{}); } else if (type_expression.return_type.proper_type != nullptr) { type_expression.return_type.proper_type->accept(this); result_return = procedure_type::return_t(this->current_type); } else { result_return = procedure_type::return_t(); } std::pair> result_type{ procedure_type(result_return), std::vector() }; for (auto& [parameter_names, parameters_type] : type_expression.parameters) { parameters_type->accept(this); for (auto& parameter_name : parameter_names) { result_type.first.parameters.push_back(this->current_type); result_type.second.push_back(parameter_name); } } return result_type; } void name_analysis_visitor::visit(program *program) { visit(static_cast(program)); this->bag.enter(); auto variable_type = this->bag.lookup("Int")->is_type()->symbol; this->bag.enter("count", std::make_shared(variable_type, false)); variable_type = this->bag.lookup("Char")->is_type()->symbol; variable_type = type(std::make_shared(variable_type)); variable_type = type(std::make_shared(variable_type)); this->bag.enter("parameters", std::make_shared(variable_type, false)); for (statement *const statement : program->body) { statement->accept(this); } this->bag.leave(); } void name_analysis_visitor::visit(type_declaration *definition) { definition->body().accept(this); auto resolved = this->bag.resolve(definition->identifier.name, this->current_type); auto info = std::make_shared(type(resolved)); info->exported = definition->identifier.exported; this->bag.enter(definition->identifier.name, info); } void name_analysis_visitor::visit(type_expression *) { } void name_analysis_visitor::visit(pointer_type_expression *type_expression) { type_expression->base().accept(this); this->current_type = type(std::make_shared(this->current_type)); } void name_analysis_visitor::visit(array_type_expression *type_expression) { type_expression->base().accept(this); auto result_type{ std::make_shared(this->current_type, type_expression->size) }; this->current_type = type(result_type); } std::vector name_analysis_visitor::build_composite_type(const std::vector& fields) { std::vector result; std::set field_names; for (auto& field : fields) { field.second->accept(this); for (auto& field_name : field.first) { if (field_names.find(field_name) != field_names.cend()) { add_error(field_name, field.second->position()); } else { field_names.insert(field_name); result.push_back(std::make_pair(field_name, this->current_type)); } } } return result; } void name_analysis_visitor::visit(record_type_expression *type_expression) { std::shared_ptr result_type; if (type_expression->base.has_value()) { if (auto unresolved_alias = this->bag.declared(type_expression->base.value())) { result_type = std::make_shared(type(unresolved_alias)); } else if (auto base_symbol = this->bag.lookup(type_expression->base.value())) { if (auto base_type_info = base_symbol->is_type()) { result_type = std::make_shared(base_type_info->symbol); } else { add_error(type_expression->base.value(), type_expression->position()); this->current_type = type(); return; } } else { add_error(type_expression->base.value(), type_expression->position()); this->current_type = type(); return; } } else { result_type = std::make_shared(); } result_type->fields = build_composite_type(type_expression->fields); this->current_type = type(result_type); } void name_analysis_visitor::visit(union_type_expression *type_expression) { auto result_type = std::make_shared(); result_type->fields = build_composite_type(type_expression->fields); this->current_type = type(result_type); } void name_analysis_visitor::visit(procedure_type_expression *type_expression) { std::shared_ptr result_type = std::make_shared(std::move(build_procedure(*type_expression).first)); this->current_type = type(result_type); } void name_analysis_visitor::visit(enumeration_type_expression *type_expression) { std::shared_ptr result_type = std::make_shared(type_expression->members); this->current_type = type(result_type); } std::shared_ptr name_analysis_visitor::register_variable(const std::string& name, const bool is_extern, const struct position position) { auto variable_symbol = std::make_shared(this->current_type, is_extern); if (!this->bag.enter(name, variable_symbol)) { add_error(name, position); } return variable_symbol; } void name_analysis_visitor::visit(variable_declaration *declaration) { declaration->variable_type().accept(this); for (const auto& variable_identifier : declaration->identifiers) { auto variable_symbol = register_variable(variable_identifier.name, declaration->is_extern, declaration->position()); variable_symbol->exported = variable_identifier.exported; } } void name_analysis_visitor::visit(constant_declaration *definition) { definition->body().accept(this); auto constant_symbol = std::make_shared(this->current_literal); constant_symbol->exported = definition->identifier.exported; this->bag.enter(definition->identifier.name, constant_symbol); } void name_analysis_visitor::visit(procedure_declaration *definition) { std::shared_ptr info; auto [heading, parameter_names] = build_procedure(definition->heading()); if (definition->body.has_value()) { info = std::make_shared(heading, std::move(parameter_names), this->bag.enter()); auto name_iterator = std::cbegin(info->names); auto type_iterator = std::cbegin(heading.parameters); while (name_iterator != std::cend(info->names) && type_iterator != std::cend(heading.parameters)) { this->current_type = *type_iterator; auto variable_symbol = register_variable(*name_iterator, false, definition->heading().position()); variable_symbol->exported = false; ++name_iterator; ++type_iterator; } for (constant_declaration *const constant : definition->body.value().constants()) { constant->accept(this); } for (variable_declaration *const variable : definition->body.value().variables()) { variable->accept(this); } for (statement *const statement : definition->body.value().body()) { statement->accept(this); } this->bag.leave(); } else { info = std::make_shared(heading, std::move(parameter_names)); } info->exported = definition->identifier.exported; this->bag.enter(definition->identifier.name, info); } void name_analysis_visitor::visit(assign_statement *statement) { statement->lvalue().accept(this); statement->rvalue().accept(this); } void name_analysis_visitor::visit(if_statement *statement) { 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 name_analysis_visitor::visit(import_declaration *) { } void name_analysis_visitor::visit(while_statement *statement) { 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 name_analysis_visitor::visit(return_statement *statement) { statement->return_expression().accept(this); } void name_analysis_visitor::visit(defer_statement *statement) { for (struct statement *const statement : statement->statements) { statement->accept(this); } } void name_analysis_visitor::visit(empty_statement *) { } void name_analysis_visitor::visit(case_statement *statement) { statement->condition().accept(this); for (const switch_case& case_block : statement->cases) { for (expression *const case_label : case_block.labels) { case_label->accept(this); } for (struct statement *const statement : case_block.statements) { statement->accept(this); } } if (statement->alternative != nullptr) { for (struct statement *const statement : *statement->alternative) { statement->accept(this); } } } void name_analysis_visitor::visit(procedure_call *call) { auto name_expression = call->callable().is_named(); if (name_expression == nullptr || name_expression->name != "assert") { call->callable().accept(this); } for (expression *const argument : call->arguments) { argument->accept(this); } } void name_analysis_visitor::visit(unit *unit) { for (type_declaration *const type : unit->types) { type->accept(this); } for (variable_declaration *const variable : unit->variables) { variable->accept(this); } for (constant_declaration *const constant : unit->constants) { constant->accept(this); } for (procedure_declaration *const procedure : unit->procedures) { procedure->accept(this); } } void name_analysis_visitor::visit(traits_expression *trait) { if (!trait->parameters.empty()) { trait->parameters.front()->accept(this); trait->types.push_back(this->current_type); } } void name_analysis_visitor::visit(cast_expression *expression) { expression->value().accept(this); expression->target().accept(this); expression->expression_type = this->current_type; } void name_analysis_visitor::visit(binary_expression *expression) { expression->lhs().accept(this); expression->rhs().accept(this); } void name_analysis_visitor::visit(unary_expression *expression) { expression->operand().accept(this); } void name_analysis_visitor::visit(named_expression *expression) { if (auto unresolved_alias = this->bag.declared(expression->name)) { this->current_type = type(unresolved_alias); } else if (auto from_symbol_table = this->bag.lookup(expression->name)) { if (auto type_symbol = from_symbol_table->is_type()) { this->current_type = type_symbol->symbol; } } else { add_error(expression->name, expression->position()); this->current_type = type(); } } void name_analysis_visitor::visit(array_access_expression *expression) { expression->base().accept(this); expression->index().accept(this); } void name_analysis_visitor::visit(field_access_expression *expression) { expression->base().accept(this); } void name_analysis_visitor::visit(dereference_expression *expression) { expression->base().accept(this); } void name_analysis_visitor::visit(literal *literal) { this->current_literal = literal->value; } void name_analysis_visitor::visit(literal *literal) { this->current_literal = literal->value; } void name_analysis_visitor::visit(literal *literal) { this->current_literal = literal->value; } void name_analysis_visitor::visit(literal *literal) { this->current_literal = literal->value; } void name_analysis_visitor::visit(literal *literal) { this->current_literal = literal->value; } void name_analysis_visitor::visit(literal *literal) { this->current_literal = literal->value; } void name_analysis_visitor::visit(literal *literal) { this->current_literal = literal->value; } declaration_visitor::declaration_visitor() : error_container() { } void declaration_visitor::visit(program *program) { visit(static_cast(program)); } void declaration_visitor::visit(import_declaration *) { } void declaration_visitor::visit(unit *unit) { for (import_declaration *const _import : unit->imports) { _import->accept(this); } for (type_declaration *const type : unit->types) { type->accept(this); } for (variable_declaration *const variable : unit->variables) { variable->accept(this); } for (procedure_declaration *const procedure : unit->procedures) { procedure->accept(this); } } void declaration_visitor::visit(type_declaration *definition) { const std::string& type_identifier = definition->identifier.name; if (!this->unresolved.insert({ type_identifier, std::make_shared(type_identifier) }).second) { add_error(definition->identifier.name, definition->position()); } } void declaration_visitor::visit(variable_declaration *) { } void declaration_visitor::visit(procedure_declaration *definition) { if (!definition->body.has_value()) { return; } for (variable_declaration *const variable : definition->body.value().variables()) { variable->accept(this); } } }