diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-10-07 17:33:40 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-10-07 17:45:20 +0200 |
| commit | 12e304fd56f054ba7296ab538eecd4280a67832a (patch) | |
| tree | 218f37dc04fa14323d2ca7704e47a830dcd98b22 /boot/name_analysis.cc | |
| parent | f547dae96d770cc7bc27d3790d0d87bb8e37963d (diff) | |
| download | elna-12e304fd56f054ba7296ab538eecd4280a67832a.tar.gz | |
Allow assigning contants to enumeration members
Diffstat (limited to 'boot/name_analysis.cc')
| -rw-r--r-- | boot/name_analysis.cc | 98 |
1 files changed, 91 insertions, 7 deletions
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 12a14c3..ff5f6bc 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -17,6 +17,7 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/name_analysis.h" +#include <limits> #include <map> #include <utility> #include <algorithm> @@ -156,6 +157,9 @@ namespace elna::boot return "Procedure types are always constant, 'const' is not allowed"; case module_entry: return "Program entry point should have no or one argument"; + case enumeration_values: + return "Enumeration values should be omitted, given for the first member only," + " or given for all members"; default: __builtin_unreachable(); } @@ -213,6 +217,10 @@ namespace elna::boot case field_on_type: return "Cannot access field '" + this->name + "' on type '" + this->composite.to_string() + "'"; + case duplicate_value: + return "Enumeration member '" + this->name + "' has a value that is already used"; + case value_out_of_range: + return "Value of the enumeration member '" + this->name + "' does not fit into 'Int'"; default: __builtin_unreachable(); } @@ -657,28 +665,104 @@ namespace elna::boot void resolving_visitor::visit(enumeration_type_expression *expression) { + const auto written = std::ranges::count_if(expression->members, + [](const enumeration_type_expression::member_declaration& member) { + return member.second != nullptr; + }); + const bool first_written = expression->members.front().second != nullptr; + const bool all_written = static_cast<std::size_t>(written) == expression->members.size(); + + if (written > 0 && !all_written && (written != 1 || !first_written)) + { + add_error<declaration_format_error>(expression->position(), + declaration_format_error::kind::enumeration_values); + this->current_type = type(); + return; + } std::vector<std::string> member_names; + std::vector<std::ptrdiff_t> member_values; + std::map<std::ptrdiff_t, std::string> used_values; + bool values_valid = true; + member_names.reserve(expression->members.size()); - for (const auto& member : expression->members) + member_values.reserve(expression->members.size()); + for (const enumeration_type_expression::member_declaration& member : expression->members) { - member_names.emplace_back(member.name()); + std::optional<std::ptrdiff_t> value; + + if (member.second != nullptr) + { + member.second->accept(this); + if (member.second->type_decoration.empty()) + { + // The value expression failed to resolve and reported its own error already. + values_valid = false; + continue; + } + const std::optional<constant_value> evaluated = this->constant_evaluator.evaluate(*member.second); + const integer_literal *literal = evaluated.has_value() + ? std::get_if<integer_literal>(&evaluated.value()) + : nullptr; + + if (literal == nullptr) + { + add_error<non_constant_expression_error>(member.second->position(), + non_constant_expression_error::enumeration_value{}); + values_valid = false; + continue; + } + value = literal->try_to<std::ptrdiff_t>(); + } + else if (member_values.empty()) + { + value = std::ptrdiff_t{ 1 }; + } + else if (member_values.back() < std::numeric_limits<std::ptrdiff_t>::max()) + { + value = member_values.back() + 1; + } + + if (!value.has_value()) + { + add_error<member_error>(member.first.position(), member.first.name(), type(), + member_error::kind::value_out_of_range); + values_valid = false; + continue; + } + const std::map<std::ptrdiff_t, std::string>::const_iterator existing = used_values.find(value.value()); + + if (existing != used_values.end()) + { + add_error<member_error>(member.first.position(), member.first.name(), type(), + member_error::kind::duplicate_value); + values_valid = false; + continue; + } + used_values.insert({ value.value(), member.first.name() }); + member_names.emplace_back(member.first.name()); + member_values.emplace_back(value.value()); + } + if (!values_valid) + { + this->current_type = type(); + return; } std::shared_ptr<enumeration_type> const result_type = std::make_shared<enumeration_type>( - member_names); + member_names, member_values); std::map<std::string, source_position> seen; const type aggregate(result_type); - for (const auto& member : expression->members) + for (const enumeration_type_expression::member_declaration& member : expression->members) { - const std::map<std::string, source_position>::const_iterator existing = seen.find(member.name()); + const std::map<std::string, source_position>::const_iterator existing = seen.find(member.first.name()); if (existing != seen.end()) { - add_error<member_error>(member.position(), member.name(), aggregate, + add_error<member_error>(member.first.position(), member.first.name(), aggregate, member_error::duplicate{ .original = existing->second, .base = std::nullopt }); } else { - seen.insert({ member.name(), member.position() }); + seen.insert({ member.first.name(), member.first.position() }); } } this->current_type = type(result_type); |
