aboutsummaryrefslogtreecommitdiff
path: root/boot/name_analysis.cc
diff options
context:
space:
mode:
Diffstat (limited to 'boot/name_analysis.cc')
-rw-r--r--boot/name_analysis.cc98
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);