-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc] Added support for fixed-points in is_signed
and is_unsigned
.
#133371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-libc Author: Abhinav Kumar (kr-2003) ChangesFixes #133365 Full diff: https://github.com/llvm/llvm-project/pull/133371.diff 3 Files Affected:
diff --git a/libc/src/__support/CPP/type_traits/is_signed.h b/libc/src/__support/CPP/type_traits/is_signed.h
index 3f56fb38aabb0..699b017eda183 100644
--- a/libc/src/__support/CPP/type_traits/is_signed.h
+++ b/libc/src/__support/CPP/type_traits/is_signed.h
@@ -8,20 +8,85 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_IS_SIGNED_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_IS_SIGNED_H
+#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/type_traits/bool_constant.h"
#include "src/__support/CPP/type_traits/is_arithmetic.h"
+#include "src/__support/CPP/type_traits/is_fixed_point.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
-// is_signed
+// Primary template: handles arithmetic and signed fixed-point types
template <typename T>
-struct is_signed : bool_constant<(is_arithmetic_v<T> && (T(-1) < T(0)))> {
+struct is_signed : bool_constant<((is_fixed_point_v<T> || is_arithmetic_v<T>) && (T(-1) < T(0)))> {
LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
};
+
+// Specializations for unsigned fixed-point types
+template <>
+struct is_signed<unsigned short fract> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned fract> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned long fract> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned short accum> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned accum> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned long accum> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned short sat fract> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned sat fract> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned long sat fract> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned short sat accum> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned sat accum> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+template <>
+struct is_signed<unsigned long sat accum> : bool_constant<false> {
+ LIBC_INLINE constexpr operator bool() const { return is_signed::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_signed::value; }
+};
+
template <typename T>
LIBC_INLINE_VAR constexpr bool is_signed_v = is_signed<T>::value;
diff --git a/libc/src/__support/CPP/type_traits/is_unsigned.h b/libc/src/__support/CPP/type_traits/is_unsigned.h
index eed519b1c067e..58cd3f0f598a9 100644
--- a/libc/src/__support/CPP/type_traits/is_unsigned.h
+++ b/libc/src/__support/CPP/type_traits/is_unsigned.h
@@ -8,20 +8,85 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_IS_UNSIGNED_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_IS_UNSIGNED_H
+#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/type_traits/bool_constant.h"
#include "src/__support/CPP/type_traits/is_arithmetic.h"
+#include "src/__support/CPP/type_traits/is_fixed_point.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
-// is_unsigned
+// Primary template: handles arithmetic and signed fixed-point types
template <typename T>
-struct is_unsigned : bool_constant<(is_arithmetic_v<T> && (T(-1) > T(0)))> {
+struct is_unsigned : bool_constant<((is_fixed_point_v<T> || is_arithmetic_v<T>) && (T(-1) > T(0)))> {
LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
};
+
+// Specializations for unsigned fixed-point types
+template <>
+struct is_unsigned<unsigned short fract> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned fract> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned long fract> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned short accum> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned accum> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned long accum> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned short sat fract> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned sat fract> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned long sat fract> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned short sat accum> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned sat accum> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+template <>
+struct is_unsigned<unsigned long sat accum> : bool_constant<true> {
+ LIBC_INLINE constexpr operator bool() const { return is_unsigned::value; }
+ LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
+};
+
template <typename T>
LIBC_INLINE_VAR constexpr bool is_unsigned_v = is_unsigned<T>::value;
diff --git a/libc/test/src/__support/CPP/type_traits_test.cpp b/libc/test/src/__support/CPP/type_traits_test.cpp
index 4b3e48c6a6c0f..338705efa5e80 100644
--- a/libc/test/src/__support/CPP/type_traits_test.cpp
+++ b/libc/test/src/__support/CPP/type_traits_test.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/macros/config.h"
#include "test/UnitTest/Test.h"
@@ -409,7 +410,35 @@ TEST(LlvmLibcTypeTraitsTest, is_object) {
// TODO is_scalar
-// TODO is_signed
+TEST(LlvmLibcTypeTraitsTest, is_signed) {
+ EXPECT_TRUE((is_signed_v<int>));
+ EXPECT_TRUE((is_signed_v<long>));
+ EXPECT_TRUE((is_signed_v<long long>));
+ EXPECT_FALSE((is_signed_v<unsigned int>));
+ EXPECT_FALSE((is_signed_v<unsigned long>));
+ EXPECT_FALSE((is_signed_v<unsigned long long>));
+ EXPECT_TRUE((is_signed_v<float>));
+ EXPECT_TRUE((is_signed_v<double>));
+ EXPECT_TRUE((is_signed_v<long double>));
+
+ // for fixed point types
+ EXPECT_TRUE((is_signed_v<fract>));
+ EXPECT_FALSE((is_signed_v<unsigned fract>));
+ EXPECT_TRUE((is_signed_v<accum>));
+ EXPECT_FALSE((is_signed_v<unsigned accum>));
+ EXPECT_TRUE((is_signed_v<sat fract>));
+ EXPECT_FALSE((is_signed_v<unsigned sat fract>));
+ EXPECT_TRUE((is_signed_v<sat accum>));
+ EXPECT_FALSE((is_signed_v<unsigned sat accum>));
+ EXPECT_TRUE((is_signed_v<short fract>));
+ EXPECT_FALSE((is_signed_v<unsigned short fract>));
+ EXPECT_TRUE((is_signed_v<short accum>));
+ EXPECT_FALSE((is_signed_v<unsigned short accum>));
+ EXPECT_TRUE((is_signed_v<long fract>));
+ EXPECT_FALSE((is_signed_v<unsigned long fract>));
+ EXPECT_TRUE((is_signed_v<long accum>));
+ EXPECT_FALSE((is_signed_v<unsigned long accum>));
+}
// TODO is_trivially_constructible
@@ -419,7 +448,35 @@ TEST(LlvmLibcTypeTraitsTest, is_object) {
// TODO is_union
-// TODO is_unsigned
+TEST(LlvmLibcTypeTraitsTest, is_unsigned) {
+ EXPECT_FALSE((is_unsigned_v<int>));
+ EXPECT_FALSE((is_unsigned_v<long>));
+ EXPECT_FALSE((is_unsigned_v<long long>));
+ EXPECT_TRUE((is_unsigned_v<unsigned int>));
+ EXPECT_TRUE((is_unsigned_v<unsigned long>));
+ EXPECT_TRUE((is_unsigned_v<unsigned long long>));
+ EXPECT_FALSE((is_unsigned_v<float>));
+ EXPECT_FALSE((is_unsigned_v<double>));
+ EXPECT_FALSE((is_unsigned_v<long double>));
+
+ // for fixed point types
+ EXPECT_FALSE((is_unsigned_v<fract>));
+ EXPECT_TRUE((is_unsigned_v<unsigned fract>));
+ EXPECT_FALSE((is_unsigned_v<accum>));
+ EXPECT_TRUE((is_unsigned_v<unsigned accum>));
+ EXPECT_FALSE((is_unsigned_v<sat fract>));
+ EXPECT_TRUE((is_unsigned_v<unsigned sat fract>));
+ EXPECT_FALSE((is_unsigned_v<sat accum>));
+ EXPECT_TRUE((is_unsigned_v<unsigned sat accum>));
+ EXPECT_FALSE((is_unsigned_v<short fract>));
+ EXPECT_TRUE((is_unsigned_v<unsigned short fract>));
+ EXPECT_FALSE((is_unsigned_v<short accum>));
+ EXPECT_TRUE((is_unsigned_v<unsigned short accum>));
+ EXPECT_FALSE((is_unsigned_v<long fract>));
+ EXPECT_TRUE((is_unsigned_v<unsigned long fract>));
+ EXPECT_FALSE((is_unsigned_v<long accum>));
+ EXPECT_TRUE((is_unsigned_v<unsigned long accum>));
+}
// TODO is_void
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
is_signed
and is_unsigned
.is_signed
and is_unsigned
.
is_signed
and is_unsigned
.is_signed
and is_unsigned
.
is_signed
and is_unsigned
.is_signed
and is_unsigned
.
template <typename T> | ||
struct is_signed : bool_constant<(is_arithmetic_v<T> && (T(-1) < T(0)))> { | ||
struct is_signed : bool_constant<((is_fixed_point_v<T> || is_arithmetic_v<T>) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably you could change it to:
(is_fixed_point_v<T> && T(-0.5) < T(0)) || (is_arithmetic_v<T> && T(-1) < T(0))
and remove all the fixed point specializations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it won't work. even if the value is just less than 0, it will try to wrap around in unsigned type(and its not possible in fixed-points). I have tried it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PiJoules is there problem with constexpr for fixed point type in clang?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the failure mode? Is it a compilation error or failure in one of the tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the failure mode? Is it a compilation error or failure in one of the tests?
i have explained here #133680
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replied to the issue.
tl;dr overflow for non-saturating types isn't defined, so unsigned _Fract(-1)
can't be constant evaluated. If you want to check if a type is signed, I think you can do FXRep<T>::MIN() < FXRep::ZERO()
. I also need to update the diagnostic since it's misleading.
template <typename T> | ||
struct is_signed : bool_constant<(is_arithmetic_v<T> && (T(-1) < T(0)))> { | ||
LIBC_INLINE constexpr operator bool() const { return is_signed::value; } | ||
LIBC_INLINE constexpr bool operator()() const { return is_signed::value; } | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I just tested locally, and the following seems to work without all the specializations:
struct is_signed : bool_constant<
(is_fixed_point_v<T> && T(-0.5) < T(0)) ||
(is_arithmetic_v<T> && (T(-1) < T(0)))> {
...
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
really? i think i am missing out on something, because this is what i get when i try this on my machine:
/Users/llvm-project/libc/src/__support/CPP/type_traits/is_signed.h:23:34: error: non-type template argument is not a constant expression
23 | struct is_signed : bool_constant<(is_fixed_point_v<T> && T(-0.5) < T(0)) ||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 | (is_arithmetic_v<T> && (T(-1) < T(0)))> {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/llvm-project/libc/src/__support/CPP/type_traits/is_signed.h:105:46: note: in instantiation of template class '__llvm_libc_21_0_0_git::cpp::is_signed<unsigned _Fract>' requested here
105 | LIBC_INLINE_VAR constexpr bool is_signed_v = is_signed<T>::value;
| ^
/Users/llvm-project/libc/test/src/__support/CPP/type_traits_test.cpp:427:17: note: in instantiation of variable template specialization '__llvm_libc_21_0_0_git::cpp::is_signed_v<unsigned _Fract>' requested here
427 | EXPECT_FALSE((is_signed_v<unsigned fract>));
/Users/llvm-project/libc/src/__support/CPP/type_traits/is_signed.h:23:60: note: value 0.0 is outside the range of representable values of type 'unsigned _Fract'
23 | struct is_signed : bool_constant<(is_fixed_point_v<T> && T(-0.5) < T(0)) ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it looks like static_assert
with that works fine, but not with EXPECT_EQ
in the tests that you added. Probably something is missing for fixed point support in constexpr context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it looks like
static_assert
with that works fine, but not withEXPECT_EQ
in the tests that you added. Probably something is missing for fixed point support in constexpr context.
opened issue at #133680
I think we'll want the saturated version of each of these also |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Let's wait for @PiJoules comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM assuming you're not blocked on that compilation issue anymore
Fixes #133365
Changes Done
in
/libc/src/__support/CPP/type_traits/is_signed.h
. Added check for fixed-points.unsigned _Fract
or any unsigned fixed-point becauseunsigned _Fract
can’t represent -1 in T(-1), whileunsigned int
can handle it via wrapping.is_signed
check forunsigned
fixed-points./libc/src/__support/CPP/type_traits/is_unsigned.h
.is_signed
andis_unsigned
.