From b2dee14873402ee3d13d59ae5579593796ea862f Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 5 Aug 2026 01:03:55 +0200 Subject: Check that constants are initialized --- boot/evaluator.cc | 101 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 31 deletions(-) (limited to 'boot/evaluator.cc') diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 8f414fa..407370a 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -19,13 +19,56 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/ast.h" -#include #include #include #include namespace elna::boot { + non_constant_expression_error::non_constant_expression_error(const source_position position, payload_type payload) + : error(position), payload(std::move(payload)) + { + } + + std::string non_constant_expression_error::what() const + { + return std::visit([](const auto& payload) -> std::string { + using T = std::decay_t; + + if constexpr (std::is_same_v) + { + return "Variable initializers must be constant expressions"; + } + else if constexpr (std::is_same_v) + { + return "Case label must be a constant expression"; + } + else if constexpr (std::is_same_v) + { + return "Array dimensions for type '" + payload.array_type.to_string() + + "' should be constant"; + } + }, this->payload); + } + + std::optional> non_constant_expression_error::note() const + { + return std::visit([](const auto& payload) -> std::optional> { + using T = std::decay_t; + + if constexpr (std::is_same_v) + { + auto position_span = source_position(payload.identifiers.front().position().start(), + payload.identifiers.back().position().end()); + return std::make_pair(join(payload.identifiers), position_span); + } + else + { + return std::nullopt; + } + }, this->payload); + } + std::optional get_type_properties(const type& subject, const target_info& target) { auto resolved = resolve_underlying_type(subject); @@ -209,6 +252,8 @@ namespace elna::boot std::optional evaluator::evaluate_literal(literal_expression& subject) { + // The type decoration here is trustable since the name analysis derives + // the decoration from literal's own value. type const decoration = subject.type_decoration; if (is_primitive_type(decoration, "Int") || is_primitive_type(decoration, "Word")) @@ -283,54 +328,49 @@ namespace elna::boot auto resolved_base = resolve_underlying_type(type_to_check); if (auto enumeration = resolved_base.get()) { - auto enumeration_position = std::distance(enumeration->members.begin(), + auto enumeration_distance = std::distance(enumeration->members.begin(), std::ranges::find(enumeration->members, subject.field().name())); + const std::size_t enumeration_position = static_cast(enumeration_distance); - return constant_value{ - integer_literal::from(static_cast(enumeration_position + 1)) - }; + if (enumeration_position >= enumeration->members.size()) + { + return std::nullopt; + } + return constant_value{ integer_literal::from(enumeration_position + 1U) }; } else if (auto base = evaluate(subject.base())) { if (auto *record = std::get_if>(&base.value())) { - return (**record)[subject.field().name()]; + auto field_iterator = (*record)->find(subject.field().name()); + + return field_iterator != std::cend(**record) + ? std::make_optional(field_iterator->second) + : std::nullopt; + } + else if (auto *vector = std::get_if>(&base.value()); + subject.field() == "length") + { + return constant_value{ integer_literal::from((*vector)->size()) }; } else if (auto *string_value = std::get_if(&base.value()); subject.field() == "length") { return constant_value{ integer_literal::from(string_value->size()) }; } } - else if (auto array = resolved_base.get(); subject.field() == "length") - { - return constant_value{ integer_literal::from(array->size) }; - } return std::nullopt; } std::optional evaluator::evaluate_index(expression& subject) { auto evaluated_index = evaluate(subject); - if (!evaluated_index.has_value()) + if (!evaluated_index.has_value() || !std::holds_alternative(evaluated_index.value())) { return std::nullopt; } - auto index_literal = std::get(evaluated_index.value()); - - if (index_literal.is_signed()) - { - auto signed_index = index_literal.to_signed(); + auto& index_literal = std::get(evaluated_index.value()); - if (!signed_index.has_value() || signed_index.value() <= 0) - { - return std::nullopt; - } - return static_cast(signed_index.value()); - } - else - { - return index_literal.to_unsigned(); - } + return index_literal.is_negative() ? std::nullopt : index_literal.to_unsigned(); } std::optional evaluator::evaluate_slicing(slicing_expression& subject) @@ -656,14 +696,13 @@ namespace elna::boot std::optional evaluator::evaluate_cast(cast_expression& subject) { - if (auto value = evaluate(subject.value())) - { - return constant_value{ value.value() }; - } - else + auto value = evaluate(subject.value()); + + if (!value.has_value() || subject.type_decoration.empty()) { return std::nullopt; } + return constant_value{ value.value() }; } std::optional evaluator::evaluate_traits_size(const type& subject) -- cgit v1.2.3