Skip to content
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

[libc++][numeric] P0543R3: Saturation arithmetic #77967

Merged
merged 71 commits into from
Jan 22, 2024

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented Jan 12, 2024

Copy link

github-actions bot commented Jan 12, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P0543R3-Saturation-arithmetic branch from cecbc2e to db3e72e Compare January 12, 2024 21:28
@H-G-Hristov H-G-Hristov marked this pull request as ready for review January 12, 2024 22:45
@H-G-Hristov H-G-Hristov requested a review from a team as a code owner January 12, 2024 22:45
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jan 12, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 12, 2024

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Implements: https://wg21.link/P0543R3

Additional references:


Patch is 39.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/77967.diff

23 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/ReleaseNotes/18.rst (+4-3)
  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (added) libcxx/include/__numeric/saturation_arithmetic.h (+119)
  • (modified) libcxx/include/module.modulemap.in (+1)
  • (modified) libcxx/include/numeric (+13)
  • (modified) libcxx/include/version (+1-1)
  • (modified) libcxx/modules/std/numeric.inc (+10)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp (+5-11)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+5-11)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp (+98)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp (+39)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp (+53)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp (+98)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp (+39)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp (+105)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp (+39)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp (+109)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp (+44)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp (+93)
  • (added) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp (+39)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+1-4)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 893a3b13ca06e0..9dd9c0c023bc8a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -432,7 +432,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_rcu``                                   *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_saturation_arithmetic``                 *unimplemented*
+    ``__cpp_lib_saturation_arithmetic``                 ``202311L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_smart_ptr_owner_equality``              *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 6de7d07e454d34..877e387d9280fd 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -54,12 +54,13 @@ Implemented Papers
 - P2905R2 - Runtime format strings
 - P2918R2 - Runtime format strings II
 - P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26
-- P2870R3 - Remove basic_string::reserve()
+- P2870R3 - Remove ``basic_string::reserve()``
 - P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?)
-- P2821R5 - span.at()
-- P0521R0 - Proposed Resolution for CA 14 (shared_ptr use_count/unique)
+- P2821R5 - ``span.at()``
+- P0521R0 - Proposed Resolution for CA 14 (``shared_ptr`` ``use_count/unique``)
 - P1759R6 - Native handles and file streams
 - P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
+- P0543R3 - Saturation arithmetic
 
 
 Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 5701717f39766c..b38b3028863fee 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -27,7 +27,7 @@
 "`P2714R1 <https://wg21.link/P2714R1>`__","LWG","Bind front and back to NTTP callables","Varna June 2023","","",""
 "`P2630R4 <https://wg21.link/P2630R4>`__","LWG","``submdspan``","Varna June 2023","","",""
 "","","","","","",""
-"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","","",""
+"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","|Complete|","18.0",""
 "`P2407R5 <https://wg21.link/P2407R5>`__","LWG","Freestanding Library: Partial Classes","Kona November 2023","","",""
 "`P2546R5 <https://wg21.link/P2546R5>`__","LWG","Debugging Support","Kona November 2023","","",""
 "`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","|Complete|","18.0","|format| |DR|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0fe3ab44d2466e..c4d8a9f092de14 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -569,6 +569,7 @@ set(files
   __numeric/pstl_reduce.h
   __numeric/pstl_transform_reduce.h
   __numeric/reduce.h
