aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-28 19:19:32 +0200
committerEugen Wissner <belka@caraus.de>2026-07-28 19:19:32 +0200
commit36a274c9a8bca944234589220def025d3920b3ef (patch)
treef198ad272b74ffa3abb13e5063b55514d521e1f6
parent5cdaceb77af6a1d98145f8afb34ce6884e21a3d6 (diff)
downloadelna-36a274c9a8bca944234589220def025d3920b3ef.tar.gz
Enforce case label uniqueness and constness
-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
-rw-r--r--gcc/Make-lang.in1
-rw-r--r--gcc/gcc/elna-builtins.cc2
-rw-r--r--gcc/gcc/elna-generic.cc140
-rw-r--r--gcc/gcc/elna-tree.cc103
-rw-r--r--include/elna/boot/ast.h4
-rw-r--r--include/elna/boot/dependency.h6
-rw-r--r--include/elna/boot/evaluator.h60
-rw-r--r--include/elna/boot/name_analysis.h22
-rw-r--r--include/elna/boot/result.h126
-rw-r--r--include/elna/boot/symbol.h55
-rw-r--r--include/elna/boot/validation.h76
-rw-r--r--include/elna/gcc/elna-generic.h2
-rw-r--r--include/elna/gcc/elna-tree.h2
-rw-r--r--testsuite/fail_compilation/case_non_constant.elna2
-rw-r--r--testsuite/fail_compilation/case_unique_label.elna7
22 files changed, 678 insertions, 450 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 });
+ }
+ }
+ }
+ }
+}
diff --git a/gcc/Make-lang.in b/gcc/Make-lang.in
index 5b8b6a8..690d047 100644
--- a/gcc/Make-lang.in
+++ b/gcc/Make-lang.in
@@ -57,6 +57,7 @@ elna_OBJS = \
elna/type_check.o \
elna/symbol.o \
elna/result.o \
+ elna/validation.o \
$(END)
elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS)
diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc
index 226de4a..7e47c30 100644
--- a/gcc/gcc/elna-builtins.cc
+++ b/gcc/gcc/elna-builtins.cc
@@ -19,6 +19,8 @@ along with GCC; see the file COPYING3. If not see
#include "elna/gcc/elna1.h"
#include "stor-layout.h"
#include "stringpool.h"
+
+#include "elna/boot/evaluator.h"
#include "elna/gcc/elna-tree.h"
namespace elna::gcc
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 00581df..559d09c 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -16,10 +16,8 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include <array>
-#include <cstring>
#include <ranges>
-#include "elna/boot/evaluator.h"
#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna-generic.h"
#include "elna/gcc/elna1.h"
@@ -38,7 +36,8 @@ namespace elna::gcc
{
generic_visitor::generic_visitor(const std::shared_ptr<symbol_table>& symbol_table,
boot::symbol_bag bag, const boot::target_info& target)
- : bag(std::move(bag)), symbols(symbol_table), target(target)
+ : bag(std::move(bag)), symbols(symbol_table), target(target),
+ constant_evaluator(this->bag, this->target)
{
}
@@ -483,104 +482,14 @@ namespace elna::gcc
cgraph_node::finalize_function(fndecl, true);
}
- static tree constant_to_tree(const boot::constant_value& constant_value, tree type = NULL_TREE)
- {
- if (std::holds_alternative<std::int32_t>(constant_value))
- {
- return build_int_cst(elna_int_type_node, std::get<std::int32_t>(constant_value));
- }
- else if (std::holds_alternative<std::uint32_t>(constant_value))
- {
- return build_int_cstu(elna_word_type_node, std::get<std::uint32_t>(constant_value));
- }
-
- else if (std::holds_alternative<double>(constant_value))
- {
- auto real_value = std::get<double>(constant_value);
- REAL_VALUE_TYPE real;
- constexpr std::size_t bits_size = (sizeof(double) + sizeof(HOST_WIDE_INT) - 1) / sizeof(HOST_WIDE_INT);
- std::array<HOST_WIDE_INT, bits_size> target_bits;
-
- std::memcpy(target_bits.data(), &real_value, sizeof(real_value));
- real_from_target(&real, target_bits.data(), REAL_MODE_FORMAT(TYPE_MODE(elna_float_type_node)));
-
- return build_real(elna_float_type_node, real);
- }
- else if (std::holds_alternative<bool>(constant_value))
- {
- return std::get<bool>(constant_value) ? boolean_true_node : boolean_false_node;
- }
- else if (std::holds_alternative<unsigned char>(constant_value))
- {
- return build_int_cstu(elna_char_type_node, std::get<unsigned char>(constant_value));
- }
- else if (std::holds_alternative<std::nullptr_t>(constant_value))
- {
- return null_pointer_node;
- }
- else if (std::holds_alternative<boot::constant_aggregate<boot::ordered_map>>(constant_value))
- {
- // NOLINTNEXTLINE(readability-simplify-boolean-expr)
- if (type == NULL_TREE || !RECORD_OR_UNION_TYPE_P(type))
- {
- return NULL_TREE;
- }
- const auto& aggregate = std::get<boot::constant_aggregate<boot::ordered_map>>(constant_value);
- const auto& fields = *aggregate;
- vec<constructor_elt, va_gc> *tree_arguments = nullptr;
-
- for (const auto& [field_name, field_value] : fields)
- {
- tree field_decl = find_field_by_name(UNKNOWN_LOCATION, type, field_name);
- if (field_decl == error_mark_node)
- {
- return NULL_TREE;
- }
- tree value_tree = constant_to_tree(field_value, TREE_TYPE(field_decl));
- if (value_tree == NULL_TREE)
- {
- return NULL_TREE;
- }
- CONSTRUCTOR_APPEND_ELT(tree_arguments, field_decl, value_tree);
- }
- return build_constructor(type, tree_arguments);
- }
- else if (std::holds_alternative<boot::constant_aggregate<std::vector>>(constant_value))
- {
- if (type == NULL_TREE || TREE_CODE(type) != ARRAY_TYPE)
- {
- return NULL_TREE;
- }
- const auto& aggregate = std::get<boot::constant_aggregate<std::vector>>(constant_value);
- const auto& elements = *aggregate;
- tree domain = TYPE_DOMAIN(type);
- tree index = TYPE_MIN_VALUE(domain);
- tree element_type = TREE_TYPE(type);
- vec<constructor_elt, va_gc> *tree_arguments = nullptr;
-
- for (const auto& element : elements)
- {
- tree element_tree = constant_to_tree(element, element_type);
- if (element_tree == NULL_TREE)
- {
- return NULL_TREE;
- }
- CONSTRUCTOR_APPEND_ELT(tree_arguments, index, element_tree);
- index = int_const_binop(PLUS_EXPR, index, elna_word_one_node);
- }
- return build_constructor(type, tree_arguments);
- }
- return NULL_TREE;
- }
-
void generic_visitor::visit(boot::literal<std::int32_t> *literal)
{
- this->current_expression = constant_to_tree(boot::constant_value{ literal->value });
+ this->current_expression = constant_to_tree(boot::constant_value{ literal->value }, this->symbols);
}
void generic_visitor::visit(boot::literal<std::uint32_t> *literal)
{
- this->current_expression = constant_to_tree(boot::constant_value{ literal->value });
+ this->current_expression = constant_to_tree(boot::constant_value{ literal->value }, this->symbols);
}
void generic_visitor::visit(boot::literal<double> *literal)
@@ -600,17 +509,17 @@ namespace elna::gcc
void generic_visitor::visit(boot::literal<bool> *boolean)
{
- this->current_expression = constant_to_tree(boot::constant_value{ boolean->value });
+ this->current_expression = constant_to_tree(boot::constant_value{ boolean->value }, this->symbols);
}
void generic_visitor::visit(boot::literal<unsigned char> *character)
{
- this->current_expression = constant_to_tree(boot::constant_value{ character->value });
+ this->current_expression = constant_to_tree(boot::constant_value{ character->value }, this->symbols);
}
void generic_visitor::visit(boot::literal<std::nullptr_t> *)
{
- this->current_expression = constant_to_tree(boot::constant_value{ std::nullptr_t{} });
+ this->current_expression = constant_to_tree(boot::constant_value{ std::nullptr_t{} }, this->symbols);
}
void generic_visitor::visit(boot::literal<std::string> *string)
@@ -639,6 +548,19 @@ namespace elna::gcc
this->current_expression = build_constructor(slice_type, elms);
}
+ void generic_visitor::visit(boot::traits_expression *trait)
+ {
+ auto value = this->constant_evaluator.evaluate_traits(*trait);
+
+ if (!value.has_value())
+ {
+ this->current_expression = error_mark_node;
+ return;
+ }
+ tree type = get_inner_alias(trait->type_decoration, this->symbols);
+ this->current_expression = constant_to_tree(value.value(), this->symbols, type);
+ }
+
void generic_visitor::visit(boot::binary_expression *expression)
{
expression->lhs().accept(this);
@@ -794,15 +716,19 @@ namespace elna::gcc
location_t declaration_location = get_location(&declaration->position());
tree declaration_tree = this->symbols->lookup(variable_identifier.name());
+ auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable();
+
if (declaration_tree == NULL_TREE)
{
- auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable();
-
declaration_tree = declare_variable(variable_identifier.name(), *variable_symbol, this->symbols);
}
- // Set initializer if given. The constant_folder pass has
- // already folded the initializer into literals at this point.
- if (declaration->initializer != nullptr)
+ if (variable_symbol->value.has_value())
+ {
+ tree type = get_inner_alias(variable_symbol->symbol, this->symbols);
+ DECL_INITIAL(declaration_tree) = constant_to_tree(
+ variable_symbol->value.value(), this->symbols, type);
+ }
+ else if (declaration->initializer != nullptr)
{
declaration->initializer->accept(this);
DECL_INITIAL(declaration_tree) = this->current_expression;
@@ -811,13 +737,9 @@ namespace elna::gcc
{
DECL_INITIAL(declaration_tree) = null_pointer_node;
}
+ if (variable_symbol->symbol.get<boot::constant_type>() != nullptr)
{
- auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable();
-
- if (variable_symbol != nullptr && variable_symbol->symbol.get<boot::constant_type>() != nullptr)
- {
- TREE_READONLY(declaration_tree) = 1;
- }
+ TREE_READONLY(declaration_tree) = 1;
}
this->current_expression = NULL_TREE;
diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc
index e8a7daa..352d413 100644
--- a/gcc/gcc/elna-tree.cc
+++ b/gcc/gcc/elna-tree.cc
@@ -15,6 +15,9 @@ 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 <array>
+#include <cstring>
+
#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna1.h"
@@ -281,4 +284,104 @@ namespace elna::gcc
}
return NULL_TREE;
}
+
+ tree constant_to_tree(const boot::constant_value& constant_value,
+ const std::shared_ptr<symbol_table>& symbols, tree type)
+ {
+ if (std::holds_alternative<std::int32_t>(constant_value))
+ {
+ return build_int_cst(elna_int_type_node, std::get<std::int32_t>(constant_value));
+ }
+ else if (std::holds_alternative<std::uint32_t>(constant_value))
+ {
+ return build_int_cstu(elna_word_type_node, std::get<std::uint32_t>(constant_value));
+ }
+ else if (std::holds_alternative<double>(constant_value))
+ {
+ auto real_value = std::get<double>(constant_value);
+ REAL_VALUE_TYPE real;
+ constexpr std::size_t bits_size = (sizeof(double) + sizeof(HOST_WIDE_INT) - 1) / sizeof(HOST_WIDE_INT);
+ std::array<HOST_WIDE_INT, bits_size> target_bits;
+
+ std::memcpy(target_bits.data(), &real_value, sizeof(real_value));
+ real_from_target(&real, target_bits.data(), REAL_MODE_FORMAT(TYPE_MODE(elna_float_type_node)));
+
+ return build_real(elna_float_type_node, real);
+ }
+ else if (std::holds_alternative<bool>(constant_value))
+ {
+ return std::get<bool>(constant_value) ? boolean_true_node : boolean_false_node;
+ }
+ else if (std::holds_alternative<unsigned char>(constant_value))
+ {
+ return build_int_cstu(elna_char_type_node, std::get<unsigned char>(constant_value));
+ }
+ else if (std::holds_alternative<std::nullptr_t>(constant_value))
+ {
+ return null_pointer_node;
+ }
+ else if (std::holds_alternative<boot::global_address>(constant_value))
+ {
+ const auto& address = std::get<boot::global_address>(constant_value);
+ tree decl = symbols->lookup(address.name);
+ if (decl == NULL_TREE)
+ {
+ return NULL_TREE;
+ }
+ return build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(decl)), decl);
+ }
+ else if (std::holds_alternative<boot::constant_aggregate<boot::ordered_map>>(constant_value))
+ {
+ // NOLINTNEXTLINE(readability-simplify-boolean-expr)
+ if (type == NULL_TREE || !RECORD_OR_UNION_TYPE_P(type))
+ {
+ return NULL_TREE;
+ }
+ const auto& aggregate = std::get<boot::constant_aggregate<boot::ordered_map>>(constant_value);
+ const auto& fields = *aggregate;
+ vec<constructor_elt, va_gc> *tree_arguments = nullptr;
+
+ for (const auto& [field_name, field_value] : fields)
+ {
+ tree field_decl = find_field_by_name(UNKNOWN_LOCATION, type, field_name);
+ if (field_decl == error_mark_node)
+ {
+ return NULL_TREE;
+ }
+ tree value_tree = constant_to_tree(field_value, symbols, TREE_TYPE(field_decl));
+ if (value_tree == NULL_TREE)
+ {
+ return NULL_TREE;
+ }
+ CONSTRUCTOR_APPEND_ELT(tree_arguments, field_decl, value_tree);
+ }
+ return build_constructor(type, tree_arguments);
+ }
+ else if (std::holds_alternative<boot::constant_aggregate<std::vector>>(constant_value))
+ {
+ if (type == NULL_TREE || TREE_CODE(type) != ARRAY_TYPE)
+ {
+ return NULL_TREE;
+ }
+ const auto& aggregate = std::get<boot::constant_aggregate<std::vector>>(constant_value);
+ const auto& elements = *aggregate;
+ tree domain = TYPE_DOMAIN(type);
+ tree index = TYPE_MIN_VALUE(domain);
+ tree element_type = TREE_TYPE(type);
+ vec<constructor_elt, va_gc> *tree_arguments = nullptr;
+
+ for (const auto& element : elements)
+ {
+ tree element_tree = constant_to_tree(element, symbols, element_type);
+ if (element_tree == NULL_TREE)
+ {
+ return NULL_TREE;
+ }
+ CONSTRUCTOR_APPEND_ELT(tree_arguments, index, element_tree);
+ index = int_const_binop(PLUS_EXPR, index, elna_word_one_node);
+ }
+ return build_constructor(type, tree_arguments);
+ }
+ return NULL_TREE;
+ }
}
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index 8ee0e8b..9697d1c 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -214,8 +214,8 @@ namespace elna::boot
void visit(variable_declaration *) override;
void visit(procedure_declaration *) override;
- void visit(assign_statement *) override;
- void visit(if_statement *) override;
+ void visit(assign_statement *statement) override;
+ void visit(if_statement *statement) override;
void visit(import_declaration *) override;
void visit(while_statement *statement) override;
void visit(repeat_statement *statement) override;
diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h
index 6a3a412..b3c1155 100644
--- a/include/elna/boot/dependency.h
+++ b/include/elna/boot/dependency.h
@@ -17,13 +17,13 @@ along with GCC; see the file COPYING3. If not see
#pragma once
-#include <filesystem>
-#include <fstream>
-#include "elna/boot/evaluator.h"
#include "elna/boot/result.h"
#include "elna/boot/ast.h"
#include "elna/boot/symbol.h"
+#include <filesystem>
+#include <fstream>
+
namespace elna::boot
{
class dependency : public error_container
diff --git a/include/elna/boot/evaluator.h b/include/elna/boot/evaluator.h
index 5812ee8..8b36e1b 100644
--- a/include/elna/boot/evaluator.h
+++ b/include/elna/boot/evaluator.h
@@ -17,28 +17,14 @@ along with GCC; see the file COPYING3. If not see
#pragma once
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <string>
-#include <variant>
-
#include "elna/boot/symbol.h"
#include "elna/boot/ast.h"
+#include <memory>
+#include <optional>
+
namespace elna::boot
{
- /**
- * A module-level variable initializer must be a constant expression.
- */
- class non_constant_initializer_error : public error
- {
- public:
- explicit non_constant_initializer_error(const source_position position);
-
- std::string what() const override;
- };
-
std::optional<type_properties> get_type_properties(const type& subject, const target_info& target);
/**
@@ -73,7 +59,6 @@ namespace elna::boot
std::optional<constant_value> evaluate_cast(cast_expression& subject);
std::optional<std::size_t> evaluate_traits_size(const type& subject);
std::optional<std::size_t> evaluate_traits_alignment(const type& subject);
- void fold_aggregate_field(field_initializer& field_init);
public:
std::optional<constant_value> evaluate_traits(traits_expression& subject);
@@ -87,44 +72,5 @@ namespace elna::boot
* expression is not constant.
*/
std::optional<constant_value> evaluate(expression& subject);
-
- /**
- * Folds an expression into literals.
- *
- * For scalars, the original expression is deleted and a new
- * literal node is returned. For constructors, fields or
- * elements are folded recursively and the original node is
- * returned.
- *
- * \param original Expression to fold.
- * \return The folded expression (either a new literal or the
- * original aggregate with folded children).
- */
- expression *fold(expression& original);
- };
-
- /**
- * Folding pass that validates constant variable initializers.
- *
- * Runs after name analysis and type checking. Walks all variable
- * declarations, evaluates constant initializers, and records them
- * so later declarations can chain through earlier ones.
- */
- class constant_folder final : public walking_visitor, public error_container
- {
- symbol_bag& bag;
- const target_info& target;
- evaluator constant_evaluator;
-
- expression& fold_trait(expression& expr);
-
- public:
- constant_folder(symbol_bag& bag, const target_info& target);
-
- void visit(variable_declaration* declaration) override;
- void visit(cast_expression *expr) override;
- void visit(binary_expression *expr) override;
- void visit(unary_expression *expr) override;
- void visit(procedure_call *call) override;
};
}
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index a4f550d..8f53260 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -17,16 +17,15 @@ along with GCC; see the file COPYING3. If not see
#pragma once
+#include "elna/boot/ast.h"
+#include "elna/boot/result.h"
+#include "elna/boot/symbol.h"
+
#include <string>
#include <memory>
-#include <map>
#include <optional>
#include <variant>
-#include "elna/boot/ast.h"
-#include "elna/boot/result.h"
-#include "elna/boot/symbol.h"
-
namespace elna::boot
{
/**
@@ -83,8 +82,17 @@ namespace elna::boot
class member_error : public error
{
public:
- struct not_found { std::string name; type composite; };
- struct duplicate { std::string name; type aggregate; std::optional<source_position> original; std::optional<std::string> base; };
+ struct not_found
+ {
+ std::string name;
+ type composite;
+ };
+ struct duplicate {
+ std::string name;
+ type aggregate;
+ std::optional<source_position> original;
+ std::optional<std::string> base;
+ };
using payload_type = std::variant<not_found, duplicate>;
member_error(const source_position position, payload_type payload);
diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h
index 89753c1..42c96f2 100644
--- a/include/elna/boot/result.h
+++ b/include/elna/boot/result.h
@@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see
#include <cstddef>
#include <cstdint>
+#include <functional>
#include <string>
#include <deque>
#include <memory>
@@ -201,6 +202,11 @@ namespace elna::boot
using iterator = std::vector<value_type>::iterator;
using const_iterator = std::vector<value_type>::const_iterator;
+ auto operator<=>(const ordered_map& that) const
+ {
+ return this->payload <=> that.payload;
+ }
+
/**
* Finds element with specific key.
*
@@ -304,6 +310,11 @@ namespace elna::boot
{
return this->payload.cend();
}
+
+ std::size_t size() const
+ {
+ return this->payload.size();
+ }
};
/**
@@ -370,6 +381,21 @@ namespace elna::boot
constant_aggregate<std::vector>
>;
+ /**
+ * Hash and equality for \c constant_value, for use with std::unordered_map.
+ */
+ struct constant_value_hash
+ {
+ std::size_t operator()(const constant_value& value) const noexcept;
+ bool operator()(const constant_value& lhs, const constant_value& rhs) const noexcept;
+ };
+
+ /**
+ * Representation of array and record literals.
+ *
+ * \tparam C Used container, like \c std::vector or ordered_map.
+ * \tparam Alloc Allocator passed to the container.
+ */
template<template<typename, typename> typename C, template<typename> typename Alloc>
class constant_aggregate
{
@@ -406,6 +432,88 @@ namespace elna::boot
{
return this->container.get();
}
+
+ bool operator==(const constant_aggregate<std::vector>& that) const
+ requires std::is_same_v<Container, std::vector<constant_value, Alloc<constant_value>>>
+ {
+ if (this->container->size() != that.container->size())
+ {
+ return false;
+ }
+ return std::ranges::equal(*this->container, *that.container, constant_value_hash{});
+ }
+
+ bool operator==(const constant_aggregate<ordered_map>& that) const
+ requires std::is_same_v<Container, ordered_map<constant_value, Alloc<constant_value>>>
+ {
+ if (this->container->size() != that.container->size())
+ {
+ return false;
+ }
+ const constant_value_hash comparator{};
+
+ return std::ranges::equal(*this->container, *that.container,
+ [comparator](const auto& lhs, const auto& rhs) {
+ return lhs.first == rhs.first && comparator(lhs.second, rhs.second);
+ });
+ }
+
+ bool operator!=(const constant_aggregate& that) const
+ {
+ return !(*this == that);
+ }
+ };
+
+ /**
+ * Primary template, intentionally left undefined. Attempting to use this trait
+ * trait with a second template argument that is not a \c std::variant results
+ * in a compile error, since no matching specialization exists.
+ *
+ * \tparam T The type to search for among the variant's alternatives.
+ * \tparam V A \c std::variant type to search within.
+ *
+ * \see is_in_variant_v
+ */
+ template<typename T, typename V>
+ struct is_in_variant;
+
+ /**
+ * Partial specialization implementing the check for std::variant<Ts...>.
+ * The first argument is the tested type, the second is \c std::variant.
+ *
+ * \tparam T The type to search for.
+ * \tparam Ts The variant's alternative types.
+ *
+ * \see is_in_variant_v
+ */
+ template<typename T, typename... Ts>
+ struct is_in_variant<T, std::variant<Ts...>>
+ : std::disjunction<std::is_same<T, Ts>...>
+ {
+ };
+
+ /**
+ * Convenience variable template for is_in_variant.
+ *
+ * \tparam T The type to search for among the variant's alternatives.
+ * \tparam V A \c std::variant type to search within.
+ *
+ * \return Whether \p T is one of \p V's alternative types.
+ *
+ * \see is_in_variant
+ */
+ template <typename T, typename V>
+ inline constexpr bool is_in_variant_v = is_in_variant<T, V>::value;
+
+ struct hash_accumulator
+ {
+ hash_accumulator operator+(const std::size_t& that) const;
+
+ std::size_t seed() const;
+
+ private:
+ static constexpr std::size_t golden_ratio = 0x9e3779b9;
+ std::size_t m_seed{ 0 };
};
}
@@ -414,3 +522,21 @@ struct std::hash<elna::boot::identifier>
{
std::size_t operator()(const elna::boot::identifier& key) const noexcept;
};
+
+template<>
+struct std::hash<elna::boot::global_address>
+{
+ std::size_t operator()(const elna::boot::global_address& key) const noexcept;
+};
+
+template<>
+struct std::hash<elna::boot::constant_aggregate<std::vector>>
+{
+ std::size_t operator()(const elna::boot::constant_aggregate<std::vector>& key) const noexcept;
+};
+
+template<>
+struct std::hash<elna::boot::constant_aggregate<elna::boot::ordered_map>>
+{
+ std::size_t operator()(const elna::boot::constant_aggregate<elna::boot::ordered_map>& key) const noexcept;
+};
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index 46d9c2c..dd3e8ff 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -41,9 +41,15 @@ namespace elna::boot
struct procedure_type;
struct enumeration_type;
+ /**
+ * Represents a type stored in the symbol table.
+ *
+ * All types are wrapped in \c std::shared_ptr so that copying is cheap.
+ * There is also an empty type representing an error.
+ */
class type
{
- std::variant<
+ using Payload = std::variant<
std::monostate,
std::weak_ptr<alias_type>,
std::shared_ptr<primitive_type>,
@@ -54,21 +60,45 @@ namespace elna::boot
std::shared_ptr<slice_type>,
std::shared_ptr<procedure_type>,
std::shared_ptr<enumeration_type>
- > payload;
+ >;
+ Payload payload;
public:
+ /**
+ * Constructs an empty, invalid type.
+ */
type() = default;
+ /**
+ * Constructs a type alias.
+ *
+ * The specialization is required because the type is internally stored
+ * as \c std::weak_ptr.
+ *
+ * \param alias Stored type.
+ */
explicit type(std::shared_ptr<alias_type> alias);
- explicit type(std::shared_ptr<primitive_type> primitive);
- explicit type(std::shared_ptr<record_type> record);
- explicit type(std::shared_ptr<pointer_type> pointer);
- explicit type(std::shared_ptr<constant_type> constant);
- explicit type(std::shared_ptr<array_type> array);
- explicit type(std::shared_ptr<slice_type> slice);
- explicit type(std::shared_ptr<procedure_type> procedure);
- explicit type(std::shared_ptr<enumeration_type> enumeration);
+ /**
+ * Constructs a non empty type.
+ *
+ * \tparam T Type kind.
+ * \param value Concrete type.
+ */
+ template<typename T>
+ explicit type(std::shared_ptr<T> value)
+ requires is_in_variant_v<std::shared_ptr<T>, Payload>
+ : payload(std::move(value))
+ {
+ }
+
+ /**
+ * Checks whether \p T is currently stored and returns this concrete
+ * type.
+ *
+ * \tparam T Type kind to check.
+ * \return Concrete type or \c nullptr.
+ */
template<typename T>
std::shared_ptr<T> get() const;
@@ -76,6 +106,9 @@ namespace elna::boot
bool operator==(const type& other) const;
explicit operator bool() const;
+ /**
+ * \return Whether type holds no concrete type.
+ */
bool empty() const;
/**
@@ -129,8 +162,6 @@ namespace elna::boot
explicit primitive_type(const std::string& identifier);
};
- using type_field = std::pair<std::string, type>;
-
struct record_type
{
ordered_map<type> fields;
diff --git a/include/elna/boot/validation.h b/include/elna/boot/validation.h
new file mode 100644
index 0000000..c11a652
--- /dev/null
+++ b/include/elna/boot/validation.h
@@ -0,0 +1,76 @@
+/* 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/>. */
+
+#pragma once
+
+#include "elna/boot/ast.h"
+#include "elna/boot/evaluator.h"
+
+#include <string>
+
+namespace elna::boot
+{
+ /**
+ * Validation error.
+ */
+ class validation_error : public error
+ {
+ public:
+ struct non_constant_initializer
+ {
+ std::vector<identifier> identifiers;
+ };
+ struct duplicate_case
+ {
+ source_position first;
+ };
+ struct non_constant_case_label
+ {
+ };
+ using payload_type = std::variant<non_constant_initializer, duplicate_case, non_constant_case_label>;
+
+ validation_error(const source_position position, payload_type payload);
+
+ std::string what() const override;
+ std::optional<std::pair<std::string, source_position>> note() const override;
+
+ static validation_error non_constant_initializer_error(const source_position position,
+ const std::vector<identifier_definition>& identifiers);
+
+ private:
+ payload_type payload;
+ };
+
+ /**
+ * Validates:
+ * - case label uniqueness
+ * - Initializer constness.
+ */
+ class validation_visitor final : public walking_visitor, public error_container
+ {
+ symbol_bag& bag;
+ const target_info& target;
+ evaluator constant_evaluator;
+
+ public:
+ validation_visitor(symbol_bag& bag, const target_info& target);
+
+ void visit(variable_declaration* declaration) override;
+ void visit(procedure_declaration* declaration) override;
+ void visit(case_statement *statement) override;
+ };
+}
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index c2f154f..b5ba5e0 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -37,6 +37,7 @@ namespace elna::gcc
elna::boot::symbol_bag bag;
std::shared_ptr<symbol_table> symbols;
const elna::boot::target_info& target;
+ elna::boot::evaluator constant_evaluator;
static tree build_equality(boot::binary_expression *expression, tree left, tree right);
static tree build_equality_comparison(location_t loc, tree left, tree right,
@@ -79,6 +80,7 @@ namespace elna::gcc
void visit(boot::literal<unsigned char> *character) override;
void visit(boot::literal<std::nullptr_t> *) override;
void visit(boot::literal<std::string> *string) override;
+ void visit(boot::traits_expression *trait) override;
void visit(boot::binary_expression *expression) override;
void visit(boot::unary_expression *expression) override;
void visit(boot::variable_declaration *declaration) override;
diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h
index a989656..71cc13b 100644
--- a/include/elna/gcc/elna-tree.h
+++ b/include/elna/gcc/elna-tree.h
@@ -68,6 +68,8 @@ namespace elna::gcc
const elna::boot::target_info& get_host_target();
tree extract_constant(tree expression);
+ tree constant_to_tree(const boot::constant_value& value,
+ const std::shared_ptr<symbol_table>& symbols, tree type = NULL_TREE);
template<typename... Args>
tree call_built_in(location_t call_location, const char *name, tree return_type, Args... arguments)
diff --git a/testsuite/fail_compilation/case_non_constant.elna b/testsuite/fail_compilation/case_non_constant.elna
index 2f19df8..dc69703 100644
--- a/testsuite/fail_compilation/case_non_constant.elna
+++ b/testsuite/fail_compilation/case_non_constant.elna
@@ -11,6 +11,6 @@ var
begin
x := r;
case x of
- r: (* @Error Expected a constant expression *)
+ r: (* @Error Case label must be a constant expression *)
end
end.
diff --git a/testsuite/fail_compilation/case_unique_label.elna b/testsuite/fail_compilation/case_unique_label.elna
new file mode 100644
index 0000000..a97fb37
--- /dev/null
+++ b/testsuite/fail_compilation/case_unique_label.elna
@@ -0,0 +1,7 @@
+begin
+ case 3 of
+ 1:
+ | 3:
+ | 1 + 2: (* @Error Duplicate case label *)
+ end
+end.