aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/dependency.cc13
-rw-r--r--boot/evaluator.cc224
-rw-r--r--boot/name_analysis.cc1
-rw-r--r--boot/result.cc75
-rw-r--r--boot/symbol.cc40
-rw-r--r--boot/type_check.cc12
-rw-r--r--boot/validation.cc155
7 files changed, 261 insertions, 259 deletions
diff --git a/boot/dependency.cc b/boot/dependency.cc
index 63d3952..a44b1f5 100644
--- a/boot/dependency.cc
+++ b/boot/dependency.cc
@@ -17,13 +17,10 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/dependency.h"
-#include <fstream>
-#include <sstream>
-#include <cstring>
-
#include "elna/boot/driver.h"
#include "elna/boot/name_analysis.h"
#include "elna/boot/type_check.h"
+#include "elna/boot/validation.h"
#include "parser.hh"
namespace elna::boot
@@ -73,12 +70,12 @@ namespace elna::boot
{
return std::move(type_analyzer.errors());
}
- constant_folder folder(bag, target);
- tree->accept(&folder);
+ validation_visitor validator(bag, target);
+ tree->accept(&validator);
- if (folder.has_errors())
+ if (validator.has_errors())
{
- return std::move(folder.errors());
+ return std::move(validator.errors());
}
return error_list{};
}
diff --git a/boot/evaluator.cc b/boot/evaluator.cc
index 6d3153e..b49a9ec 100644
--- a/boot/evaluator.cc
+++ b/boot/evaluator.cc
@@ -16,25 +16,17 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "elna/boot/evaluator.h"
+
#include "elna/boot/ast.h"
#include <algorithm>
#include <cstddef>
+#include <cstdint>
#include <limits>
#include <ranges>
namespace elna::boot
{
- non_constant_initializer_error::non_constant_initializer_error(const source_position position)
- : error(position)
- {
- }
-
- std::string non_constant_initializer_error::what() const
- {
- return "Variable initializers must be constant expressions";
- }
-
std::optional<type_properties> get_type_properties(const type& subject, const target_info& target)
{
auto resolved = resolve_underlying_type(subject);
@@ -304,6 +296,23 @@ namespace elna::boot
std::optional<constant_value> evaluator::evaluate_field_access(field_access_expression& subject)
{
+ auto type_to_check = subject.base().type_decoration;
+ if (type_to_check.empty())
+ {
+ type_to_check = subject.type_decoration;
+ }
+ auto resolved_base = resolve_underlying_type(type_to_check);
+ if (auto enumeration = resolved_base.get<enumeration_type>())
+ {
+ auto member_iterator = std::ranges::find(enumeration->members, subject.field().name());
+ if (member_iterator != enumeration->members.end())
+ {
+ return constant_value{
+ static_cast<std::int32_t>(std::distance(enumeration->members.begin(), member_iterator) + 1)
+ };
+ }
+ return std::nullopt;
+ }
auto base = evaluate(subject.base());
if (!base.has_value())
{
@@ -770,199 +779,4 @@ namespace elna::boot
return std::nullopt;
}
- /**
- * Converts a constant_value to an AST literal expression.
- *
- * \param value Evaluated constant to convert.
- * \param position Source position for the new literal node.
- * \param decoration Type decoration to apply to the literal.
- * \return A new literal expression, or \c nullptr if \p value
- * is an aggregate (handled separately by the caller).
- */
- static expression *value_to_expression(const constant_value& value,
- const source_position& position, const type& decoration)
- {
- return std::visit([&position, &decoration](auto&& value) -> expression*
- {
- using T = std::decay_t<decltype(value)>;
-
- if constexpr (std::is_same_v<T, std::int32_t>)
- {
- auto *lit = new literal<std::int32_t>(position, value);
- lit->type_decoration = decoration;
- return lit;
- }
- else if constexpr (std::is_same_v<T, std::uint32_t>)
- {
- auto *lit = new literal<std::uint32_t>(position, value);
- lit->type_decoration = decoration;
- return lit;
- }
- else if constexpr (std::is_same_v<T, double>)
- {
- auto *lit = new literal<double>(position, value);
- lit->type_decoration = decoration;
- return lit;
- }
- else if constexpr (std::is_same_v<T, bool>)
- {
- auto *lit = new literal<bool>(position, value);
- lit->type_decoration = decoration;
- return lit;
- }
- else if constexpr (std::is_same_v<T, unsigned char>)
- {
- auto *lit = new literal<unsigned char>(position, value);
- lit->type_decoration = decoration;
- return lit;
- }
- else if constexpr (std::is_same_v<T, std::nullptr_t>
- || std::is_same_v<T, global_address>)
- {
- auto *lit = new literal<std::nullptr_t>(position, nullptr);
- lit->type_decoration = decoration;
- return lit;
- }
- else
- {
- return nullptr;
- }
- }, value);
- }
-
- expression *evaluator::fold(expression& original)
- {
- auto value = evaluate(original);
- if (!value.has_value())
- {
- return &original;
- }
- if (expression *literal = value_to_expression(value.value(), original.position(), original.type_decoration))
- {
- delete &original;
- return literal;
- }
- if (auto *record = original.is_record_constructor())
- {
- for (auto& field_init : record->field_initializers)
- {
- fold_aggregate_field(field_init);
- }
- }
- else if (auto *array = original.is_array_constructor())
- {
- for (auto& element : array->elements)
- {
- element = fold(*element);
- }
- }
- return &original;
- }
-
- void evaluator::fold_aggregate_field(field_initializer& field_init)
- {
- auto value = evaluate(field_init.value());
- if (!value.has_value())
- {
- return;
- }
- expression *literal = value_to_expression(value.value(),
- field_init.value().position(), field_init.value().type_decoration);
- if (literal != nullptr)
- {
- field_init.value(*literal);
- return;
- }
- expression *folded = fold(field_init.value());
- if (folded != &field_init.value())
- {
- field_init.value(*folded);
- }
- }
-
- constant_folder::constant_folder(symbol_bag& bag, const target_info& target)
- : bag(bag), target(target), constant_evaluator(this->bag, this->target)
- {
- }
-
- void constant_folder::visit(variable_declaration* declaration)
- {
- if (declaration->initializer == nullptr || has_errors())
- {
- return;
- }
- auto computed = this->constant_evaluator.evaluate(*declaration->initializer);
- if (!computed)
- {
- add_error<non_constant_initializer_error>(declaration->initializer->position());
- return;
- }
- declaration->initializer = this->constant_evaluator.fold(*declaration->initializer);
-
- for (const auto& identifier : declaration->identifiers)
- {
- auto symbol = this->bag.lookup(identifier.name());
- if (symbol == nullptr)
- {
- continue;
- }
- if (auto var = symbol->is_variable();
- resolve_aliases(var->symbol).get<constant_type>() != nullptr)
- {
- var->value = computed;
- }
- }
- }
-
- void constant_folder::visit(cast_expression *expr)
- {
- expr->target().accept(this);
- expr->value().accept(this);
- expr->value(fold_trait(expr->value()));
- }
-
- void constant_folder::visit(binary_expression *expr)
- {
- expr->lhs().accept(this);
- expr->lhs(fold_trait(expr->lhs()));
- expr->rhs().accept(this);
- expr->rhs(fold_trait(expr->rhs()));
- }
-
- void constant_folder::visit(unary_expression *expr)
- {
- expr->operand().accept(this);
- expr->operand(fold_trait(expr->operand()));
- }
-
- void constant_folder::visit(procedure_call *call)
- {
- call->callable().accept(this);
- for (auto& argument : call->arguments)
- {
- argument->accept(this);
- expression& folded = fold_trait(*argument);
- if (&folded != argument)
- {
- delete argument;
- argument = &folded;
- }
- }
- }
-
- expression& constant_folder::fold_trait(expression& expr)
- {
- if (auto *trait = expr.is_traits())
- {
- if (auto value = this->constant_evaluator.evaluate_traits(*trait))
- {
- if (auto *literal = value_to_expression(value.value(), trait->position(), trait->type_decoration))
- {
- return *literal;
- }
- }
- }
- return expr;
- }
-
}
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 07f6d31..077b5d6 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 <map>
#include <utility>
namespace elna::boot
diff --git a/boot/result.cc b/boot/result.cc
index 86b0e7a..0eb5c12 100644
--- a/boot/result.cc
+++ b/boot/result.cc
@@ -17,6 +17,8 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/result.h"
+#include <numeric>
+
namespace elna::boot
{
location::location(const std::size_t line, const std::size_t column)
@@ -137,6 +139,49 @@ namespace elna::boot
{
return this->m_exported;
}
+
+ std::size_t constant_value_hash::operator()(const elna::boot::constant_value& value) const noexcept
+ {
+ return std::visit([](auto&& alternative) -> std::size_t {
+ using T = std::decay_t<decltype(alternative)>;
+
+ return std::hash<T>{}(alternative);
+ }, value);
+ }
+
+ bool constant_value_hash::operator()(const elna::boot::constant_value& lhs,
+ const elna::boot::constant_value& rhs) const noexcept
+ {
+ return std::visit([](auto&& first, auto&& second) -> bool {
+ using T = std::decay_t<decltype(first)>;
+ using U = std::decay_t<decltype(second)>;
+
+ if constexpr (std::is_same_v<T, U>)
+ {
+ return first == second;
+ }
+ else
+ {
+ return false;
+ }
+ }, lhs, rhs);
+ }
+
+ hash_accumulator hash_accumulator::operator+(const std::size_t& that) const
+ {
+ hash_accumulator result{};
+
+ result.m_seed ^= that + hash_accumulator::golden_ratio
+ // NOLINTNEXTLINE(readability-magic-numbers)
+ + (this->m_seed << 6) + (this->m_seed >> 2);
+
+ return result;
+ }
+
+ std::size_t hash_accumulator::seed() const
+ {
+ return this->m_seed;
+ }
}
std::size_t std::hash<elna::boot::identifier>::operator()(
@@ -144,3 +189,33 @@ std::size_t std::hash<elna::boot::identifier>::operator()(
{
return std::hash<std::string>{}(key.name());
}
+
+std::size_t std::hash<elna::boot::global_address>::operator()(
+ const elna::boot::global_address& key) const noexcept
+{
+ return std::hash<std::string>{}(key.name);
+}
+
+std::size_t std::hash<elna::boot::constant_aggregate<std::vector>>::operator()(
+ const elna::boot::constant_aggregate<std::vector>& key) const noexcept
+{
+ const elna::boot::constant_value_hash hasher{};
+ auto hash = std::accumulate(key->begin(), key->end(), elna::boot::hash_accumulator{},
+ [&hasher](const auto& accumulator, const auto& element) {
+ return accumulator + hasher(element);
+ });
+
+ return hash.seed();
+}
+
+std::size_t std::hash<elna::boot::constant_aggregate<elna::boot::ordered_map>>::operator()(
+ const elna::boot::constant_aggregate<elna::boot::ordered_map>& key) const noexcept
+{
+ const elna::boot::constant_value_hash hasher{};
+ auto hash = std::accumulate(key->begin(), key->end(), elna::boot::hash_accumulator{},
+ [&hasher](const auto& accumulator, const auto& element) {
+ return accumulator + std::hash<std::string>{}(element.first) + hasher(element.second);
+ });
+
+ return hash.seed();
+}
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 5e303fb..e0a89e6 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -26,46 +26,6 @@ namespace elna::boot
{
}
- type::type(std::shared_ptr<primitive_type> primitive)
- : payload(primitive)
- {
- }
-
- type::type(std::shared_ptr<record_type> record)
- : payload(record)
- {
- }
-
- type::type(std::shared_ptr<pointer_type> pointer)
- : payload(pointer)
- {
- }
-
- type::type(std::shared_ptr<constant_type> constant)
- : payload(constant)
- {
- }
-
- type::type(std::shared_ptr<array_type> array)
- : payload(array)
- {
- }
-
- type::type(std::shared_ptr<slice_type> slice)
- : payload(slice)
- {
- }
-
- type::type(std::shared_ptr<procedure_type> procedure)
- : payload(procedure)
- {
- }
-
- type::type(std::shared_ptr<enumeration_type> enumeration)
- : payload(enumeration)
- {
- }
-
template<typename T>
std::shared_ptr<T> type::get() const
{
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 2bb0ddd..343b5aa 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -198,9 +198,9 @@ namespace elna::boot
}
else if (auto record = referent.get<record_type>())
{
- for (const type_field& field : record->fields)
+ for (const auto& [field_name, field_type] : record->fields)
{
- if (contains_constant_member(field.second))
+ if (contains_constant_member(field_type))
{
return true;
}
@@ -660,14 +660,14 @@ namespace elna::boot
}
for (const field_initializer& initializer : expression->field_initializers)
{
- for (const type_field& field : record->fields)
+ for (const auto& [field_name, field_type]: record->fields)
{
- if (field.first == initializer.name())
+ if (field_name == initializer.name())
{
- if (!is_assignable_from(field.second, initializer.value().type_decoration))
+ if (!is_assignable_from(field_type, initializer.value().type_decoration))
{
add_error<type_mismatch_error>(
- initializer.value().position(), field.second,
+ initializer.value().position(), field_type,
initializer.value().type_decoration);
}
break;
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 });
+ }
+ }
+ }
+ }
+}