[libcxx-commits] [libcxx] [libc++][math] Add `constexpr` for `std::signbit()` (PR #105946)

Robin Caloudis via libcxx-commits libcxx-commits at lists.llvm.org
Sat Aug 31 01:46:48 PDT 2024


https://github.com/robincaloudis updated https://github.com/llvm/llvm-project/pull/105946

>From f54f52c472334eb7e9c555bf429bb9f75325a80b Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 18:00:25 +0200
Subject: [PATCH 01/10] Test constexpr for signbit()

---
 .../c.math/constexpr-cxx23-clang.pass.cpp     |   6 +-
 .../c.math/constexpr-cxx23-gcc.pass.cpp       |   6 +-
 .../test/std/numerics/c.math/signbit.pass.cpp | 131 ++++++++++++++++++
 libcxx/test/support/type_algorithms.h         |  20 ++-
 4 files changed, 150 insertions(+), 13 deletions(-)
 create mode 100644 libcxx/test/std/numerics/c.math/signbit.pass.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index a07260a34516f1..20887b8cf2678b 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -220,9 +220,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 8c481f41a945ed..d8779706bcee22 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -217,9 +217,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
diff --git a/libcxx/test/std/numerics/c.math/signbit.pass.cpp b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
new file mode 100644
index 00000000000000..cdeab92e0836e2
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// bool signbit(floating-point-type x); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::signbit(T(0)));
+    assert(!std::signbit(std::numeric_limits<T>::min()));
+    assert(!std::signbit(std::numeric_limits<T>::denorm_min()));
+    assert(!std::signbit(std::numeric_limits<T>::max()));
+    assert(!std::signbit(std::numeric_limits<T>::infinity()));
+    assert(!std::signbit(std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::signbit(std::numeric_limits<T>::signaling_NaN()));
+    assert(std::signbit(-T(0)));
+    assert(std::signbit(-std::numeric_limits<T>::infinity()));
+    assert(std::signbit(std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestUnsignedIntAndFixedWidthChar {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::signbit(std::numeric_limits<T>::max()));
+    assert(!std::signbit(T(0)));
+    assert(!std::signbit(std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestSignedInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::signbit(std::numeric_limits<T>::max()));
+    assert(!std::signbit(T(0)));
+    assert(std::signbit(std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestVariableWidthChar {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::signbit(std::numeric_limits<T>::max()));
+    assert(!std::signbit(T(0)));
+    // Signed or unsigned depending on the architecture and platform.
+    if (std::is_unsigned<T>::value) {
+      assert(!std::signbit(std::numeric_limits<T>::lowest()));
+    } else {
+      assert(std::signbit(std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::concatenate_t<types::unsigned_integer_types, types::fixed_width_character_types>(),
+                  TestUnsignedIntAndFixedWidthChar());
+  types::for_each(types::signed_integer_types(), TestSignedInt());
+  types::for_each(types::variable_width_character_types(), TestVariableWidthChar());
+
+  // Make sure we can call `std::signbit` with convertible types. This checks
+  // whether overloads for all cv-unqualified floating-point types are working
+  // as expected.
+  {
+    assert(!std::signbit(ConvertibleTo<float>()));
+    assert(!std::signbit(ConvertibleTo<double>()));
+    assert(!std::signbit(ConvertibleTo<long double>()));
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/type_algorithms.h b/libcxx/test/support/type_algorithms.h
index da3d0add4d0c45..6b3b9aba11e5ec 100644
--- a/libcxx/test/support/type_algorithms.h
+++ b/libcxx/test/support/type_algorithms.h
@@ -82,22 +82,28 @@ struct partial_instantiation {
 
 // type categories defined in [basic.fundamental] plus extensions (without CV-qualifiers)
 
-using character_types =
+using variable_width_character_types =
     type_list<char
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
               ,
               wchar_t
 #endif
+              >;
+
+using fixed_width_character_types = type_list<
 #ifndef TEST_HAS_NO_CHAR8_T
-              ,
-              char8_t
+    char8_t
 #endif
 #if TEST_STD_VER >= 11
-              ,
-              char16_t,
-              char32_t
+#  ifndef TEST_HAS_NO_CHAR8_T
+    ,
+#  endif
+    char16_t,
+    char32_t
 #endif
-              >;
+    >;
+
+using character_types = concatenate_t<variable_width_character_types, fixed_width_character_types>;
 
 using signed_integer_types =
     type_list<signed char,

>From a1d92c4ee2c2aac7a11fe7820882b1736f3bbbad Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 18:00:56 +0200
Subject: [PATCH 02/10] Add constexpr for signbit()

---
 libcxx/include/__math/traits.h | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0638a6949580eb..9be06b7b8efae9 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -28,17 +28,29 @@ namespace __math {
 // signbit
 
 template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+  return __builtin_signbit(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(float __x) _NOEXCEPT {
+  return __builtin_signbit(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(double __x) _NOEXCEPT {
+  return __builtin_signbit(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(long double __x) _NOEXCEPT {
   return __builtin_signbit(__x);
 }
 
 template <class _A1, __enable_if_t<is_integral<_A1>::value && is_signed<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
   return __x < 0;
 }
 
 template <class _A1, __enable_if_t<is_integral<_A1>::value && !is_signed<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
   return false;
 }
 

>From a9e2291a0ae7e62e2adc0df9ed535ba0a4d73ca4 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 18:01:43 +0200
Subject: [PATCH 03/10] Update cxx23 status

---
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 2c8a91d8401b53..20bd7b70a0d619 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -39,7 +39,7 @@
 "`P2401R0 <https://wg21.link/P2401R0>`__","Add a conditional ``noexcept`` specification to ``std::exchange``","2021-10 (Virtual)","|Complete|","14.0",""
 "","","","","",""
 "`P0323R12 <https://wg21.link/P0323R12>`__","``std::expected``","2022-02 (Virtual)","|Complete|","16.0",""
-"`P0533R9 <https://wg21.link/P0533R9>`__","``constexpr`` for ``<cmath>`` and ``<cstdlib>``","2022-02 (Virtual)","|In Progress|","","``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented"
+"`P0533R9 <https://wg21.link/P0533R9>`__","``constexpr`` for ``<cmath>`` and ``<cstdlib>``","2022-02 (Virtual)","|In Progress|","","``isfinite``, ``isinf``, ``isnan``, ``isnormal`` and ``signbit`` are implemented"
 "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15.0",""
 "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17.0",""
 "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations."

>From 1369b39a73391af76cea178b44b0203a44cde4be Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 20:51:54 +0200
Subject: [PATCH 04/10] Use signcopy workaround

If constexpr signbit is not available, we make
use of signcopy(), which has been constexpr for
quite a while in Clang.
---
 libcxx/include/__math/traits.h | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 9be06b7b8efae9..077f0aea5b7a91 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -29,19 +29,39 @@ namespace __math {
 
 template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
 _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+  return __builtin_copysign(1.0, __x) == -1.0;
+#else
   return __builtin_signbit(__x);
+#endif
 }
 
 _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(float __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+  return __builtin_copysign(1.0, __x) == -1.0;
+#else
   return __builtin_signbit(__x);
+#endif
 }
 
 _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(double __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+  return __builtin_copysign(1.0, __x) == -1.0;
+#else
   return __builtin_signbit(__x);
+#endif
 }
 
 _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(long double __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+  return __builtin_copysign(1.0, __x) == -1.0;
+#else
   return __builtin_signbit(__x);
+#endif
 }
 
 template <class _A1, __enable_if_t<is_integral<_A1>::value && is_signed<_A1>::value, int> = 0>

>From 23559f0532e13f4736a289859827359f3a5614a3 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sun, 25 Aug 2024 00:39:30 +0200
Subject: [PATCH 05/10] Prefer libcxx overload

By using `_LIBCPP_PREFERRED_OVERLOAD` we make
sure that a given overload is a better match
than an otherwise equally good function
declaration.

Why is there an equally good function
declaration in the first place? Underlying the
Windows SDK is the UCRT, the universal C runtime,
which clang-cl makes use of. The UCRT should
provide only C library headers, but does on top
comes with overloads for all cv-unqualified floating
point types (float, double, long double) for
`std::signbit()` in https://github.com/microsoft/win32metadata/blob/e012b29924c53aa941fc010850b68331b0c3ea80/generation/WinSDK/RecompiledIdlHeaders/ucrt/corecrt_math.h#L309-L322.
In a certain way, this can be seen as a deviation
from the C standard. We need to work around it.
---
 libcxx/include/__math/traits.h | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 077f0aea5b7a91..e668c5805cea39 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -37,7 +37,12 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo
 #endif
 }
 
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(float __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    bool
+    signbit(float __x) _NOEXCEPT {
 // TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
 #if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
   return __builtin_copysign(1.0, __x) == -1.0;
@@ -46,7 +51,12 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo
 #endif
 }
 
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(double __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    bool
+    signbit(double __x) _NOEXCEPT {
 // TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
 #if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
   return __builtin_copysign(1.0, __x) == -1.0;
@@ -55,7 +65,12 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo
 #endif
 }
 
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(long double __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    bool
+    signbit(long double __x) _NOEXCEPT {
 // TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
 #if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
   return __builtin_copysign(1.0, __x) == -1.0;

>From f160b6a5fcecee515cb154aacfdfe22b68dcc6ae Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Thu, 29 Aug 2024 08:55:59 +0200
Subject: [PATCH 06/10] Reduce complexity of tests

---
 .../test/std/numerics/c.math/signbit.pass.cpp | 46 +------------------
 libcxx/test/support/type_algorithms.h         | 20 +++-----
 2 files changed, 9 insertions(+), 57 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/signbit.pass.cpp b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
index cdeab92e0836e2..08e785b1d3c61c 100644
--- a/libcxx/test/std/numerics/c.math/signbit.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
@@ -44,50 +44,11 @@ struct TestFloat {
   }
 };
 
-struct TestUnsignedIntAndFixedWidthChar {
+struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(!std::signbit(std::numeric_limits<T>::max()));
     assert(!std::signbit(T(0)));
-    assert(!std::signbit(std::numeric_limits<T>::lowest()));
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
-  }
-};
-
-struct TestSignedInt {
-  template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(!std::signbit(std::numeric_limits<T>::max()));
-    assert(!std::signbit(T(0)));
-    assert(std::signbit(std::numeric_limits<T>::lowest()));
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
-  }
-};
-
-struct TestVariableWidthChar {
-  template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(!std::signbit(std::numeric_limits<T>::max()));
-    assert(!std::signbit(T(0)));
-    // Signed or unsigned depending on the architecture and platform.
     if (std::is_unsigned<T>::value) {
       assert(!std::signbit(std::numeric_limits<T>::lowest()));
     } else {
@@ -113,10 +74,7 @@ struct ConvertibleTo {
 
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
-  types::for_each(types::concatenate_t<types::unsigned_integer_types, types::fixed_width_character_types>(),
-                  TestUnsignedIntAndFixedWidthChar());
-  types::for_each(types::signed_integer_types(), TestSignedInt());
-  types::for_each(types::variable_width_character_types(), TestVariableWidthChar());
+  types::for_each(types::integral_types(), TestInt());
 
   // Make sure we can call `std::signbit` with convertible types. This checks
   // whether overloads for all cv-unqualified floating-point types are working
diff --git a/libcxx/test/support/type_algorithms.h b/libcxx/test/support/type_algorithms.h
index 6b3b9aba11e5ec..da3d0add4d0c45 100644
--- a/libcxx/test/support/type_algorithms.h
+++ b/libcxx/test/support/type_algorithms.h
@@ -82,28 +82,22 @@ struct partial_instantiation {
 
 // type categories defined in [basic.fundamental] plus extensions (without CV-qualifiers)
 
-using variable_width_character_types =
+using character_types =
     type_list<char
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
               ,
               wchar_t
 #endif
-              >;
-
-using fixed_width_character_types = type_list<
 #ifndef TEST_HAS_NO_CHAR8_T
-    char8_t
+              ,
+              char8_t
 #endif
 #if TEST_STD_VER >= 11
-#  ifndef TEST_HAS_NO_CHAR8_T
-    ,
-#  endif
-    char16_t,
-    char32_t
+              ,
+              char16_t,
+              char32_t
 #endif
-    >;
-
-using character_types = concatenate_t<variable_width_character_types, fixed_width_character_types>;
+              >;
 
 using signed_integer_types =
     type_list<signed char,

>From 8478001e74a9f6ef6d5072159415e30b809942bd Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Thu, 29 Aug 2024 16:42:57 +0200
Subject: [PATCH 07/10] Apply constexpr only when builtin is

---
 libcxx/include/__math/traits.h                      | 13 +++++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp  |  7 +++++++
 libcxx/test/std/numerics/c.math/signbit.pass.cpp    |  3 +++
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index e668c5805cea39..458bde4b9b2406 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -28,13 +28,14 @@ namespace __math {
 // signbit
 
 template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
-// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
-#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
-  return __builtin_copysign(1.0, __x) == -1.0;
-#else
-  return __builtin_signbit(__x);
+_LIBCPP_NODISCARD inline
+// TODO(LLVM 22): Remove conditional once support for Clang 19 is dropped.
+#if !defined(__clang__) || __has_constexpr_builtin(__builtin_signbit)
+    _LIBCPP_CONSTEXPR_SINCE_CXX23
 #endif
+    _LIBCPP_HIDE_FROM_ABI bool
+    signbit(_A1 __x) _NOEXCEPT {
+  return __builtin_signbit(__x);
 }
 
 _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 20887b8cf2678b..3f17f21e8c1087 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -220,9 +220,16 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);
 
+// TODO(LLVM 22): Remove `__has_constexpr_builtin` conditional once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit)
+  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
+  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
+  ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
+#else
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
+#endif
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
diff --git a/libcxx/test/std/numerics/c.math/signbit.pass.cpp b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
index 08e785b1d3c61c..64a65f6c9cd656 100644
--- a/libcxx/test/std/numerics/c.math/signbit.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
@@ -11,6 +11,9 @@
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
 
+// These compilers don't support constexpr `__builtin_signbit` yet.
+// UNSUPPORTED: clang-17, clang-18, clang-19, apple-clang-16, apple-clang-17
+
 #include <cassert>
 #include <cmath>
 #include <limits>

>From f3b6397e2606139045864c8b81ff862161dd86c3 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Thu, 29 Aug 2024 16:43:41 +0200
Subject: [PATCH 08/10] Remove overloads

We will add the overloads in a seperate
patch.
---
 libcxx/include/__math/traits.h                | 44 +------------------
 .../std/numerics/c.math/isnormal.pass.cpp     |  2 +-
 .../test/std/numerics/c.math/signbit.pass.cpp | 14 ------
 3 files changed, 2 insertions(+), 58 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 458bde4b9b2406..9841e7bbc1f8a1 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -30,7 +30,7 @@ namespace __math {
 template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
 _LIBCPP_NODISCARD inline
 // TODO(LLVM 22): Remove conditional once support for Clang 19 is dropped.
-#if !defined(__clang__) || __has_constexpr_builtin(__builtin_signbit)
+#if defined(_LIBCPP_COMPILER_GCC) || __has_constexpr_builtin(__builtin_signbit)
     _LIBCPP_CONSTEXPR_SINCE_CXX23
 #endif
     _LIBCPP_HIDE_FROM_ABI bool
@@ -38,48 +38,6 @@ _LIBCPP_NODISCARD inline
   return __builtin_signbit(__x);
 }
 
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
-#ifdef _LIBCPP_PREFERRED_OVERLOAD
-_LIBCPP_PREFERRED_OVERLOAD
-#endif
-    bool
-    signbit(float __x) _NOEXCEPT {
-// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
-#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
-  return __builtin_copysign(1.0, __x) == -1.0;
-#else
-  return __builtin_signbit(__x);
-#endif
-}
-
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
-#ifdef _LIBCPP_PREFERRED_OVERLOAD
-_LIBCPP_PREFERRED_OVERLOAD
-#endif
-    bool
-    signbit(double __x) _NOEXCEPT {
-// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
-#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
-  return __builtin_copysign(1.0, __x) == -1.0;
-#else
-  return __builtin_signbit(__x);
-#endif
-}
-
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
-#ifdef _LIBCPP_PREFERRED_OVERLOAD
-_LIBCPP_PREFERRED_OVERLOAD
-#endif
-    bool
-    signbit(long double __x) _NOEXCEPT {
-// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
-#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
-  return __builtin_copysign(1.0, __x) == -1.0;
-#else
-  return __builtin_signbit(__x);
-#endif
-}
-
 template <class _A1, __enable_if_t<is_integral<_A1>::value && is_signed<_A1>::value, int> = 0>
 _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
   return __x < 0;
diff --git a/libcxx/test/std/numerics/c.math/isnormal.pass.cpp b/libcxx/test/std/numerics/c.math/isnormal.pass.cpp
index 76c3d13520d996..d2586147345427 100644
--- a/libcxx/test/std/numerics/c.math/isnormal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isnormal.pass.cpp
@@ -64,7 +64,7 @@ struct TestInt {
 
 template <typename T>
 struct ConvertibleTo {
-  operator T() const { return T(1); }
+  operator T() const { return T(); }
 };
 
 int main(int, char**) {
diff --git a/libcxx/test/std/numerics/c.math/signbit.pass.cpp b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
index 64a65f6c9cd656..7111d89fb2aeb3 100644
--- a/libcxx/test/std/numerics/c.math/signbit.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
@@ -70,23 +70,9 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
-  // Make sure we can call `std::signbit` with convertible types. This checks
-  // whether overloads for all cv-unqualified floating-point types are working
-  // as expected.
-  {
-    assert(!std::signbit(ConvertibleTo<float>()));
-    assert(!std::signbit(ConvertibleTo<double>()));
-    assert(!std::signbit(ConvertibleTo<long double>()));
-  }
-
   return 0;
 }

>From e2d8d5be90e8a28fe6fda62e64c8e50819ccfd3b Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Fri, 30 Aug 2024 18:55:33 +0200
Subject: [PATCH 09/10] Delete update in CSV status file

Custom notes are encoded via GitHub
issues from now on.
---
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 20bd7b70a0d619..2c8a91d8401b53 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -39,7 +39,7 @@
 "`P2401R0 <https://wg21.link/P2401R0>`__","Add a conditional ``noexcept`` specification to ``std::exchange``","2021-10 (Virtual)","|Complete|","14.0",""
 "","","","","",""
 "`P0323R12 <https://wg21.link/P0323R12>`__","``std::expected``","2022-02 (Virtual)","|Complete|","16.0",""
-"`P0533R9 <https://wg21.link/P0533R9>`__","``constexpr`` for ``<cmath>`` and ``<cstdlib>``","2022-02 (Virtual)","|In Progress|","","``isfinite``, ``isinf``, ``isnan``, ``isnormal`` and ``signbit`` are implemented"
+"`P0533R9 <https://wg21.link/P0533R9>`__","``constexpr`` for ``<cmath>`` and ``<cstdlib>``","2022-02 (Virtual)","|In Progress|","","``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented"
 "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15.0",""
 "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17.0",""
 "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations."

>From b08760200663d82f54fe460fcb8e08e32524c353 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 31 Aug 2024 10:31:58 +0200
Subject: [PATCH 10/10] Extract signbit constexprness

---
 libcxx/include/__math/traits.h | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 9841e7bbc1f8a1..3d4f14fc9cd552 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -27,24 +27,25 @@ namespace __math {
 
 // signbit
 
-template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline
 // TODO(LLVM 22): Remove conditional once support for Clang 19 is dropped.
 #if defined(_LIBCPP_COMPILER_GCC) || __has_constexpr_builtin(__builtin_signbit)
-    _LIBCPP_CONSTEXPR_SINCE_CXX23
+#  define _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_CONSTEXPR_SINCE_CXX23
+#else
+#  define _LIBCPP_SIGNBIT_CONSTEXPR
 #endif
-    _LIBCPP_HIDE_FROM_ABI bool
-    signbit(_A1 __x) _NOEXCEPT {
+
+template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
+_LIBCPP_NODISCARD inline _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
   return __builtin_signbit(__x);
 }
 
 template <class _A1, __enable_if_t<is_integral<_A1>::value && is_signed<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
   return __x < 0;
 }
 
 template <class _A1, __enable_if_t<is_integral<_A1>::value && !is_signed<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
   return false;
 }
 



More information about the libcxx-commits mailing list