aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-08-01 16:05:51 +0200
committerEugen Wissner <belka@caraus.de>2026-08-01 16:05:51 +0200
commit71b3bbb7698b13b42c99a4ca66c27db4a6189ff8 (patch)
tree1fc6ed246ecc0cf561f38ad774a4815ba357ea7b /boot
parent8539c10542a4be1e1127daea60bf1b1e4c37e092 (diff)
downloadelna-71b3bbb7698b13b42c99a4ca66c27db4a6189ff8.tar.gz
Enforce integral values in slice ranges
Diffstat (limited to 'boot')
-rw-r--r--boot/evaluator.cc35
-rw-r--r--boot/lexer.ll8
-rw-r--r--boot/parser.yy4
-rw-r--r--boot/result.cc57
-rw-r--r--boot/type_check.cc34
5 files changed, 101 insertions, 37 deletions
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 <algorithm>
#include <cstddef>
-#include <cstdint>
#include <limits>
#include <ranges>
@@ -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<literal<integer_literal>&>(subject).value };
}
- if (is_primitive_type(decoration, "Word"))
- {
- return constant_value{ static_cast<literal<integer_literal>&>(subject).value };
- }
- if (is_primitive_type(decoration, "Float"))
+ else if (is_primitive_type(decoration, "Float"))
{
return constant_value{ static_cast<literal<double>&>(subject).value };
}
- if (is_primitive_type(decoration, "Bool"))
+ else if (is_primitive_type(decoration, "Bool"))
{
return constant_value{ static_cast<literal<bool>&>(subject).value };
}
- if (is_primitive_type(decoration, "Char"))
+ else if (is_primitive_type(decoration, "Char"))
{
return constant_value{ static_cast<literal<unsigned char>&>(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<std::int32_t>(enumeration_position))
+ integer_literal::from(static_cast<std::size_t>(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<std::uint32_t>(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<std::uint32_t>(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<std::int32_t>::min()) };
+ return constant_value{ integer_literal::from(std::numeric_limits<std::ptrdiff_t>::min()) };
}
if (is_primitive_type(resolved, "Word"))
{
- return constant_value{ integer_literal::from<std::uint32_t>(0) };
+ return constant_value{ integer_literal::from<std::size_t>(0) };
}
if (is_primitive_type(resolved, "Char"))
{
@@ -740,7 +735,7 @@ namespace elna::boot
}
if (auto enumeration = resolved.get<enumeration_type>())
{
- return constant_value{ integer_literal::from<std::int32_t>(1) };
+ return constant_value{ integer_literal::from<std::size_t>(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<std::int32_t>::max()) };
+ return constant_value{ integer_literal::from(std::numeric_limits<std::ptrdiff_t>::max()) };
}
if (is_primitive_type(resolved, "Word"))
{
- return constant_value{ integer_literal::from(std::numeric_limits<std::uint32_t>::max()) };
+ return constant_value{ integer_literal::from(std::numeric_limits<std::size_t>::max()) };
}
if (is_primitive_type(resolved, "Char"))
{
@@ -769,7 +764,7 @@ namespace elna::boot
}
if (auto enumeration = resolved.get<enumeration_type>())
{
- return constant_value{ integer_literal::from(static_cast<std::int32_t>(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<std::uint32_t>(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 <std::string> IDENTIFIER
%token <std::string> TRAIT
-%token <std::int32_t> INTEGER
-%token <std::uint32_t> WORD
+%token <std::int64_t> INTEGER
+%token <std::uint64_t> WORD
%token <double> FLOAT
%token <std::string> CHARACTER
%token <std::string> 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 <cstring>
#include <numeric>
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<int>(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> 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> 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<elna::boot::constant_aggregate<elna::boot::ordered_map>>::
std::size_t std::hash<elna::boot::integer_literal>::operator()(const elna::boot::integer_literal& key) const noexcept
{
- return std::hash<std::uint32_t>{}(key.to<std::uint32_t>());
+ if (key.is_signed())
+ {
+ if (auto converted = key.to_signed())
+ {
+ return std::hash<std::ptrdiff_t>{}(*converted);
+ }
+ }
+ else
+ {
+ if (auto converted = key.to_unsigned())
+ {
+ return std::hash<std::size_t>{}(*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<T, integer_literal_overflow>)
+ {
+ return "The number " + payload.overflow.to_string()
+ + " does not fit into the type '" + actual.to_string() + "'";
+ }
else if constexpr (std::is_same_v<T, kind>)
{
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<type_mismatch_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<type_mismatch_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<integer_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<type_mismatch_error>(expression->position(), expression->type_decoration,
+ type_mismatch_error::integer_literal_overflow{ expression->value });
+ }
+ }
}