+  __numeric/saturation_arithmetic.h
   __numeric/transform_exclusive_scan.h
   __numeric/transform_inclusive_scan.h
   __numeric/transform_reduce.h
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
new file mode 100644
index 00000000000000..6d13ec5b3fd5bc
--- /dev/null
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -0,0 +1,119 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
+#define _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
+
+#include <__concepts/arithmetic.h>
+#include <__config>
+#include <__type_traits/decay.h>
+#include <__utility/cmp.h>
+#include <limits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <typename _Tp>
+concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
+
+template <__libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+  if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
+    return __sum;
+  // Handle overflow
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    return std::numeric_limits<_Tp>::max();
+  } else {
+    // Signed addition overflow
+    if (__x > 0)
+      // Overflows if (x > 0 && y > 0)
+      return std::numeric_limits<_Tp>::max();
+    else
+      // Overflows if  (x < 0 && y < 0)
+      return std::numeric_limits<_Tp>::min();
+  }
+}
+
+template <__libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+  if (_Tp __sub; !__builtin_sub_overflow(__x, __y, &__sub))
+    return __sub;
+  // Handle overflow
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    // Overflows if (x < y)
+    return std::numeric_limits<_Tp>::min();
+  } else {
+    // Signed subtration overflow
+    if (__x > 0)
+      // Overflows if (x > 0 && y < 0)
+      return std::numeric_limits<_Tp>::max();
+    else
+      // Overflows if (x < 0 && y > 0)
+      return std::numeric_limits<_Tp>::min();
+  }
+}
+
+template <__libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+  if (_Tp __mul; !__builtin_mul_overflow(__x, __y, &__mul))
+    return __mul;
+  // Handle overflow
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    return std::numeric_limits<_Tp>::max();
+  } else {
+    // Signed multiplication overflow
+    if (__x > 0) {
+      if (__y > 0)
+        // Overflows if (x > 0 && y > 0)
+        return std::numeric_limits<_Tp>::max();
+      // Overflows if (x > 0 && y < 0)
+      return std::numeric_limits<_Tp>::min();
+    }
+    if (__y > 0)
+      // Overflows if (x < 0 && y > 0)
+      return std::numeric_limits<_Tp>::min();
+    // Overflows if (x < 0 && y < 0)
+    return std::numeric_limits<_Tp>::max();
+  }
+}
+
+template <__libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+  _LIBCPP_ASSERT_UNCATEGORIZED(__y != 0, "Division by 0 is undefined");
+  if constexpr (__libcpp_unsigned_integer<_Tp>) {
+    return __x / __y;
+  } else {
+    // Handle signed division overflow
+    if (__x == std::numeric_limits<_Tp>::min() && __y == _Tp{-1})
+      return std::numeric_limits<_Tp>::max();
+    return __x / __y;
+  }
+}
+
+template <__libcpp_standard_integer _Rp, __libcpp_standard_integer _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+  // Handle overflow
+  if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
+    return std::numeric_limits<_Rp>::min();
+  if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
+    return std::numeric_limits<_Rp>::max();
+  // No overflow
+  return static_cast<_Rp>(__x);
+}
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d10670d4faaffc..194a74a1e07b14 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1580,6 +1580,7 @@ module std_private_numeric_pstl_transform_reduce    [system] {
   export *
 }
 module std_private_numeric_reduce                   [system] { header "__numeric/reduce.h" }
+module std_private_numeric_saturation_arithmetic    [system] { header "__numeric/saturation_arithmetic.h" }
 module std_private_numeric_transform_exclusive_scan [system] { header "__numeric/transform_exclusive_scan.h" }
 module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
 module std_private_numeric_transform_reduce         [system] { header "__numeric/transform_reduce.h" }
diff --git a/libcxx/include/numeric b/libcxx/include/numeric
index d09d0a81fcc03a..0fe7115f1c666e 100644
--- a/libcxx/include/numeric
+++ b/libcxx/include/numeric
@@ -140,6 +140,18 @@ template<class T>
 template<class T>
     constexpr T* midpoint(T* a, T* b);        // C++20
 
+// [numeric.sat], saturation arithmetic
+template<class T>
+constexpr T add_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T>
+constexpr T sub_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T>
+constexpr T mul_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T>
+constexpr T div_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+template<class T, class U>
+constexpr T saturate_cast(U x) noexcept;                    // freestanding, Since C++26
+
 }  // std
 
 */
@@ -160,6 +172,7 @@ template<class T>
 #include <__numeric/pstl_reduce.h>
 #include <__numeric/pstl_transform_reduce.h>
 #include <__numeric/reduce.h>
+#include <__numeric/saturation_arithmetic.h>
 #include <__numeric/transform_exclusive_scan.h>
 #include <__numeric/transform_inclusive_scan.h>
 #include <__numeric/transform_reduce.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index c96647894dce63..b8d0c4e6f3c898 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -504,7 +504,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_out_ptr                              202311L
 # define __cpp_lib_ratio                                202306L
 // # define __cpp_lib_rcu                                  202306L
