/* Final validation after constant folding. 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/validation.h" #include #include #include namespace elna::boot { validation_error::validation_error(const source_position position, payload_type payload) : error(position), payload(std::move(payload)) { } std::string validation_error::what() const { return std::visit([](const auto& payload) -> std::string { using T = std::decay_t; if constexpr (std::is_same_v) { return "Variable initializers must be constant expressions"; } else if constexpr (std::is_same_v) { return "Duplicate case label"; } else if constexpr (std::is_same_v) { return "Case label must be a constant expression"; } }, this->payload); } std::optional> validation_error::note() const { return std::visit([](const auto& payload) -> std::optional> { using T = std::decay_t; if constexpr (std::is_same_v) { std::string identifier_list = std::accumulate( std::next(payload.identifiers.begin()), payload.identifiers.end(), payload.identifiers.front().name(), [](const std::string& accumulator, const identifier& next) -> std::string { return accumulator + ", " + next.name(); }); auto position_span = source_position(payload.identifiers.front().position().start(), payload.identifiers.back().position().end()); return std::make_pair(std::move(identifier_list), position_span); } else if constexpr (std::is_same_v) { return std::make_pair("Previous label here", payload.first); } else if constexpr (std::is_same_v) { return std::nullopt; } }, this->payload); } validation_error validation_error::non_constant_initializer_error(const source_position position, const std::vector& identifiers) { non_constant_initializer payload; payload.identifiers.reserve(identifiers.size()); std::ranges::transform(identifiers, std::back_inserter(payload.identifiers), [](const auto& identifier) { return identifier.id(); }); return validation_error(position, std::move(payload)); } validation_visitor::validation_visitor(symbol_bag& bag, const target_info& target) : bag(bag), target(target), constant_evaluator(this->bag, this->target) { } void validation_visitor::visit(variable_declaration* declaration) { if (declaration->initializer == nullptr || has_errors()) { return; } auto computed = this->constant_evaluator.evaluate(*declaration->initializer); if (!computed) { auto non_constant_initializer_error = validation_error::non_constant_initializer_error( declaration->initializer->position(), declaration->identifiers); add_error(non_constant_initializer_error); return; } for (const auto& identifier : declaration->identifiers) { auto variable_symbol = this->bag.lookup(identifier.name())->is_variable(); variable_symbol->value = computed; } } void validation_visitor::visit(procedure_declaration *declaration) { if (declaration->body.has_value()) { auto procedure = this->bag.lookup(declaration->identifier.name())->is_procedure(); this->bag.enter(procedure->scope); } walking_visitor::visit(declaration); if (declaration->body.has_value()) { this->bag.leave(); } } void validation_visitor::visit(case_statement *statement) { walking_visitor::visit(statement); std::unordered_map seen; for (const auto& case_block : statement->cases) { for (auto *label : case_block.labels) { auto value = this->constant_evaluator.evaluate(*label); if (!value.has_value()) { add_error(label->position(), validation_error::non_constant_case_label{}); continue; } auto [case_position, inserted] = seen.try_emplace(value.value(), label->position()); if (!inserted) { add_error(label->position(), validation_error::duplicate_case{ case_position->second }); } } } } }