diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-08-06 23:12:01 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-08-06 23:12:01 +0200 |
| commit | eaf5934f81dcd8ce5705f13b651d7b93ca8c459f (patch) | |
| tree | 890d2d69a3e80d1d3a64e69cb4af69a190986c5f | |
| parent | b2dee14873402ee3d13d59ae5579593796ea862f (diff) | |
| download | elna-eaf5934f81dcd8ce5705f13b651d7b93ca8c459f.tar.gz | |
Implement compile time castings for integers
| -rw-r--r-- | boot/evaluator.cc | 73 | ||||
| -rw-r--r-- | boot/result.cc | 26 | ||||
| -rw-r--r-- | gcc/gcc/elna-tree.cc | 4 | ||||
| -rw-r--r-- | include/elna/boot/evaluator.h | 3 | ||||
| -rw-r--r-- | 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<integer_literal>(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::size_t>(); } std::optional<constant_value> evaluator::evaluate_slicing(slicing_expression& subject) @@ -694,6 +694,66 @@ namespace elna::boot }, lhs.value()); } + std::optional<constant_value> evaluator::cast_to_int(const constant_value& source_expression) + { + return std::visit([](auto&& source_value) -> std::optional<constant_value> { + using T = std::decay_t<decltype(source_value)>; + + if constexpr (std::is_same_v<T, integer_literal>) + { + std::optional<std::ptrdiff_t> signed_value = source_value.template try_to<std::ptrdiff_t>(); + + return signed_value.has_value() + ? std::optional(integer_literal::from(signed_value.value())) + : std::nullopt; + } + else if constexpr (std::is_same_v<T, double> + || std::is_same_v<T, bool> + || std::is_same_v<T, unsigned char>) + { + return integer_literal::from(static_cast<std::ptrdiff_t>(source_value)); + } + else if constexpr (std::is_same_v<T, std::nullptr_t>) + { + return integer_literal::from<std::ptrdiff_t>(0); + } + else + { + return std::nullopt; + } + }, source_expression); + } + + std::optional<constant_value> evaluator::cast_to_word(const constant_value& source_expression) + { + return std::visit([](auto&& source_value) -> std::optional<constant_value> { + using T = std::decay_t<decltype(source_value)>; + + if constexpr (std::is_same_v<T, integer_literal>) + { + std::optional<std::size_t> unsigned_value = source_value.template try_to<std::size_t>(); + + return unsigned_value.has_value() + ? std::optional(integer_literal::from(unsigned_value.value())) + : std::nullopt; + } + else if constexpr (std::is_same_v<T, double> + || std::is_same_v<T, bool> + || std::is_same_v<T, unsigned char>) + { + return integer_literal::from(static_cast<std::size_t>(source_value)); + } + else if constexpr (std::is_same_v<T, std::nullptr_t>) + { + return integer_literal::from<std::ptrdiff_t>(0U); + } + else + { + return std::nullopt; + } + }, source_expression); + } + std::optional<constant_value> 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<std::ptrdiff_t> integer_literal::to_signed() const - { - if (is_negative_minimum(sizeof(std::ptrdiff_t) * CHAR_BIT)) - { - return std::numeric_limits<ptrdiff_t>::min(); - } - auto [written, rop] = export_to_words<std::ptrdiff_t>(); - - if (written > 1 || rop < 0) - { - return std::nullopt; - } - return is_negative() ? -rop : rop; - } - - std::optional<std::size_t> integer_literal::to_unsigned() const - { - auto [written, rop] = export_to_words<std::size_t>(); - - 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<elna::boot::integer_literal>::operator()(const elna::boot: { if (key.is_signed()) { - if (auto converted = key.to_signed()) + if (auto converted = key.try_to<std::ptrdiff_t>()) { return std::hash<std::ptrdiff_t>{}(*converted); } } else { - if (auto converted = key.to_unsigned()) + if (auto converted = key.try_to<std::size_t>()) { return std::hash<std::size_t>{}(*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<std::ptrdiff_t>()) { 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<std::size_t>()) { 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<std::size_t> evaluate_traits_size(const type& subject); std::optional<std::size_t> evaluate_traits_alignment(const type& subject); + static std::optional<constant_value> cast_to_int(const constant_value& source_expression); + static std::optional<constant_value> cast_to_word(const constant_value& source_expression); + public: std::optional<std::size_t> evaluate_index(expression& subject); std::optional<constant_value> 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<typename T> - inline constexpr bool is_signed = std::is_same_v<T, std::int8_t> + inline constexpr bool is_signed_v = std::is_same_v<T, std::int8_t> || std::is_same_v<T, std::int16_t> || std::is_same_v<T, std::int32_t> || std::is_same_v<T, std::int64_t>; @@ -239,10 +239,10 @@ namespace elna::boot * * \tparam T The examined type. * - * \see is_signed + * \see is_signed_v */ template<typename T> - inline constexpr bool is_unsigned = std::is_same_v<T, std::uint8_t> + inline constexpr bool is_unsigned_v = std::is_same_v<T, std::uint8_t> || std::is_same_v<T, std::uint16_t> || std::is_same_v<T, std::uint32_t> || std::is_same_v<T, std::uint64_t>; @@ -255,7 +255,7 @@ namespace elna::boot * \tparam T The examined type. */ template<typename T> - inline constexpr bool is_integral = is_signed<T> || is_unsigned<T>; + inline constexpr bool is_integral = is_signed_v<T> || is_unsigned_v<T>; struct integer_literal { @@ -281,25 +281,25 @@ namespace elna::boot std::weak_ordering operator<=>(const integer_literal& that) const; template<typename U> bool operator==(U that) const - requires(is_unsigned<U> && sizeof(U) <= sizeof(unsigned long int)) + requires(is_unsigned_v<U> && sizeof(U) <= sizeof(unsigned long int)) { return mpz_cmp_ui(this->raw, that) == 0; } template<typename U> bool operator==(U that) const - requires(is_signed<U> && sizeof(U) <= sizeof(signed long int)) + requires(is_signed_v<U> && sizeof(U) <= sizeof(signed long int)) { return mpz_cmp_si(this->raw, that) == 0; } template<typename U> std::weak_ordering operator<=>(U that) const - requires(is_unsigned<U> && sizeof(U) <= sizeof(unsigned long int)) + requires(is_unsigned_v<U> && sizeof(U) <= sizeof(unsigned long int)) { return mpz_cmp_ui(this->raw, that) <=> 0; } template<typename U> std::weak_ordering operator<=>(U that) const - requires(is_signed<U> && sizeof(U) <= sizeof(signed long int)) + requires(is_signed_v<U> && 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<std::ptrdiff_t> 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<std::size_t> to_unsigned() const; + template<typename T> + std::optional<T> try_to() const + requires is_integral<T> + { + if constexpr (is_signed_v<T>) + { + return to_signed<T>(); + } + else if constexpr (is_unsigned_v<T>) + { + return to_unsigned<T>(); + } + 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<integer_literal> 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<typename T = std::ptrdiff_t> + std::optional<T> to_signed() const + { + if (is_negative_minimum(sizeof(T) * CHAR_BIT)) + { + return std::numeric_limits<T>::min(); + } + auto [written, rop] = export_to_words<T>(); + + if (written > 1 || rop < 0) + { + return std::nullopt; + } + return is_negative() ? -rop : rop; + } + + template<typename T = std::size_t> + std::optional<T> to_unsigned() const + { + auto [written, rop] = export_to_words<T>(); + + return written > 1 ? std::nullopt : std::make_optional(rop); + } }; /** |