-// # define __cpp_lib_saturation_arithmetic                202311L
+# define __cpp_lib_saturation_arithmetic                202311L
 // # define __cpp_lib_smart_ptr_owner_equality             202306L
 # define __cpp_lib_span_at                              202311L
 // # define __cpp_lib_span_initializer_list                202311L
diff --git a/libcxx/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc
index d2b7688d4e5f10..3bc7b231681584 100644
--- a/libcxx/modules/std/numeric.inc
+++ b/libcxx/modules/std/numeric.inc
@@ -54,4 +54,14 @@ export namespace std {
 
   // [numeric.ops.midpoint], midpoint
   using std::midpoint;
+
+#if _LIBCPP_STD_VER >= 26
+  // [numeric.sat], saturation arithmetic
+  using std::add_sat;
+  using std::div_sat;
+  using std::mul_sat;
+  using std::saturate_cast;
+  using std::sub_sat;
+#endif
+
 } // namespace std
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
index b510eefc69a5d3..d132b7c7b9c4f5 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
@@ -263,17 +263,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should be defined in c++26"
-#   endif
-#   if __cpp_lib_saturation_arithmetic != 202311L
-#     error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_saturation_arithmetic
+#   error "__cpp_lib_saturation_arithmetic should be defined in c++26"
+# endif
+# if __cpp_lib_saturation_arithmetic != 202311L
+#   error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
 # endif
 
 #endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index d5a0839b30f824..edb36da28bbe15 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7167,17 +7167,11 @@
 #   error "__cpp_lib_sample should have the value 201603L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should be defined in c++26"
