diff options
Diffstat (limited to 'boot/validation.cc')
| -rw-r--r-- | boot/validation.cc | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/boot/validation.cc b/boot/validation.cc new file mode 100644 index 0000000..ccc8865 --- /dev/null +++ b/boot/validation.cc @@ -0,0 +1,155 @@ +/* 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 +<http://www.gnu.org/licenses/>. */ + +#include "elna/boot/validation.h" + +#include <algorithm> +#include <numeric> +#include <unordered_map> + +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<decltype(payload)>; + + if constexpr (std::is_same_v<T, non_constant_initializer>) + { + return "Variable initializers must be constant expressions"; + } + else if constexpr (std::is_same_v<T, duplicate_case>) + { + return "Duplicate case label"; + } + else if constexpr (std::is_same_v<T, non_constant_case_label>) + { + return "Case label must be a constant expression"; + } + }, this->payload); + } + + std::optional<std::pair<std::string, source_position>> validation_error::note() const + { + return std::visit([](const auto& payload) -> std::optional<std::pair<std::string, source_position>> { + using T = std::decay_t<decltype(payload)>; + + if constexpr (std::is_same_v<T, non_constant_initializer>) + { + 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<T, duplicate_case>) + { + return std::make_pair("Previous label here", payload.first); + } + else if constexpr (std::is_same_v<T, non_constant_case_label>) + { + return std::nullopt; + } + }, this->payload); + } + + validation_error validation_error::non_constant_initializer_error(const source_position position, + const std::vector<identifier_definition>& 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<validation_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<constant_value, source_position, constant_value_hash, constant_value_hash> 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<validation_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<validation_error>(label->position(), + validation_error::duplicate_case{ case_position->second }); + } + } + } + } +} |
