From eaf5934f81dcd8ce5705f13b651d7b93ca8c459f Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 6 Aug 2026 23:12:01 +0200 Subject: Implement compile time castings for integers --- boot/evaluator.cc | 73 +++++++++++++++++++++++++++++++++++++++- boot/result.cc | 26 ++------------- gcc/gcc/elna-tree.cc | 4 +-- include/elna/boot/evaluator.h | 3 ++ include/elna/boot/result.h | 77 ++++++++++++++++++++++++++++++------------- 5 files changed, 134 insertions(+), 49 deletions(-) diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 407370a..14ca889 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -370,7 +370,7 @@ namespace elna::boot } auto& index_literal = std::get(evaluated_index.value()); - return index_literal.is_negative() ? std::nullopt : index_literal.to_unsigned(); + return index_literal < 0 ? std::nullopt : index_literal.try_to(); } std::optional evaluator::evaluate_slicing(slicing_expression& subject) @@ -694,6 +694,66 @@ namespace elna::boot }, lhs.value()); } + std::optional evaluator::cast_to_int(const constant_value& source_expression) + { + return std::visit([](auto&& source_value) -> std::optional { + using T = std::decay_t; + + if constexpr (std::is_same_v) + { + std::optional signed_value = source_value.template try_to(); + + return signed_value.has_value() + ? std::optional(integer_literal::from(signed_value.value())) + : std::nullopt; + } + else if constexpr (std::is_same_v + || std::is_same_v + || std::is_same_v) + { + return integer_literal::from(static_cast(source_value)); + } + else if constexpr (std::is_same_v) + { + return integer_literal::from(0); + } + else + { + return std::nullopt; + } + }, source_expression); + } + + std::optional evaluator::cast_to_word(const constant_value& source_expression) + { + return std::visit([](auto&& source_value) -> std::optional { + using T = std::decay_t; + + if constexpr (std::is_same_v) + { + std::optional unsigned_value = source_value.template try_to(); + + return unsigned_value.has_value() + ? std::optional(integer_literal::from(unsigned_value.value())) + : std::nullopt; + } + else if constexpr (std::is_same_v + || std::is_same_v + || std::is_same_v) + { + return integer_literal::from(static_cast(source_value)); + } + else if constexpr (std::is_same_v) + { + return integer_literal::from(0U); + } + else + { + return std::nullopt; + } + }, source_expression); + } + std::optional evaluator::evaluate_cast(cast_expression& subject) { auto value = evaluate(subject.value()); @@ -702,6 +762,17 @@ namespace elna::boot { return std::nullopt; } + const type resolved = resolve_underlying_type(subject.type_decoration); + + if (is_primitive_type(resolved, "Int")) + { + return cast_to_int(value.value()); + } + else if (is_primitive_type(resolved, "Word")) + { + return cast_to_word(value.value()); + } + return constant_value{ value.value() }; } diff --git a/boot/result.cc b/boot/result.cc index e9090de..30b4576 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -328,28 +328,6 @@ namespace elna::boot return *this; } - std::optional integer_literal::to_signed() const - { - if (is_negative_minimum(sizeof(std::ptrdiff_t) * CHAR_BIT)) - { - return std::numeric_limits::min(); - } - auto [written, rop] = export_to_words(); - - if (written > 1 || rop < 0) - { - return std::nullopt; - } - return is_negative() ? -rop : rop; - } - - std::optional integer_literal::to_unsigned() const - { - auto [written, rop] = export_to_words(); - - 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)) @@ -485,14 +463,14 @@ std::size_t std::hash::operator()(const elna::boot: { if (key.is_signed()) { - if (auto converted = key.to_signed()) + if (auto converted = key.try_to()) { return std::hash{}(*converted); } } else { - if (auto converted = key.to_unsigned()) + if (auto converted = key.try_to()) { return std::hash{}(*converted); } diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index 69ef37f..10efb90 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -280,7 +280,7 @@ namespace elna::gcc if (literal_value.is_signed()) { - if (auto converted = literal_value.to_signed()) + if (auto converted = literal_value.try_to()) { return build_int_cst(elna_int_type_node, *converted); } @@ -288,7 +288,7 @@ namespace elna::gcc } else { - if (auto converted = literal_value.to_unsigned()) + if (auto converted = literal_value.try_to()) { return build_int_cstu(elna_word_type_node, *converted); } diff --git a/include/elna/boot/evaluator.h b/include/elna/boot/evaluator.h index 0d1a7c8..02db6ab 100644 --- a/include/elna/boot/evaluator.h +++ b/include/elna/boot/evaluator.h @@ -86,6 +86,9 @@ namespace elna::boot std::optional evaluate_traits_size(const type& subject); std::optional evaluate_traits_alignment(const type& subject); + static std::optional cast_to_int(const constant_value& source_expression); + static std::optional cast_to_word(const constant_value& source_expression); + public: std::optional evaluate_index(expression& subject); std::optional evaluate_traits(traits_expression& subject); diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 3d01eb2..49e078e 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -226,10 +226,10 @@ namespace elna::boot * * \tparam T The examined type. * - * \see is_unsigned + * \see is_unsigned_v */ template - inline constexpr bool is_signed = std::is_same_v + inline constexpr bool is_signed_v = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; @@ -239,10 +239,10 @@ namespace elna::boot * * \tparam T The examined type. * - * \see is_signed + * \see is_signed_v */ template - inline constexpr bool is_unsigned = std::is_same_v + inline constexpr bool is_unsigned_v = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; @@ -255,7 +255,7 @@ namespace elna::boot * \tparam T The examined type. */ template - inline constexpr bool is_integral = is_signed || is_unsigned; + inline constexpr bool is_integral = is_signed_v || is_unsigned_v; struct integer_literal { @@ -281,25 +281,25 @@ namespace elna::boot std::weak_ordering operator<=>(const integer_literal& that) const; template bool operator==(U that) const - requires(is_unsigned && sizeof(U) <= sizeof(unsigned long int)) + requires(is_unsigned_v && sizeof(U) <= sizeof(unsigned long int)) { return mpz_cmp_ui(this->raw, that) == 0; } template bool operator==(U that) const - requires(is_signed && sizeof(U) <= sizeof(signed long int)) + requires(is_signed_v && sizeof(U) <= sizeof(signed long int)) { return mpz_cmp_si(this->raw, that) == 0; } template std::weak_ordering operator<=>(U that) const - requires(is_unsigned && sizeof(U) <= sizeof(unsigned long int)) + requires(is_unsigned_v && sizeof(U) <= sizeof(unsigned long int)) { return mpz_cmp_ui(this->raw, that) <=> 0; } template std::weak_ordering operator<=>(U that) const - requires(is_signed && sizeof(U) <= sizeof(signed long int)) + requires(is_signed_v && sizeof(U) <= sizeof(signed long int)) { return mpz_cmp_si(this->raw, that) <=> 0; } @@ -312,23 +312,31 @@ namespace elna::boot std::string to_string(const std::uint8_t base = 10U) const; /** - * \return Whether the stored value is a negative integer. - */ - bool is_negative() const; - - /** - * Exports the stored value as a host \c std::ptrdiff_t. + * Exports the stored value as a host \p T. * - * \return The converted value, or \c std::nullopt if out of range. - */ - std::optional to_signed() const; - - /** - * Exports the stored value as a host \c std::size_t. + * This function tries to shrink or extend the value to fit into the + * target type if possible. * + * \tparam Target type. * \return The converted value, or \c std::nullopt if out of range. */ - std::optional to_unsigned() const; + template + std::optional try_to() const + requires is_integral + { + if constexpr (is_signed_v) + { + return to_signed(); + } + else if constexpr (is_unsigned_v) + { + return to_unsigned(); + } + else + { + static_assert("integer_literal::to expected only integral types"); + } + } /** * Attempts to narrow this literal's internally stored size to @@ -366,6 +374,7 @@ namespace elna::boot integer_literal(bool is_signed, std::size_t size); bool fits_in(const std::size_t bits) const; std::optional check() &&; + bool is_negative() const; bool is_negative_minimum(const std::size_t bits) const; bool is_negative_minimum() const; std::size_t bits() const; @@ -381,6 +390,30 @@ namespace elna::boot return { written, rop[0] }; } + + template + std::optional to_signed() const + { + if (is_negative_minimum(sizeof(T) * CHAR_BIT)) + { + return std::numeric_limits::min(); + } + auto [written, rop] = export_to_words(); + + if (written > 1 || rop < 0) + { + return std::nullopt; + } + return is_negative() ? -rop : rop; + } + + template + std::optional to_unsigned() const + { + auto [written, rop] = export_to_words(); + + return written > 1 ? std::nullopt : std::make_optional(rop); + } }; /** -- cgit v1.2.3