Skip to content

[libc++][math] Add constexpr for std::abs() #146633

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

arjunUpatel
Copy link

Implement constexpr for std::abs() as defined by P0533R9 (new C++23 feature). Progress tracked by #105174

@arjunUpatel arjunUpatel requested a review from a team as a code owner July 2, 2025 04:39
Copy link

github-actions bot commented Jul 2, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 2, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 2, 2025

@llvm/pr-subscribers-libcxx

Author: Arjun Patel (arjunUpatel)

Changes

Implement constexpr for std::abs() as defined by P0533R9 (new C++23 feature). Progress tracked by #105174


Full diff: https://github.com/llvm/llvm-project/pull/146633.diff

3 Files Affected:

  • (modified) libcxx/include/__math/abs.h (+6-6)
  • (modified) libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp (+6-6)
  • (modified) libcxx/test/std/numerics/c.math/abs.pass.cpp (+19-11)
diff --git a/libcxx/include/__math/abs.h b/libcxx/include/__math/abs.h
index b780159f11ebf..a1d5854ac8878 100644
--- a/libcxx/include/__math/abs.h
+++ b/libcxx/include/__math/abs.h
@@ -41,25 +41,25 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 
 // abs
 
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline float abs(float __x) _NOEXCEPT { return __builtin_fabsf(__x); }
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline double abs(double __x) _NOEXCEPT { return __builtin_fabs(__x); }
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline float abs(float __x) _NOEXCEPT { return __builtin_fabsf(__x); }
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline double abs(double __x) _NOEXCEPT { return __builtin_fabs(__x); }
 
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline long double abs(long double __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline long double abs(long double __x) _NOEXCEPT {
   return __builtin_fabsl(__x);
 }
 
 template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline int abs(int __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline int abs(int __x) _NOEXCEPT {
   return __builtin_abs(__x);
 }
 
 template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline long abs(long __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline long abs(long __x) _NOEXCEPT {
   return __builtin_labs(__x);
 }
 
 template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline long long abs(long long __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline long long abs(long long __x) _NOEXCEPT {
   return __builtin_llabs(__x);
 }
 
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 3f17f21e8c108..64164240c8564 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
@@ -40,12 +40,12 @@ int main(int, char**) {
   double DummyDouble;
   long double DummyLongDouble;
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::labs(-1L) == 1L);
   ASSERT_NOT_CONSTEXPR_CXX23(std::llabs(-1LL) == 1LL);
diff --git a/libcxx/test/std/numerics/c.math/abs.pass.cpp b/libcxx/test/std/numerics/c.math/abs.pass.cpp
index 51aee6e986836..908620b2a3290 100644
--- a/libcxx/test/std/numerics/c.math/abs.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/abs.pass.cpp
@@ -20,21 +20,29 @@ struct correct_size_int {
 };
 
 template <class Source, class Result>
-void test_abs() {
-  Source neg_val = -5;
-  Source pos_val = 5;
-  Result res     = 5;
+TEST_CONSTEXPR_CXX23 void test_abs() {
+  TEST_CONSTEXPR_CXX23 Source neg_val = -5;
+  TEST_CONSTEXPR_CXX23 Source pos_val = 5;
+  TEST_CONSTEXPR_CXX23 Result res     = 5;
 
   ASSERT_SAME_TYPE(decltype(std::abs(neg_val)), Result);
-
-  assert(std::abs(neg_val) == res);
-  assert(std::abs(pos_val) == res);
+  #if TEST_STD_VER >= 23
+    static_assert(std::abs(neg_val) == res);
+    static_assert(std::abs(pos_val) == res);
+  #else 
+    assert(std::abs(neg_val) == res);
+    assert(std::abs(pos_val) == res);
+  #endif
 }
 
-void test_big() {
-  long long int big_value          = std::numeric_limits<long long int>::max(); // a value too big for ints to store
-  long long int negative_big_value = -big_value;
-  assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
+TEST_CONSTEXPR_CXX23 void test_big() {
+  TEST_CONSTEXPR_CXX23 long long int big_value          = std::numeric_limits<long long int>::max(); // a value too big for ints to store
+  TEST_CONSTEXPR_CXX23 long long int negative_big_value = -big_value;
+  #if TEST_STD_VER >= 23
+    static_assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
+  #else
+    assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
+  #endif
 }
 
 // The following is helpful to keep in mind:

Copy link

github-actions bot commented Jul 2, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions h,cpp -- libcxx/include/__math/abs.h libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp libcxx/test/std/numerics/c.math/abs.pass.cpp
View the diff from clang-format here.
diff --git a/libcxx/include/__math/abs.h b/libcxx/include/__math/abs.h
index a1d5854ac..cf047df68 100644
--- a/libcxx/include/__math/abs.h
+++ b/libcxx/include/__math/abs.h
@@ -41,10 +41,15 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 
 // abs
 
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline float abs(float __x) _NOEXCEPT { return __builtin_fabsf(__x); }
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline double abs(double __x) _NOEXCEPT { return __builtin_fabs(__x); }
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline float abs(float __x) _NOEXCEPT {
+  return __builtin_fabsf(__x);
+}
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline double abs(double __x) _NOEXCEPT {
+  return __builtin_fabs(__x);
+}
 
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline long double abs(long double __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline long double
+abs(long double __x) _NOEXCEPT {
   return __builtin_fabsl(__x);
 }
 
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 675270264..8988491f9 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
@@ -40,37 +40,37 @@ int main(int, char**) {
   double DummyDouble;
   long double DummyLongDouble;
 
-  // TODO(LLVM 22): Remove `__has_constexpr_builtin` conditional once support for Clang 19 is dropped.
-  #if __has_constexpr_builtin(__builtin_abs)
-    ASSERT_CONSTEXPR_CXX23(std::abs(-1) == 1);
-  #else
-    ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1) == 1);
-  #endif
-  #if __has_constexpr_builtin(__builtin_labs)
-    ASSERT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
-  #else
-    ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
-  #endif
-  #if __has_constexpr_builtin(__builtin_llabs)
-    ASSERT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
-  #else
-    ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
-  #endif
-  #if __has_constexpr_builtin(__builtin_fabsf)
-    ASSERT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
-  #else
-    ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
-  #endif
-  #if __has_constexpr_builtin(__builtin_fabs)
-    ASSERT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
-  #else
-    ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
-  #endif
-  #if __has_constexpr_builtin(__builtin_fabsl)
-    ASSERT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
-  #else
-    ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
-  #endif
+// TODO(LLVM 22): Remove `__has_constexpr_builtin` conditional once support for Clang 19 is dropped.
+#if __has_constexpr_builtin(__builtin_abs)
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1) == 1);
+#else
+  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1) == 1);
+#endif
+#if __has_constexpr_builtin(__builtin_labs)
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
+#else
+  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
+#endif
+#if __has_constexpr_builtin(__builtin_llabs)
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
+#else
+  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
+#endif
+#if __has_constexpr_builtin(__builtin_fabsf)
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
+#else
+  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
+#endif
+#if __has_constexpr_builtin(__builtin_fabs)
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
+#else
+  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
+#endif
+#if __has_constexpr_builtin(__builtin_fabsl)
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
+#else
+  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
+#endif
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::labs(-1L) == 1L);
   ASSERT_NOT_CONSTEXPR_CXX23(std::llabs(-1LL) == 1LL);
diff --git a/libcxx/test/std/numerics/c.math/abs.pass.cpp b/libcxx/test/std/numerics/c.math/abs.pass.cpp
index 908620b2a..6c9456f0d 100644
--- a/libcxx/test/std/numerics/c.math/abs.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/abs.pass.cpp
@@ -26,23 +26,24 @@ TEST_CONSTEXPR_CXX23 void test_abs() {
   TEST_CONSTEXPR_CXX23 Result res     = 5;
 
   ASSERT_SAME_TYPE(decltype(std::abs(neg_val)), Result);
-  #if TEST_STD_VER >= 23
-    static_assert(std::abs(neg_val) == res);
-    static_assert(std::abs(pos_val) == res);
-  #else 
-    assert(std::abs(neg_val) == res);
-    assert(std::abs(pos_val) == res);
-  #endif
+#if TEST_STD_VER >= 23
+  static_assert(std::abs(neg_val) == res);
+  static_assert(std::abs(pos_val) == res);
+#else
+  assert(std::abs(neg_val) == res);
+  assert(std::abs(pos_val) == res);
+#endif
 }
 
 TEST_CONSTEXPR_CXX23 void test_big() {
-  TEST_CONSTEXPR_CXX23 long long int big_value          = std::numeric_limits<long long int>::max(); // a value too big for ints to store
+  TEST_CONSTEXPR_CXX23 long long int big_value =
+      std::numeric_limits<long long int>::max(); // a value too big for ints to store
   TEST_CONSTEXPR_CXX23 long long int negative_big_value = -big_value;
-  #if TEST_STD_VER >= 23
-    static_assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
-  #else
-    assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
-  #endif
+#if TEST_STD_VER >= 23
+  static_assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
+#else
+  assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
+#endif
 }
 
 // The following is helpful to keep in mind:

ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
ASSERT_CONSTEXPR_CXX23(std::abs(-1) == 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is problematic. Currently, this abs overload is from the underlying C standard library and not constexpr-since-C++23 yet. I guess we can avoid testing this overload to make this PR proceed more quickly.

@philnik777
Copy link
Contributor

philnik777 commented Jul 2, 2025

I'm really not sure whether we want to do this right now. It seems very weird to me to sometimes have constexpr support and sometimes not, depending on what you include.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants