aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-08-06 23:12:01 +0200
committerEugen Wissner <belka@caraus.de>2026-08-06 23:12:01 +0200
commiteaf5934f81dcd8ce5705f13b651d7b93ca8c459f (patch)
tree890d2d69a3e80d1d3a64e69cb4af69a190986c5f
parentb2dee14873402ee3d13d59ae5579593796ea862f (diff)
downloadelna-eaf5934f81dcd8ce5705f13b651d7b93ca8c459f.tar.gz
Implement compile time castings for integers
-rw-r--r--boot/evaluator.cc73
-rw-r--r--boot/result.cc26
-rw-r--r--gcc/gcc/elna-tree.cc4
-rw-r--r--include/elna/boot/evaluator.h3
-rw-r--r--include/elna/boot/result.h77
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);
+ }
};
/**