From 71b3bbb7698b13b42c99a4ca66c27db4a6189ff8 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 1 Aug 2026 16:05:51 +0200 Subject: Enforce integral values in slice ranges --- boot/evaluator.cc | 35 ++++++++++++++------------------- boot/lexer.ll | 8 ++++---- boot/parser.yy | 4 ++-- boot/result.cc | 57 +++++++++++++++++++++++++++++++++++++++++++----------- boot/type_check.cc | 34 ++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 37 deletions(-) (limited to 'boot') diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 14b8ea3..56ba270 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -21,7 +21,6 @@ along with GCC; see the file COPYING3. If not see #include #include -#include #include #include @@ -212,27 +211,23 @@ namespace elna::boot { type const decoration = subject.type_decoration; - if (is_primitive_type(decoration, "Int")) + if (is_primitive_type(decoration, "Int") || is_primitive_type(decoration, "Word")) { return constant_value{ static_cast&>(subject).value }; } - if (is_primitive_type(decoration, "Word")) - { - return constant_value{ static_cast&>(subject).value }; - } - if (is_primitive_type(decoration, "Float")) + else if (is_primitive_type(decoration, "Float")) { return constant_value{ static_cast&>(subject).value }; } - if (is_primitive_type(decoration, "Bool")) + else if (is_primitive_type(decoration, "Bool")) { return constant_value{ static_cast&>(subject).value }; } - if (is_primitive_type(decoration, "Char")) + else if (is_primitive_type(decoration, "Char")) { return constant_value{ static_cast&>(subject).value }; } - if (is_primitive_type(decoration, "Pointer")) + else if (is_primitive_type(decoration, "Pointer")) { return constant_value{ std::nullptr_t{} }; } @@ -289,7 +284,7 @@ namespace elna::boot { auto enumeration_position = std::distance(enumeration->members.begin(), member_iterator) + 1; return constant_value{ - integer_literal::from(static_cast(enumeration_position)) + integer_literal::from(static_cast(enumeration_position)) }; } return std::nullopt; @@ -704,14 +699,14 @@ namespace elna::boot { if (auto size = evaluate_traits_size(subject.types.front())) { - return constant_value{ integer_literal::from(static_cast(size.value())) }; + return constant_value{ integer_literal::from(size.value()) }; } } else if (subject.name.name() == "alignment") { if (auto alignment = evaluate_traits_alignment(subject.types.front())) { - return constant_value{ integer_literal::from(static_cast(alignment.value())) }; + return constant_value{ integer_literal::from(alignment.value()) }; } } else if (subject.name.name() == "min") @@ -720,11 +715,11 @@ namespace elna::boot if (is_primitive_type(resolved, "Int")) { - return constant_value{ integer_literal::from(std::numeric_limits::min()) }; + return constant_value{ integer_literal::from(std::numeric_limits::min()) }; } if (is_primitive_type(resolved, "Word")) { - return constant_value{ integer_literal::from(0) }; + return constant_value{ integer_literal::from(0) }; } if (is_primitive_type(resolved, "Char")) { @@ -740,7 +735,7 @@ namespace elna::boot } if (auto enumeration = resolved.get()) { - return constant_value{ integer_literal::from(1) }; + return constant_value{ integer_literal::from(1) }; } } else if (subject.name.name() == "max") @@ -749,11 +744,11 @@ namespace elna::boot if (is_primitive_type(resolved, "Int")) { - return constant_value{ integer_literal::from(std::numeric_limits::max()) }; + return constant_value{ integer_literal::from(std::numeric_limits::max()) }; } if (is_primitive_type(resolved, "Word")) { - return constant_value{ integer_literal::from(std::numeric_limits::max()) }; + return constant_value{ integer_literal::from(std::numeric_limits::max()) }; } if (is_primitive_type(resolved, "Char")) { @@ -769,7 +764,7 @@ namespace elna::boot } if (auto enumeration = resolved.get()) { - return constant_value{ integer_literal::from(static_cast(enumeration->members.size())) }; + return constant_value{ integer_literal::from(enumeration->members.size()) }; } } else if (subject.name.name() == "offset") @@ -786,7 +781,7 @@ namespace elna::boot if (field_search != std::cend(record_layout.value().offset_map)) { - return constant_value{ integer_literal::from(static_cast(field_search->second)) }; + return constant_value{ integer_literal::from(field_search->second) }; } } } diff --git a/boot/lexer.ll b/boot/lexer.ll index 89fe85c..ba54433 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -174,7 +174,7 @@ to { return yy::parser::make_TRAIT(yytext + 1, this->location); } [[:digit:]]+u { - unsigned long result = strtoul(yytext, NULL, 10); + std::uint64_t result = strtoull(yytext, NULL, 10); if (errno == ERANGE) { @@ -186,7 +186,7 @@ to { } } [[:digit:]]+ { - long result = strtol(yytext, NULL, 10); + std::int64_t result = strtoll(yytext, NULL, 10); if (errno == ERANGE) { @@ -198,7 +198,7 @@ to { } } 0[x|X]{HIGIT}+ { - unsigned long result = strtoul(yytext, NULL, 16); + std::uint64_t result = strtoull(yytext, NULL, 16); if (errno == ERANGE) { @@ -210,7 +210,7 @@ to { } } 0[b|B]{BIGIT}+ { - unsigned long result = strtoul(yytext, NULL, 2); + std::uint64_t result = strtoull(yytext, NULL, 2); if (errno == ERANGE) { diff --git a/boot/parser.yy b/boot/parser.yy index 670a265..bc0cb53 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -78,8 +78,8 @@ along with GCC; see the file COPYING3. If not see %token IDENTIFIER %token TRAIT -%token INTEGER -%token WORD +%token INTEGER +%token WORD %token FLOAT %token CHARACTER %token STRING diff --git a/boot/result.cc b/boot/result.cc index 4d4c20f..f769625 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -17,6 +17,7 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/result.h" +#include #include namespace elna::boot @@ -340,6 +341,16 @@ namespace elna::boot return written > 1 ? std::nullopt : std::make_optional(rop); } + bool integer_literal::fit_into(const std::size_t target_size) + { + if (fits_in(target_size * CHAR_BIT)) + { + this->m_size = target_size; + return true; + } + return false; + } + bool integer_literal::is_signed() const { return this->m_signed; @@ -350,6 +361,18 @@ namespace elna::boot return this->m_size; } + std::string integer_literal::to_string(const std::uint8_t base) const + { + // +1 sign, +1 null terminator + const size_t buffer_size = mpz_sizeinbase(this->raw, static_cast(base)) + 2; + std::string result(buffer_size, '\0'); + + mpz_get_str(result.data(), base, this->raw); + result.resize(std::strlen(result.c_str())); + + return result; + } + void swap(integer_literal& lhs, integer_literal& rhs) noexcept { mpz_swap(lhs.raw, rhs.raw); @@ -357,22 +380,20 @@ namespace elna::boot std::swap(lhs.m_size, rhs.m_size); } - std::optional integer_literal::check() && + bool integer_literal::fits_in(const std::size_t bits) const { std::size_t required_bits = mpz_sizeinbase(this->raw, 2); - if (!is_negative_minimum()) + if (!is_negative_minimum(bits)) { ++required_bits; // Add one bit for the sign. } - if (required_bits > bits() || (mpz_sgn(this->raw) < 0 && !is_signed())) - { - return std::nullopt; - } - else - { - return std::move(*this); - } + return required_bits <= bits && (mpz_sgn(this->raw) >= 0 || is_signed()); + } + + std::optional integer_literal::check() && + { + return fits_in(bits()) ? std::make_optional(std::move(*this)) : std::nullopt; } bool integer_literal::is_negative_minimum(const std::size_t bits) const @@ -454,5 +475,19 @@ std::size_t std::hash>:: std::size_t std::hash::operator()(const elna::boot::integer_literal& key) const noexcept { - return std::hash{}(key.to()); + if (key.is_signed()) + { + if (auto converted = key.to_signed()) + { + return std::hash{}(*converted); + } + } + else + { + if (auto converted = key.to_unsigned()) + { + return std::hash{}(*converted); + } + } + return 0; } diff --git a/boot/type_check.cc b/boot/type_check.cc index 07deb2d..529f66a 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -165,6 +165,11 @@ namespace elna::boot return "Type '" + this->actual.to_string() + "' cannot be converted to '" + payload.target.to_string() + "'"; } + else if constexpr (std::is_same_v) + { + return "The number " + payload.overflow.to_string() + + " does not fit into the type '" + actual.to_string() + "'"; + } else if constexpr (std::is_same_v) { switch (payload) @@ -762,6 +767,17 @@ namespace elna::boot void type_analysis_visitor::visit(slicing_expression *expression) { walking_visitor::visit(expression); + + if (!is_integral_type(resolve_underlying_type(expression->start().type_decoration))) + { + add_error(expression->start().position(), + expression->start().type_decoration, type_mismatch_error::kind::array_index); + } + if (!is_integral_type(resolve_underlying_type(expression->end().type_decoration))) + { + add_error(expression->end().position(), + expression->end().type_decoration, type_mismatch_error::kind::array_index); + } } void type_analysis_visitor::visit(array_access_expression *expression) @@ -1008,4 +1024,22 @@ namespace elna::boot } } } + + void type_analysis_visitor::visit(literal *expression) + { + bool narrowed{ false }; + if (expression->value.is_signed()) + { + narrowed = expression->value.fit_into(target.int_properties.size); + } + else + { + narrowed = expression->value.fit_into(target.word_properties.size); + } + if (!narrowed) + { + add_error(expression->position(), expression->type_decoration, + type_mismatch_error::integer_literal_overflow{ expression->value }); + } + } } -- cgit v1.2.3