-#   endif
-#   if __cpp_lib_saturation_arithmetic != 202311L
-#     error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_saturation_arithmetic
-#     error "__cpp_lib_saturation_arithmetic should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_saturation_arithmetic
+#   error "__cpp_lib_saturation_arithmetic should be defined in c++26"
+# endif
+# if __cpp_lib_saturation_arithmetic != 202311L
+#   error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
 # endif
 
 # ifndef __cpp_lib_scoped_lock
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
new file mode 100644
index 00000000000000..ccd3594fd3a652
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T add_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+template <typename IntegerT>
+constexpr bool test_signed() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::div_sat(minVal, maxVal)));
+
+  // No saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{3}, IntegerT{4});
+    assert(sum == IntegerT{7});
+  }
+
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-3}, IntegerT{4});
+    assert(sum == IntegerT{1});
+  }
+
+  // Saturation - max - both arguments positive
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+    assert(sum == maxVal);
+  }
+
+  // Saturation - min - both arguments negative
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{-4});
+    assert(sum == minVal);
+  }
+
+  return true;
+}
+
+template <typename IntegerT>
+constexpr bool test_unsigned() {
+  constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+  constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+  static_assert(noexcept(std::div_sat(minVal, maxVal)));
+
+  // No Saturation
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{3}, IntegerT{4});
+    assert(sum == IntegerT{7});
+  }
+
+  // Saturation - max only
+  {
+    std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+    assert(sum == maxVal);
+  }
+
+  return true;
+}
+
+constexpr bool test() {
+  // Signed
+  test_signed<signed char>();
+  test_signed<short int>();
+  test_signed<int>();
+  test_signed<long int>();
+  test_signed<long long int>();
+  // Unsigned
+  test_unsigned<unsigned char>();
+  test_unsigned<unsigned short int>();
+  test_unsigned<unsigned int>();
+  test_unsigned<unsigned long int>();
+  test_unsigned<unsigned long long int>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
new file mode 100644
index 00000000000000..b05b30abea6fdb
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <numeric>
+
+// template<class T>
+// constexpr T add_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cstdint>
+#include <numeric>
+
+#include "test_macros.h"
+
+template <typename IntegerT>
+constexpr void test_constraint() {
+  // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
+  // expected-error@*:* 0-3 {{no matching function for call to 'add_sat'}}
+  // expected-error@*:* 0-2 {{expected unqualified-id}}
+  [[maybe_unused]] auto sum = std::add_sat(IntegerT{3}, IntegerT{4});
+}
+
+constexpr bool test() {
+  test_constraint<bool>();
+  test_constraint<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_constraint<wchar_t>();
+#endif
+  test_constraint<std::char16_t>();
+  test_constraint<std::char32_t>();
+
+  return true;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
new file mode 100644
index 00000000000000..98616d2c1c427c
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: availability-verbose_abort-missing
+
+// <numeric>
+
+// template<class T>
+// constexpr T div_sat(T x, T y) noexcept;                     // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+#include "check_assertion.h"
+
+template <typename IntegerT>
+constexpr void test() {
+  TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{3}, IntegerT{0}), "Division by 0 is undefined");
+}
+
+constexpr bool test() {
+  // signed
+  test<signed char>();
+  test<short int>();
+  test<int>();
+  test<long int>();
+  test<long long int>();
+  // unsigned
+  test<unsigned char>();
+  test<unsigned short int>();
+  test<unsigned int>();
+  test<unsigned long int>();
+  test<unsigned long long int>();
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
new file mode 100644
index 00000000000000..d2891f77c564fa
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===--------------------------------------------------...
[truncated]

@mordante mordante self-assigned this Jan 13, 2024
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

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

Thanks for working on this. I'm happy to accept the patch without 128-bit integral support and add that later.

I have not looked closely at the tests, I'd like a bit more test cases before looking at all of them.

@H-G-Hristov
Copy link
Contributor Author

Thank you for the review!

@H-G-Hristov
Copy link
Contributor Author

I did some re-imagining of the tests, there is a some redundancy but it is cleared that nothing was missed.
I also used the Clang 18's "Placeholder variables with no name" to make the test a bit cleaner, so I disabled the unsupported compilers.
The tests are formatted in tabular form manually to make them easier to read.

If the above is unacceptable, it can be fixed easily. For now it makes it easier to read and review.

@H-G-Hristov H-G-Hristov marked this pull request as ready for review January 21, 2024 12:31
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

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

I did some re-imagining of the tests, there is a some redundancy but it is cleared that nothing was missed. The tests are formatted in tabular form manually to make them easier to read.

Redundant code in tests when if improves readability is not too bad. I really like how the new testing code looks. It was a lot easier to follow. Thanks a lot!

I also used the Clang 18's "Placeholder variables with no name" to make the test a bit cleaner, so I disabled the unsupported compilers.
If the above is unacceptable, it can be fixed easily. For now it makes it easier to read and review.

IMO we should not disable tests for compilers, only for the reason to be able to use newer language features in the test. Note if the header requires language features I'm fine to disable that for a compiler. In this case we can easily allow clang-17 with a minor change as pointed out in the review.

The patch is getting close to be ready.

@H-G-Hristov H-G-Hristov marked this pull request as draft January 21, 2024 19:02
@H-G-Hristov
Copy link
Contributor Author

H-G-Hristov commented Jan 21, 2024

There is a problem with the test. Working on it.

@H-G-Hristov H-G-Hristov marked this pull request as ready for review January 21, 2024 19:32
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

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

LGTM modulo one typo.

…cast.pass.cpp

Co-authored-by: Mark de Wever <zar-rpg@xs4all.nl>
@Zingam Zingam merged commit 03c19e9 into llvm:main Jan 22, 2024
44 checks passed
@vvereschaka
Copy link
Contributor

@Zingam ,
these changes break the aarch64 cross builder with the failed test llvm-libc++-static.cfg.in::mul_sat.pass.cpp

# .---command stderr------------
# | ld.lld: error: undefined symbol: __muloti4
# | >>> referenced by mul_sat.pass.cpp
# | >>>               C:\Users\buildbot\AppData\Local\Temp\lit-tmp-vspdi658\mul_sat-857336.o:(__int128 std::__2::mul_sat[abi:ne180000]<__int128>(__int128, __int128))
# | clang++: error: linker command failed with exit code 1 (use -v to see invocation)
# `-----------------------------

@vvereschaka
Copy link
Contributor

@H-G-Hristov, @Zingam any updates?

@H-G-Hristov H-G-Hristov deleted the hgh/libcxx/P0543R3-Saturation-arithmetic branch January 23, 2024 20:50
@vvereschaka
Copy link
Contributor

Looks like I see the problem. The test configuration must be updated on the builder.
Sorry for the false alarm.

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.

None yet

8 participants